Skip to Content
TutorialsImplement DatafeedStreaming implementation

Streaming implementation

Add live updates with either subscribeTicks / unsubscribeTicks or subscribeBars / unsubscribeBars (not both).

  • Ticks path: push trade ticks; the SDK aggregates them into the active candle.
  • Bars path: push complete OHLCV bars for the chart resolution; the SDK applies them directly (no tick aggregation).

If both are implemented on the datafeed, the SDK prefers ticks and ignores bars.

Labs:

How it fits together (ticks)

  1. History loads via getBars (previous step).
  2. After the symbol is ready, the chart calls subscribeTicks(...).
  3. For each trade, call onRealtimeCallback(tradeMessage).
  4. On symbol change / destroy, the chart calls unsubscribeTicks(subscriberUID).
Your WebSocket / bus → subscribeTicks → onRealtimeCallback(tick) SDK aggregates into live bars

How it fits together (bars)

  1. History loads via getBars.
  2. The chart calls subscribeBars(...) with the active chart resolution.
  3. For each bar update, call onRealtimeCallback({ time, open, high, low, close, volume }).
  4. On symbol change / destroy, the chart calls unsubscribeBars(subscriberUID).
Your WebSocket / bus → subscribeBars → onRealtimeCallback(bar) SDK applies host OHLC (no tick aggregation)

Trade message shape

{ type: "trade", productId: "BYBIT:FUTURE:BTCUSDT", symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", timeStamp: new Date(), // Date object tradeID: "abc123", price: 45123.45, quantity: 0.123, amount: 5555.67, // price * quantity side: "BUY", // or "SELL" (uppercase) }

Implement subscribeTicks / unsubscribeTicks

const datafeed = { subscriptions: new Map(), // subscriberUID → cleanup // … resolveSymbol + getBars from the previous page … subscribeTicks( symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback, ) { const productId = symbolInfo.full_name || `${symbolInfo.exchange}:${symbolInfo.segment}:${symbolInfo.symbol}`; const ws = new WebSocket("wss://stream.example.com/trades"); ws.onopen = () => { ws.send( JSON.stringify({ op: "subscribe", symbol: productId, }), ); }; ws.onmessage = (event) => { const msg = JSON.parse(event.data); const trades = Array.isArray(msg.data) ? msg.data : [msg]; for (const t of trades) { onRealtimeCallback({ type: "trade", productId, symbol: symbolInfo.symbol || symbolInfo.ticker, exchange: symbolInfo.exchange, segment: symbolInfo.segment, timeStamp: new Date(t.time ?? t.T ?? Date.now()), tradeID: String(t.id ?? t.i ?? `${t.time}-${t.price}`), price: Number(t.price ?? t.p), quantity: Number(t.size ?? t.v ?? 0), amount: Number(t.price ?? t.p) * Number(t.size ?? t.v ?? 0), side: String(t.side ?? t.S ?? "BUY").toUpperCase(), }); } }; this.subscriptions.set(subscriberUID, () => { try { ws.close(); } catch (_) {} }); }, unsubscribeTicks(subscriberUID) { const cleanup = this.subscriptions.get(subscriberUID); if (cleanup) { cleanup(); this.subscriptions.delete(subscriberUID); } }, };

Checklist

  • subscribeTicks stores cleanup keyed by subscriberUID or you use subscribeBars instead (not both)
  • Each trade calls onRealtimeCallback with type: "trade" and numeric price (ticks path)
  • unsubscribeTicks / unsubscribeBars closes the socket / cancels the listener
  • You implement only one realtime path: ticks or bars
Last updated on