Reference
Methods you implement
The datafeed contract, method by method. Two are required and the rest are optional. A chart with only resolveSymbol and getBars works completely; it simply does not stream or search.
The object
The same shape you would hand any charting library, and deliberately the conventional method names, so anyone who has written one before is already right. Nothing about it knows it is behind an iframe.
vpEmbed.datafeed('mine', {
resolveSymbol: (name) => ({ name, pricescale: 100, session: '24x7' }),
getBars: async (symbol, resolution, period) => fetchMyBars(symbol, resolution, period),
});datafeedCall message. See Wire messages.Every method
| Method | Arguments | Returns | Notes | |
|---|---|---|---|---|
onReady | optional | – | { supported_resolutions } | Your feed's configuration. Omit it and the chart uses its own defaults, which are usually fine, so this is the first method to skip. |
searchSymbols | optional | query, exchange, type | SearchItem[] | Rows for the chart's symbol search. Omit it and the search stays empty rather than erroring, a reasonable choice when your page decides the instrument. |
resolveSymbol | required | symbol | SymbolInfo | Describe one instrument. name is the only required field; everything else has a default. Throw to report an unknown symbol. |
getBars | required | symbol, resolution, period | Bar[] | History for a window. ASCENDING by time. Return [] when you have no more, that is what stops the chart walking backwards forever. |
subscribeBars | optional | symbol, resolution, subscriberUID, push | nothing | Start streaming. Call push(bar) for each update. Skip it and the chart draws history and stops, correct for an end-of-day feed. |
unsubscribeBars | optional | subscriberUID | nothing | Stop that stream and release whatever it holds. |
getMarks | optional | symbol, from, to, resolution | Mark[] | Round badges under individual bars, for fills and events tied to one bar. time must be a bar's own opening time in seconds, because a mark is looked up by its bar rather than positioned by pixel. A pushed setMarks set takes precedence over this. |
getServerTime | optional | – | seconds | Your clock, for the countdown on the forming bar. Without it the chart uses the reader's own, which drifts by whatever their machine is out by. |
getTimescaleMarks | optional | symbol, from, to, resolution | TimescaleMark[] | Roundels in the time gutter, for events tied to a moment rather than a bar. Your set is CONCATENATED with ours rather than replacing it, unlike getMarks: two sources describing one bar would contradict each other, while two describing one moment are compatible. Asked for only when your onReady config sets supports_timescale_marks: true. |
searchSymbolsPaginated | optional | query, exchange, type, page | { items, hasMore } | A catalogue the reader can scroll past the first page of. hasMore is what separates a short page from the end of the list; returning a bare array is read as one page with nothing further. |
getFootprint | optional | symbol, resolution, period | FootprintSlice[] | Order flow for a window of bars, asked for only while the footprint chart type is showing and once per history page. period.from / to are SECONDS; each slice's time is the bar's opening time in MILLISECONDS and must match a bar you served. A feed without it may carry levels on the bars themselves instead. |
subscribeTrades | optional | symbol, resolution, subscriberUID, push | nothing | The subscribeBars twin for live order flow. Call push(trade) for each print, { price, size, side, timestamp } with side the aggressor; each is folded into the forming bar's levels. Opened only while the footprint chart is showing and closed when it is left. |
unsubscribeTrades | optional | subscriberUID | nothing | Stop that order-flow stream. |
The order they are called in
Once per chart, in this sequence. Nothing is called twice for the same reason.
onReady ── your configuration
resolveSymbol ── describe the instrument
getBars ── the first window ← firstDataRequest: true
subscribeBars ── start streaming
… the reader scrolls back …
getBars ── an older window ← firstDataRequest: false
… the reader leaves …
unsubscribeBarsperiod.firstDataRequest tells the two getBars calls apart. The first wants the most recent countBack bars ending at to; later ones are the chart walking backwards for history. Returning [] on those is how you say the instrument starts there.The period argument
| Field | Type | Meaning |
|---|---|---|
| from | number | Window start, in SECONDS. |
| to | number | Window end, in SECONDS. |
| countBack | number | How many bars the chart wants ending at to. Prefer this over from. |
| firstDataRequest | boolean | True for the initial window, false for every scroll back. |
to and countBack, not from. A window narrower than the chart asked for returns too few bars whenever the range holds less history than countBack, and the chart, given a short page, concludes the instrument ends there.Order flow, for the footprint chart
Every other chart type reads five numbers a bar. footprint reads volume per PRICE LEVEL, split by aggressor side, and there are three ways to supply it: levels on the bars getBars returns, a getFootprint answer for a window, or live prints through subscribeTrades. They compose: history from the first two, the forming bar from the third.
| Field | Type | Meaning |
|---|---|---|
| levels | FootprintLevel[] | { price, bid, ask } per level, ascending by price, volumes never negative. ABSENT and EMPTY are different facts: absent means you did not supply order flow for this bar, and the chart draws an empty column rather than inventing one from the candle; [] means you did and nothing traded there. |
| levelStep | number | The price step the levels sit on. Positive. Absent, the chart uses the symbol's tick, then the spacing of the levels themselves. |
| footprintSource | string | exchange_trades, sub_candles or synthetic, weakest last. Anything below exchange_trades is drawn with a quality ring so the reader knows it is derived. Absent: exchange_trades when levels is present. |
getFootprint answers { time, levels, levelStep?, footprintSource? } per bar, with levels required so a slice cannot be mistaken for a bar you have no flow for. A subscribeTrades print lands in the bar its timestamp falls in and is bucketed onto that bar's levelStep. Nothing here is synthesised: a footprint chart on a feed that supplies no order flow draws bars with no cells, which is the honest picture.What is checked, and what is not
Every reply is validated for shape, a bar must be five finite numbers and pages must ascend, because malformed data reaches a canvas renderer and a chart that throws mid-paint is worse than one that draws nothing.
It is not treated as hostile. You are feeding your own reader your own data, so the threat model here is a mistake rather than an attack: every refusal names the field, and for bars the index.