DocsRoid RunnerChart & Visualisation

Chart & Visualisation

RoidRunnerChart — a React component that wraps lightweight-charts to render a candlestick series, up to 12 indicator line overlays, optional stacked panes, and pattern arrow markers — with support for both full batch-load and incremental live-append.

Core Types

BarData
export interface BarData {
  symbol: string; // e.g. "AAPL"
  t: number;      // Unix timestamp
  o: number;      // open
  h: number;      // high
  l: number;      // low
  c: number;      // close
  v: number;      // volume
}
OutputPoint
export interface OutputPoint {
  node:  string; // Channel label, e.g. "sma" or "ichi.conv"
  t:     number; // Unix timestamp (must align with bar times)
  value: number; // Indicator value
}

Props

PropTypeDescription
theme'light' | 'dark'Drives chart background, grid, and axis colours. Changing theme destroys and recreates the chart instance.
barsBarData[]Full OHLCV history for the selected roid. Triggers a setData() bulk-load when changed.
lastBarBarData | nullMost recent bar received from a live roid_output frame. Triggers an update() incremental append.
outputsMap<string, OutputPoint[]>Full indicator output history. Triggers a full series rebuild (remove all → re-create) when changed.
pendingOutputsReact.RefObject<OutputPoint[]>Ref-backed queue of live output points to append. Drained on each outputTick change without triggering a full rebuild.
outputTicknumberIncremented by the parent each time new live output points are queued. Triggers the drain effect.
hiddenChannelsSet<string>Labels whose line series have visible: false. Effect applies options without rebuilding series.
stackedChannelsSet<string>Node names whose series are placed in a dedicated stacked IPaneApi. Triggers a full series rebuild.

Internal Refs

RefTypeRole
containerRefHTMLDivElementMount point passed to createChart(). Uses autoSize: true so no explicit width/height is needed.
chartRefIChartApiThe lightweight-charts instance. Created in the theme effect; destroyed on theme change or unmount.
seriesRefISeriesApi<'Candlestick'>The single candlestick series. All OHLCV data is set/updated via this reference.
lineSeriesRefMap<label, ISeriesApi<'Line'>>All active indicator line series, keyed by channel label. Rebuilt whenever outputs or stackedChannels changes.
markersPluginRefcreateSeriesMarkers resultThe markers plugin instance (from lightweight-charts). Created on first marker application; reused via setMarkers() on subsequent calls.
stackedPanesRefMap<nodeName, IPaneApi<Time>>Dedicated panes for stacked nodes. Populated in the first pass of the outputs rebuild effect.
markerDataRefMap<prefix, Map<timestamp, MarkerVals>>Accumulates pattern signal/confidence values indexed by node prefix and timestamp. Used to build arrow markers.

useEffect Pipeline

The component uses five isolated effects that each respond to a specific prop change, minimising unnecessary work during high-frequency streaming.

deps: [theme]Chart creation / destruction

Creates the chart with theme-appropriate colours (background, grid, crosshair, axis borders). Adds the CandlestickSeries with green/red candle colours. Returns a cleanup that calls chart.remove() and clears all refs.

deps: [bars]Full OHLCV load

Sorts bars by timestamp, maps to CandlestickData, calls seriesRef.setData(). When bars is empty, clears all markers and calls setData([]). After a full load, calls timeScale().fitContent() to show all history.

deps: [lastBar]Incremental bar append

Calls seriesRef.update() with the single latest bar. This is the hot path during live streaming — does not re-sort or re-map the full array.

deps: [outputs, stackedChannels]Full series rebuild

Marks all stacked panes for auto-removal (setPreserveEmptyPane(false)), removes all existing line series (triggering pane destruction), then recreates everything in two passes: (1) create one stacked pane per stacked node; (2) create a LineSeries for each non-pattern channel, in the appropriate pane. Pattern channels (signal/confidence/timestamp) are accumulated into markerDataRef instead. Calls applyMarkers() at the end.

deps: [hiddenChannels]Visibility toggle

Iterates lineSeriesRef and calls applyOptions({ visible }) for each series. Also calls applyMarkers(buildMarkers()) in case a signal channel was toggled. Intentionally excluded from the outputs effect to avoid full rebuilds on every eye-click.

deps: [outputTick]Live output drain

Drains pendingOutputs.current. For pattern channels, accumulates into markerDataRef and calls applyMarkers(). For regular channels, calls lineSeriesRef.get(label)?.update(). Clears the pending array after draining.

Stacked Panes

When a node name is in stackedChannels, all its channel series are placed in a separate price scale pane created via chart.addPane(). Each pane is stored in stackedPanesRef keyed by node name.

Creation

In the first pass of the outputs rebuild effect, one addPane() call is made per stacked node (skipping pattern channels). setPreserveEmptyPane(true) is called immediately to keep the pane alive while series are being added.

Destruction

Before rebuilding, each stacked pane has setPreserveEmptyPane(false) called. When all series are removed from the pane (via chart.removeSeries()), the library automatically destroys the pane.

Pattern Arrow Markers

Candlestick pattern nodes emit three channels: signal, confidence, and timestamp. These are not rendered as line series — instead, buildMarkers() converts them into SeriesMarker objects applied to the candlestick series via the createSeriesMarkers plugin.

signal valuepositionshapecolourtext
1 (bullish)belowBararrowUp#ffcc00{confidence}%
-1 (bearish)aboveBararrowDown#ff4444{confidence}%

Markers are sorted by timestamp before being applied. If the signal channel label is in hiddenChannels, no markers are emitted for that node prefix.

Chart Configuration

Candlestick colours

Up candle: #22c55e (border + wick)
Down candle: #ef4444 (border + wick)

Chart options

autoSize: true — fills the container div

crosshair.mode: 0 — normal crosshair mode

timeScale.timeVisible: true

timeScale.secondsVisible: false