Realtime over WebSocket
Stream live bar updates from a WebSocket into ChartForge — reconnection, multi-symbol fan-out, message shape.
The most common production setup — REST for history, WebSocket for
live ticks. ChartForge calls your subscribe(req, onBar) once per
(symbol, resolution) pair; you fan that out to a single shared
WebSocket.
Quick example
import type { Bar, Datafeed, GetBarsRequest, SubscribeRequest } from '@elitechart/elitechart';
import { asPrice, asTimestampMs, asVolume } from '@elitechart/elitechart';
class WsDatafeed implements Datafeed {
private ws: WebSocket | null = null;
private subs = new Map<string, (bar: Bar) => void>();
private ensureWs(): WebSocket {
if (this.ws !== null && this.ws.readyState === WebSocket.OPEN) return this.ws;
const ws = new WebSocket('wss://example.com/stream');
ws.onmessage = (e) => {
const m = JSON.parse(e.data) as { symbol: string; tf: string; t: number; o: number; h: number; l: number; c: number; v: number };
const cb = this.subs.get(`${m.symbol}|${m.tf}`);
if (cb !== undefined) {
cb({
time: asTimestampMs(m.t), open: asPrice(m.o), high: asPrice(m.h),
low: asPrice(m.l), close: asPrice(m.c), volume: asVolume(m.v),
});
}
};
ws.onclose = () => { this.ws = null; setTimeout(() => this.ensureWs(), 1000); };
this.ws = ws;
return ws;
}
async getBars(req: GetBarsRequest): Promise<ReadonlyArray<Bar>> {
const r = await fetch(`/api/bars?symbol=${req.symbol}&tf=${req.resolution}&from=${req.from}&to=${req.to}`);
return (await r.json()) as ReadonlyArray<Bar>;
}
subscribe(req: SubscribeRequest, onBar: (bar: Bar) => void): () => void {
const key = `${req.symbol}|${req.resolution}`;
this.subs.set(key, onBar);
const ws = this.ensureWs();
const send = () => ws.send(JSON.stringify({ op: 'sub', symbol: req.symbol, tf: req.resolution }));
if (ws.readyState === WebSocket.OPEN) send();
else ws.addEventListener('open', send, { once: true });
return () => {
this.subs.delete(key);
this.ws?.send(JSON.stringify({ op: 'unsub', symbol: req.symbol, tf: req.resolution }));
};
}
}
export const datafeed = new WsDatafeed();
How it works
A single WebSocket fans out messages to per-(symbol, tf) callbacks
via the subs map. On disconnect, the class auto-reconnects and
re-sends every active subscription on open.
Bars from your server should be the current bar — not just
ticks. The chart merges the incoming bar with the buffer by
time. So if the same bar's close keeps moving, you're flooding
the chart with (t, o, h, l, c, v) tuples that all share t; the
chart updates the in-progress bar.
Variations
Polling fallback if WebSocket is unavailable
See the polling pattern in datafeed-contract.
Backoff on repeated reconnects
let attempt = 0;
ws.onclose = () => {
attempt += 1;
setTimeout(() => this.ensureWs(), Math.min(1000 * 2 ** attempt, 30_000));
};
API