Charting
AXIS chart: ChartHost, PaneManager, series factory, drawings, crosshair sync, and trade markers.
This page
Charting
Charting is the spatial surface for OHLCV, overlays, and annotations. Implementation is imperative under a Solid shell so lightweight-charts owns its DOM subtree.
Abstract
| Module | Role |
|---|---|
ChartHost.tsx / ChartWorkspace.tsx | Solid mount; multi-slot grid; empty-state |
pane-manager.ts | Multi-pane LWC charts, time sync, overlay apply |
pane-badge.ts | Corner name + script action icons |
series-factory.ts | Candle/volume/line series helpers + palette |
chart-type.ts | Price style transforms (HA, hollow, …) |
bar-replay.ts + BarReplayControls | History scrub / play |
layout.ts / layout-recipes.ts / chart-registry.ts | Multi-chart layouts + presets |
compare-overlay.ts | Second-symbol compare series |
volume-profile.ts | OHLCV volume-at-price overlay |
drawing-layer.ts + drawings/* | User + script drawings, templates, sync helpers |
price-precision.ts | Price-scale decimals (auto or 0–8) from symbol + bars |
results/plot-visuals.ts | Pine plot.style_* → LWC series kind (line, stepline, columns, area, …) |
crosshair-sync.ts | Cross-pane crosshair |
pyne-drawings.ts | Engine drawing objects → layer |
manager-access.ts | Process-wide manager/layer getters |
Conceptual model
Rendering…
Interface surface
Panes
store.panes: { id, type, height, order, visible, label }.
| type | Typical series |
|---|---|
price | Price series (style below) + overlay lines + markers + drawings |
volume | Histogram |
indicator | Non-overlay plots |
equity | Strategy equity curve |
Defaults: price + volume. Indicator/equity created on demand.
Price chart styles (store.chartType)
Topbar Type select (persisted). Implementation: chart/chart-type.ts + createPriceSeries.
chartType | Series | Notes |
|---|---|---|
candles | Candlestick | Japanese candles (default) |
hollow | Candlestick | Hollow body on up bars |
bars | Bar | Classic OHLC bars |
heikinashi | Candlestick | Heikin-Ashi transform of OHLCV |
line | Line | Close only |
area | Area | Close with fill |
baseline | Baseline | Close vs first-bar base |
Series still live under pane key candle so markers, hlines, and the drawing layer keep a stable host. Changing type swaps the LWC series and rebinds data without a full history reload.
Empty / status overlay
When bars.length === 0, ChartHost shows contextual title/sub from store.status (loading, error, running, or “Load data to begin”).
Price-scale decimals
Price pane labels use chart/price-precision.ts:
| Mode | Behavior |
|---|---|
auto (default) | Merge symbol heuristics (e.g. BTC→2, SHIB→8) with recent OHLCV significant digits |
0–8 | Fixed display decimals + matching minMove |
UI control cycles A → 0 → … → 8 → A on the price-pane scale chrome. Store field: priceScaleDecimals (persisted).
Plot style parity
Engine plot series honor Pine Script™ plot.style_* via mapPlotStyleToSeriesKind (results/plot-visuals.ts → pane-manager series factory):
| Pine style (examples) | Chart series |
|---|---|
plot.style_line / default | Line |
plot.style_stepline | Stepline (LWC WithSteps) |
plot.style_stepline_diamond | Stepline + vertex point markers |
plot.style_histogram | Histogram (base 0) |
plot.style_columns | Columns → LWC Histogram (base 0; no column-width/gap API) |
plot.style_area | Area |
plot.style_circles | Circular point markers + faint hairline |
plot.style_cross | Larger point markers, connector hidden |
plot.style_linebr / steplinebr / areabr | Broken line / stepline / area — na ends a segment (LWC would otherwise connect through whitespace) |
LWC limits: Histogram series has no bar-width or gap option, so columns and histogram share the same bar geometry (both zero-based). Point markers are always circular — cross is approximated by discrete markers without a connector; stepline_diamond uses stepline + vertex markers. Sparse plotshape styles map diamond/cross to square markers (optional + / ✕ glyphs when text is omitted).
fill(plot1, plot2, color=…) renders as SVG bands between the two series when both resolve on the same pane.
Multi-chart layouts
- Modes: 1 / 2H / 2V / 4 (
store.chartLayout) - Per-slot symbol/interval via registry; active slot drives topbar + Run
- Named layouts save/load (optional chrome snapshot)
- Recipes in Layouts menu: Scalp 1m+5m, Swing HTF, Quad, BTC majors (
layout-recipes.ts)
Bar Replay
Topbar Replay → session over loaded OHLCV (bar-replay.ts):
- Enters with full history visible (cursor on last bar)
- Scrub / step / play; Play at end restarts from bar 0
- Mutually exclusive with Live
Compare & volume profile
- Compare control: second symbol as % or absolute overlay (
compare-overlay.ts) - Volume profile toggle in Layers → OHLCV approximation histogram (
volume-profile.ts)
Pane badges
Each pane’s top-left chip (pane-badge.ts): label + script actions when scripts are applied:
| Icon | Action |
|---|---|
| Settings | Script inputs modal |
| Eye | Show / hide series |
| Refresh | Re-run script |
| Trash | Remove from chart |
Volume/equity without scripts: name + hide.
Drawings toolbar
See Drawings. Tools include cursor, hline, vline, trend, ray, extend, rect, ellipse, arrow, fib, measure, text (plus fib/shape/annotation/trading packs). The style bar edits color/width/dash plus per-tool extras (extend, fib levels, RR, text); last-used style is stored per kind. Templates and duplicate helpers live under Layers / drawings modules.
Crosshair & time
syncTimeScales / syncCrosshair keep multi-pane navigation coherent. scrollToTime / jumpToDebugPin used from Results and debug pins.
Internals
Solid vs imperative boundary
// ChartHost — Solid owns outer shell
// PaneManager only mutates panesEl — never put Solid children inside panesEl
On cleanup: destroy drawing layer, dispose manager, clear accessors.
Data path
setDataToChart(bars) applies OHLCV; createEffect on store.bars re-applies if bars change outside load helper.
Run apply (chart side)
From runAndApply (indicators):
syncOverlayLines— update existing overlay series in place (avoids destroy→blank→add flash on live re-runs)- Markers from normalized events (set in place)
- Script drawings atomic replace via DrawingLayer (
setScriptDrawingswithout empty frame);linefillquads andforce_overlaygeometry route with other script drawings;barcolortints candles via plot-visuals (not the SVG layer) - Equity series when strategy report warrants (silent live runs skip hide thrash)
History vs live bars
- Full loads:
loadBars→chartDataGen++→setDataToChart+ fitContent - Live:
multiplex→appendBar+manager.appendBaronly (no fitContent, no full setData) - Performance (2.0.x): live tick path is O(1) append + conflation under load; heavy history paint is batched; load requests use AbortController so symbol switches cancel in-flight venue fetches
- Overlay re-runs prefer in-place series updates (
syncOverlayLines) to avoid destroy→blank flash
series-factory
Centralizes series construction options, plot-style mapping, and color palette so Results plot summary and chart stay chromatically related.
Invariants
- Solid reconciliation must not rewrite pane roots.
- Script drawings ephemeral per run; user drawings store-backed.
- Overlay routing: engine
meta.overlay+ script type; oscillator-scale series forced to sub-pane when they would vanish on price (see Indicators). - Shared non-overlay sub-pane id is stable:
indicator. - Manager may be null before mount—runner no-ops chart apply safely.
Failure modes
| Symptom | Cause |
|---|---|
| Blank panes after hot reload | Manager disposed; full remount |
| Overlays stack forever | Old path skipped removeOverlays |
| Fib/tools ignore clicks | No bars / time scale; wrong tool |
| Markers at t=0 | Event times not bar-aligned |
Worked example (dev)
import { getManager } from './chart/manager-access';
getManager()?.scrollToTime(barTime);