Skip to content

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.

A complete minimal feed
vpEmbed.datafeed('mine', {
  resolveSymbol: (name) => ({ name, pricescale: 100, session: '24x7' }),
  getBars: async (symbol, resolution, period) => fetchMyBars(symbol, resolution, period),
});
Not using our loader? The same object, reached through a bridge you write, the wire turns each of these into a datafeedCall message. See Wire messages.

Every method

MethodArgumentsReturnsNotes
onReadyoptional–{ 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.
searchSymbolsoptionalquery, exchange, typeSearchItem[]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.
resolveSymbolrequiredsymbolSymbolInfoDescribe one instrument. name is the only required field; everything else has a default. Throw to report an unknown symbol.
getBarsrequiredsymbol, resolution, periodBar[]History for a window. ASCENDING by time. Return [] when you have no more, that is what stops the chart walking backwards forever.
subscribeBarsoptionalsymbol, resolution, subscriberUID, pushnothingStart streaming. Call push(bar) for each update. Skip it and the chart draws history and stops, correct for an end-of-day feed.
unsubscribeBarsoptionalsubscriberUIDnothingStop that stream and release whatever it holds.
getMarksoptionalsymbol, from, to, resolutionMark[]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.
getServerTimeoptional–secondsYour 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.
getTimescaleMarksoptionalsymbol, from, to, resolutionTimescaleMark[]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.
searchSymbolsPaginatedoptionalquery, 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.
getFootprintoptionalsymbol, resolution, periodFootprintSlice[]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.
subscribeTradesoptionalsymbol, resolution, subscriberUID, pushnothingThe 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.
unsubscribeTradesoptionalsubscriberUIDnothingStop that order-flow stream.
Sync or async, both work. Return a value or a promise; the bridge resolves either. Throwing, or rejecting, is how you report a failure, and the message reaches the browser console rather than the reader.

The order they are called in

Once per chart, in this sequence. Nothing is called twice for the same reason.

A chart's lifetime
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 …
unsubscribeBars
period.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

FieldTypeMeaning
fromnumberWindow start, in SECONDS.
tonumberWindow end, in SECONDS.
countBacknumberHow many bars the chart wants ending at to. Prefer this over from.
firstDataRequestbooleanTrue for the initial window, false for every scroll back.
Ask your backend with 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.

FieldTypeMeaning
levelsFootprintLevel[]{ 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.
levelStepnumberThe price step the levels sit on. Positive. Absent, the chart uses the symbol's tick, then the spacing of the levels themselves.
footprintSourcestringexchange_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.