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

ModuleRole
ChartHost.tsx / ChartWorkspace.tsxSolid mount; multi-slot grid; empty-state
pane-manager.tsMulti-pane LWC charts, time sync, overlay apply
pane-badge.tsCorner name + script action icons
series-factory.tsCandle/volume/line series helpers + palette
chart-type.tsPrice style transforms (HA, hollow, …)
bar-replay.ts + BarReplayControlsHistory scrub / play
layout.ts / layout-recipes.ts / chart-registry.tsMulti-chart layouts + presets
compare-overlay.tsSecond-symbol compare series
volume-profile.tsOHLCV volume-at-price overlay
drawing-layer.ts + drawings/*User + script drawings, templates, sync helpers
price-precision.tsPrice-scale decimals (auto or 0–8) from symbol + bars
results/plot-visuals.tsPine plot.style_* → LWC series kind (line, stepline, columns, area, …)
crosshair-sync.tsCross-pane crosshair
pyne-drawings.tsEngine drawing objects → layer
manager-access.tsProcess-wide manager/layer getters

Conceptual model

Diagram

Rendering…

Interface surface

Price, volume, and RSI panes with drawing toolbar

Panes

store.panes: { id, type, height, order, visible, label }.

typeTypical series
pricePrice series (style below) + overlay lines + markers + drawings
volumeHistogram
indicatorNon-overlay plots
equityStrategy 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.

chartTypeSeriesNotes
candlesCandlestickJapanese candles (default)
hollowCandlestickHollow body on up bars
barsBarClassic OHLC bars
heikinashiCandlestickHeikin-Ashi transform of OHLCV
lineLineClose only
areaAreaClose with fill
baselineBaselineClose 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:

ModeBehavior
auto (default)Merge symbol heuristics (e.g. BTC→2, SHIB→8) with recent OHLCV significant digits
08Fixed 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 / defaultLine
plot.style_steplineStepline (LWC WithSteps)
plot.style_stepline_diamondStepline + vertex point markers
plot.style_histogramHistogram (base 0)
plot.style_columnsColumns → LWC Histogram (base 0; no column-width/gap API)
plot.style_areaArea
plot.style_circlesCircular point markers + faint hairline
plot.style_crossLarger point markers, connector hidden
plot.style_linebr / steplinebr / areabrBroken 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

2×2 multi-chart grid with BTCUSDT loaded in the active slot
  • 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

Bar replay strip on BTCUSDT: scrub, play, and speed Scrubbing and playing bar replay over loaded history

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

ETHUSDT percent overlay compared against BTCUSDT Layers panel: panes, volume profile overlay, scripts, drawings
  • 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:

IconAction
SettingsScript inputs modal
EyeShow / hide series
RefreshRe-run script
TrashRemove 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 (setScriptDrawings without empty frame); linefill quads and force_overlay geometry route with other script drawings; barcolor tints 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: loadBarschartDataGen++setDataToChart + fitContent
  • Live: multiplexappendBar + manager.appendBar only (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

  1. Solid reconciliation must not rewrite pane roots.
  2. Script drawings ephemeral per run; user drawings store-backed.
  3. Overlay routing: engine meta.overlay + script type; oscillator-scale series forced to sub-pane when they would vanish on price (see Indicators).
  4. Shared non-overlay sub-pane id is stable: indicator.
  5. Manager may be null before mount—runner no-ops chart apply safely.

Failure modes

SymptomCause
Blank panes after hot reloadManager disposed; full remount
Overlays stack foreverOld path skipped removeOverlays
Fib/tools ignore clicksNo bars / time scale; wrong tool
Markers at t=0Event times not bar-aligned

Worked example (dev)

import { getManager } from './chart/manager-access';
getManager()?.scrollToTime(barTime);

See also