Streams and history
Subscribe to bars, order flow, trades, the order book and your own data, with history.
subscribe returns named streams. The handler of the same name in on gets each event of that stream, and ctx.streams.<name> holds what it received so far.
subscribe: (ctx) => ({
symbol: ctx.market.symbol(),
h4: ctx.market.bars({ timeframe: "4h", warmup: 50 }),
m1: ctx.market.bars({ timeframe: "1m" }),
big: ctx.market.trades({ minSize: 50, history: "1d" }),
}),
on: {
h4: (s, { bar, confirmed }) => { /* 4h bars */ },
m1: (s, { bar }) => { /* 1m bars */ },
big: (s, trade) => { /* trades of 50 and more */ },
},Each stream has its own history, so one script can read confirmed 4h bars, 1m bars and every trade at once. Symbol and timeframe default to the chart's.
Streams
| Builder | Handler gets | ctx.streams.<name> | Notes |
|---|---|---|---|
ctx.market.bars({ symbol, timeframe, warmup }) | BarEvent: { bar, confirmed } | BarsView: typed arrays time, open, …, length | |
ctx.market.orderFlow({ symbol, timeframe, warmup }) | OrderFlowEvent: bid and ask volume per price of a bar | OrderFlowView: bars | "1m" or coarser |
ctx.market.trades({ symbol, minSize, history }) | Trade: time, price, size, aggressor | TradesView: typed arrays | minSize filters at the source |
ctx.market.book({ symbol, history }) | BookSnapshot: bids and asks | BookView: latest, simulated | Simulated for now, simulated is true |
ctx.market.symbol({ symbol }) | SymbolInfo, once | SymbolView: info | tick size, point value, sessionOf(time) |
ctx.data.poll({ url, every, schema }) | DataEvent: Value or Error | DataView: latest | Your API, through our proxy |
ctx.data.websocket({ url, onOpenSend, schema }) | DataEvent | DataView |
Full types: Streams reference.
History
barsandorderFlowload the chart's time range: from the chart's first loaded bar to now.warmuploads more before that: a bar count (warmup: 100), a duration (warmup: "2d") or"session", back to the open of the session the range starts in. Use it for indicators that need past bars, e.g. a 200 bar average.- When the chart loads older bars (scrolling left), each stream loads the same older range plus its warmup and sends it as backfill: a bar can arrive that is older than bars the handler already has. Insert by time, or skip older bars on purpose.
tradesandbookloadhistory, a duration back from now. Without it they start live.- Missing history stays missing.
The forming bar
A bar that has not closed arrives again on every update with confirmed: false, then once with confirmed: true. Replace the last entry when the time repeats, and commit sums only on confirmed:
if (s.points.at(-1)?.time === bar.time) s.points[s.points.length - 1] = point;
else s.points.push(point);
if (confirmed) s.sums = sums;Order flow works the same way, and a bar can arrive again with more levels: the last event of a time holds all its volume.
Status
Market views have a status:
Loading: nothing loaded yet.Readywithfrom: data is complete fromfromto now.LoadingOlderwithfrom: complete fromfrom, older history still loading.Unavailablewitherror: the stream failed, e.g.NoAccesswhen the data feed has no order flow. Nothing more arrives.
Check status before trusting a range, e.g. show "Waiting for order flow" for a range that starts before from. A failed stream also shows in the indicator list. The other streams keep running.
Your own data (poll, websocket) has no status: errors come to the handler as { type: "Error", error }, so the script decides what to do.
Pull access
Handlers get events one by one. draw, legend and event handlers can look back without copying:
const { time, close, length } = ctx.streams.bars;
for (let i = 0; i < length; i += 1) {
// close[i] is the close of bar i. Arrays can be longer than `length`: stop at `length`.
}