Docs·Charting Library·Core API

Data Loading

Hand OHLCV candles to the engine — full reload, append on tick, update the last bar, prepend on infinite scroll. The full kline data API in one page.

The engine takes OHLCV as six parallel arrays. This shape lets the renderer fan out a 100 000-bar load in a few milliseconds without per-bar object overhead. Every kline method below uses the same shape.

Time units are seconds. Always. Convert milliseconds before passing in.

Initial load

chart.setKlines(timestamps, open, high, low, close, volume)

Replaces the entire kline dataset. Resets the viewport to show the last ~120 candles (typical "chart-just-opened" look).

ArgumentTypeLengthNotes
timestampsFloat64Array | number[]NUnix seconds, strictly increasing
openFloat64Array | number[]N
highFloat64Array | number[]N
lowFloat64Array | number[]N
closeFloat64Array | number[]N
volumeFloat64Array | number[]NBase-asset volume
JavaScript
chart.setKlines(timestamps, opens, highs, lows, closes, volumes)

Prefer typed arrays. Passing Float64Array skips a copy step — the engine reads the buffer directly. Regular number[] works but copies once on entry.

chart.setKlinesPreserveViewport(timestamps, open, high, low, close, volume)

Same as setKlines, but keeps the current viewport (pan offset + zoom). Use when reloading the same symbol+timeframe (e.g. on a websocket reconnect) — preserving the viewport avoids "the chart jumped" feeling.

Real-time updates

The two most-called methods in any live chart. They run in the microsecond range — call them once per tick.

chart.updateLastKline(ts, o, h, l, c, v)

Updates the last candle in place. Use on every tick while the current bar is still forming.

JavaScript
ws.on('trade', (t) => {
  const lastTs = currentBarStart  // Unix seconds, start of current bar
  const lastOpen = currentBarOpen
  const newClose = t.price
  const newHigh = Math.max(currentBarHigh, t.price)
  const newLow  = Math.min(currentBarLow, t.price)
  const newVolume = currentBarVolume + t.qty

  chart.updateLastKline(lastTs, lastOpen, newHigh, newLow, newClose, newVolume)
})

chart.appendKline(ts, o, h, l, c, v)

Appends a new candle. Call when a bar closes and a new one starts.

JavaScript
function onBarClose(closedBar, newBar) {
  chart.appendKline(
    newBar.ts, newBar.open, newBar.open, newBar.open, newBar.open, 0,
  )
}

After appendKline, the new bar becomes the "last" — subsequent updateLastKline calls update it.

Standard real-time pattern

JavaScript
function handleTrade(trade) {
  if (trade.ts >= currentBarEnd) {
    // Bar boundary crossed — finalise current, open new.
    chart.updateLastKline(/* final OHLCV of the closing bar */)
    chart.appendKline(/* opening tick of the new bar */)
    currentBarEnd += intervalSeconds
  } else {
    chart.updateLastKline(/* updated OHLCV of the current bar */)
  }
}

The engine handles the "candle is forming" visual cue (dimmer body, no border) for the last bar automatically.

Infinite scroll (load older bars)

When the user pans left past the oldest loaded bar, you'll want to fetch and prepend more history.

chart.prependKlines(timestamps, open, high, low, close, volume)

Prepends a batch of older candles. Adjusts the internal index so the viewport doesn't jump.

JavaScript
chart.onPanEnd(async () => {
  const visible = chart.getKlineVisible()
  // Trigger a fetch when the user pans within N bars of the oldest one.
  if (/* user is near the left edge */) {
    const older = await fetch(`/api/klines?to=${oldestTs}&limit=1000`)
    const arrs = toFloat64Arrays(older)
    chart.prependKlines(arrs.t, arrs.o, arrs.h, arrs.l, arrs.c, arrs.v)
  }
})

A complete infinite-scroll recipe (with rate-limiting, dedup, and viewport-preservation tuning) lives in the Infinite Scroll guide.

Removing the last bar

chart.popLastKline()

Removes the last candle. Returns true on success, false if the dataset was empty.

Useful for replay flows where the user scrubs backwards, or for cleanly cancelling a partial bar before reloading from the exchange API.

Reading what the engine has

chart.getLastClose()

Returns the close price of the last candle, or null if no data is loaded. Cheaper than reading from your own array because the engine already has the value in memory.

chart.getKlineVisible()

Returns whether the candle layer is currently shown. Toggle with setKlineVisible(true | false) — useful for indicator-only views (e.g. an RSI-only research pane).

Common pitfalls

Timestamps are in milliseconds, not seconds. Every public exchange API returns milliseconds. Divide by 1000 once at parse time. Mixing units leads to candles spaced 1000× wider than expected.

Timestamps not strictly increasing. Duplicate or out-of-order timestamps cause undefined rendering. If your data source can emit duplicates (some exchange websockets do), dedupe in your handler before calling appendKline.

Floats with too many decimals on shitcoin prices. The engine handles arbitrary precision — but your fetch parser shouldn't. parseFloat("0.00000000023") works; coercing through int will not.

Calling setKlines on every tick.setKlines is the full reload method. It resets the viewport. For tick updates, use updateLastKline / appendKline.

Forgetting to call start() after the first load. The engine has data but is not painting. Symptom: chart looks blank or stuck on its mount frame. Always call start() after the first setKlines.

Next

  • Orderbook Heatmap — hand depth snapshots and append new columns on every snapshot tick.
  • Footprint Chart — switch chart type and feed trade-level data for bid×ask cells.
  • Indicators — turn RSI, MACD, VRVP, and 15+ others on / off.
  • Performance & Smoothness — the do-this / don't-do-this list for keeping a tick-heavy chart at 60 fps.
  • Common Pitfalls — symptom → one-line fix catalogue for the bugs we see most often.