Realtime & replay

Stream live data, aggregate ticks into bars, and replay historical data at controlled speed.

Built-in adapters

All free, no API key — connect any of them the same way:

import { BinanceAdapter, CoinbaseAdapter, BybitAdapter, KrakenAdapter } from '@tradecanvas/chart'

chart.connect({ adapter: new BinanceAdapter(),  symbol: 'BTCUSDT', timeframe: '1m' })
chart.connect({ adapter: new BybitAdapter(),    symbol: 'BTCUSDT', timeframe: '1m' })
chart.connect({ adapter: new KrakenAdapter(),   symbol: 'BTC/USD', timeframe: '5m' })
chart.connect({ adapter: new CoinbaseAdapter(), symbol: 'BTC-USD', timeframe: '15m' })

DataAdapter interface

interface DataAdapter {
  name: string
  connect(config): void
  disconnect(): void
  getConnectionState(): ConnectionState
  fetchHistory(symbol, timeframe, limit?): Promise<OHLCBar[]>
  on(event, listener): void   // 'bar' | 'tick' | 'snapshot' | 'connectionChange' | 'error'
  off(event, listener): void
  dispose(): void
}

Any feed in ~20 lines

Extend WebSocketAdapter (live + REST history) or PollingAdapter (REST-only feeds). The base handles the connection lifecycle, reconnect, decoding, and event emission — you supply a URL and a parse function.

import { WebSocketAdapter } from '@tradecanvas/chart'

const myAdapter = new WebSocketAdapter({
  name: 'myexchange',
  wsUrl: (c) => 'wss://api.myexchange.com/ws/' + c.symbol + '@kline_' + c.timeframe,
  fetchHistory: (symbol, tf, limit) => fetch('/candles?...').then((r) => r.json()),
  parseMessage: (raw) => ({ bar: toBar(raw), closed: raw.k.x }),
})

Tick aggregation

Roll raw ticks into OHLC bars per timeframe:

import { TickAggregator } from '@tradecanvas/chart'

const agg = new TickAggregator('1m')
agg.processTick({ time, price, volume })

const current = agg.getCurrentBar()         // forming bar
const closed = agg.flushClosedBars()        // bars that have rolled over

Reconnect

ReconnectManager handles exponential backoff with a cap and a final give-up.

import { ReconnectManager } from '@tradecanvas/chart'

const rm = new ReconnectManager({ maxRetries: 6, baseDelay: 500, maxDelay: 30_000 })

rm.start()
rm.schedule(() => connectWebsocket())   // call on disconnect; backs off automatically
rm.onConnected()                        // call on a successful connect to reset

Replay mode

Drive a historical DataSeries forward at controlled speed. Decoupled from Chart so it can power both UI replay and headless backtests.

import { ReplayController } from '@tradecanvas/chart'

const replay = new ReplayController({
  data: historicalBars,
  speed: 10,      // bars per second
  startIndex: 0,
})

// Seed the chart with everything before startIndex
chart.setData(replay.getPrefix())

// Wire each emitted bar into the chart
replay.on('bar', ({ bar }) => chart.appendBar(bar))
replay.on('finished', () => console.log('done'))

replay.start()
// replay.pause(); replay.resume(); replay.step(5); replay.seek(200); replay.setSpeed(20)

In the widget's replay mode, click any revealed bar to jump the replay cursor there. More generally, the chart now emits click and barClick events for plain left-clicks (press and release without dragging):

chart.on('barClick', (e) => {
  const { bar, barIndex, point } = e.payload
})

API

MethodPurpose
start()Begin emitting from current index.
pause() / resume()Pause/continue timer.
step(n)Emit N bars synchronously without the timer.
seek(index)Jump without emitting in-between bars.
setSpeed(bps)Bars per second (clamped to >= 0.01).
destroy()Clear timer + listeners.

See Analytics for using replay alongside the backtester.