Reference
Methods you call
This article lists every method a mounted chart accepts. Call them through the loader as chart.<name>(), or post a command message directly; the method names are identical either way.
Getting a chart to talk to
vpEmbed.chart(el) returns a handle for one placeholder element. Every method returns a promise, because the chart runs in a separate document and the reply crosses an origin boundary.
const chart = vpEmbed.chart(document.querySelector('.vp-chart'));
await chart.setInterval('60');
const line = await chart.createOrderLine({ price: 42000, text: 'Bid' });
await line.set({ price: 42500 }); // one message, one repaint
await line.remove();ready fires when the frame’s document mounts, shortly before the widget is constructed, so commands issued at that point are held and replayed in order. Buffering is safe only at this stage: with no chart yet, a held command has no state to become stale against.Methods that are not commands
The tables below describe the command API, which is a single message type carrying a method name. Three methods on the chart object are not part of it, and neither are the five synchronous price-scale readers further down.
| Method | Returns | What it does |
|---|---|---|
chart.setSymbol(s) | nothing | Changes the instrument. It uses an older message type rather than a command, so it returns no reply: a symbol the embed refuses is ignored and the chart retains the previous one. |
chart.setTheme(t) | nothing | Applies light or dark immediately. For a page with its own theme toggle, see Styling. |
chart.on(name, fn) | an unsubscribe function | Subscribes to an event. Call the returned function when your component unmounts. There is no off method: it would require the original handler reference, and a handler left subscribed fires twice with no visible error. |
setSymbol and setTheme predate the command API and were left unchanged so that snippets written against the first release continue to work. The practical difference is that they resolve to nothing. To confirm a change was applied, read it back with getSymbol().<div class="vp-chart"
data-symbol="BTC-USDT"
data-datafeed="myfeed"></div><div class="vp-chart"
data-symbol="BTC-USDT"
data-datafeed="myfeed"
data-theme="light"></div>const off = chart.on('intervalChanged', (e) => {
console.log('now on', e.interval);
});
off(); // CALL what it returns when your component unmountsHandles, and why they take an object
Every create* call resolves to a handle. The three trading calls return the same handle type. createToolbarButton returns a button handle, which carries no price.
| Method | On | What it does |
|---|---|---|
handle.set(props) | both | Changes any subset of the object's properties in one message and one repaint. |
handle.remove() | both | Removes it from the chart. Removing an object twice is not an error. |
handle.setPrice(p) | lines | Shorthand for set({ price }). |
handle.setText(t) | both | Shorthand for set({ text }). |
handle.setActive(on) | buttons | Shorthand for set({ active }), which is also what makes the button announce itself as a toggle to assistive technology. |
There are no further shorthands and no chaining. A chained .setPrice(x).setText(y) would be two messages, with one frame rendered in between showing the new price beside the old label, so any change to two properties at once must go through set. handle.id is the string commandResult returned; a bridge written without the loader holds that instead of a handle, and a toolbarButtonClick payload names it, so a chart with several buttons can identify which was pressed.
lineColourreturns a commandError identifying it. A recognised property outside its domain is also refused: lineStyle accepts 0 to 4, and lineStyle: 9 was previously accepted, applied, and rendered nothing. Every property and its domain is listed on Objects.What is on screen
The instrument, the resolution, the shape of the bars and the window in view.
| Call it as | Wire method | What it does |
|---|---|---|
chart.setInterval(v) | setInterval | Change resolution. Same vocabulary as data-interval. |
chart.setChartType(v) | setChartType | candles · bars · line · area · footprint. The last draws order flow per price level and needs a feed that supplies it (levels on the bars, getFootprint, or subscribeTrades); without one it draws bars with no cells rather than a synthesised distribution. |
chart.setVisibleRange({from,to}) | setVisibleRange | Scroll and zoom to a window. Seconds. |
<div class="vp-chart"
data-symbol="BTC-USDT"
data-datafeed="myfeed"
data-interval="60"></div><div class="vp-chart"
data-symbol="BTC-USDT"
data-datafeed="myfeed"
data-type="area"></div>// The last thirty days. SECONDS, not milliseconds.
const to = Math.floor(Date.now() / 1000);
await chart.setVisibleRange({ from: to - 30 * 86400, to });How it looks
The palette, as a live call rather than an opening attribute. Ten role names, the same ones data-colors takes.
| Call it as | Wire method | What it does |
|---|---|---|
chart.setStyle({ bg, up, … }) | setStyle | Recolour the chart. The runtime half of data-colors, same ten role names. Merges, so a call names only what it is changing, and it outlives setTheme, because your palette is re-applied on top of each base theme rather than dropped. |
<div class="vp-chart"
data-symbol="BTC-USDT"
data-datafeed="myfeed"
data-colors="bg:fffdf7,text:44403c,grid:e7e5e4,up:15803d,down:b91c1c"></div>The reader's chart, kept by you
We store nothing about your reader, so a page that wants their layout to survive a reload is the page that keeps it. Take the whole document, or just the annotations.
| Call it as | Wire method | What it does |
|---|---|---|
chart.saveState() | saveState | Hand the whole chart back as plain data: indicators, drawings, panes, the lot, plus the timeframes the reader starred under favoriteIntervals. Where you keep it is yours; we store nothing about your reader. Feed it to loadState to put it back. |
chart.loadState(state) | loadState | Restore what saveState gave you, starred timeframes included. It carries the symbol and interval it was saved on, so a page that owns those wants setDrawings instead. A state without favoriteIntervals leaves the reader’s stars alone. |
chart.getDrawings() | getDrawings | Just the annotations, as an array. The pair to reach for when your page decides the instrument and the reader decides what is drawn on it. |
chart.setDrawings(drawings) | setDrawings | Put them back. Replaces every drawing on the chart rather than adding to them, so restoring twice does not double them. |
// Remember the reader's chart. Where you store it is yours; we keep nothing.
const state = await chart.saveState();
localStorage.setItem('their-chart', JSON.stringify(state));const saved = localStorage.getItem('their-chart');
if (saved) await chart.loadState(JSON.parse(saved));// Just the annotations. Your page still owns the instrument.
const drawings = await chart.getDrawings();
await fetch('/api/chart-notes', { method: 'POST', body: JSON.stringify(drawings) });const drawings = await (await fetch('/api/chart-notes')).json();
await chart.setDrawings(drawings);The clock, and pinning the view
The clock the time axis is read in, how every number is punctuated, and whether the reader may pan or zoom away from the window you selected. None of these hides a control; the locks constrain the pointer only.
| Call it as | Wire method | What it does |
|---|---|---|
chart.setTimezone(name) | setTimezone | The clock the time axis is read in. An IANA name such as Europe/Paris or UTC, never an offset, which cannot express daylight saving. The runtime half of data-timezone. |
chart.setNumberFormat('european') | setNumberFormat | How every number on the chart is punctuated: standard (1,234.56), european (1.234,56), plain (1234,56), spaced (1 234,56), or auto for the reader’s own locale. The runtime half of data-numberFormat. |
chart.setSessions([{ name, from, to, color }]) | setSessions | Tint trading sessions behind the bars. Each band is { name, from, to, color, days?, timezone? }: from / to are HH:mm read on the symbol’s own clock, or on timezone when the band names one; to <= from wraps midnight; days is 0 = Sunday to 6 = Saturday and defaults to weekdays; color carries its own alpha. Replaces the set, and an empty array clears it. The runtime half of data-sessions. |
chart.setScrollEnabled(false) | setScrollEnabled | Stop the reader panning off the window you chose. Hides no control, so the range chips still work if the toolbar is on. |
chart.setZoomEnabled(false) | setZoomEnabled | The same for zoom. Pair them to pin a chart to exactly one view. |
<div class="vp-chart"
data-symbol="AAPL"
data-datafeed="myfeed"
data-timezone="America/New_York"></div><div class="vp-chart"
data-symbol="BTC-USDT"
data-datafeed="myfeed"
data-numberFormat="european"></div><div class="vp-chart"
data-symbol="AAPL"
data-datafeed="myfeed"
data-sessions="us"></div>// Pin the chart to the window you chose.
await chart.setVisibleRange({ from: 1767225600, to: 1769904000 });
await chart.setScrollEnabled(false);
await chart.setZoomEnabled(false);await chart.setZoomEnabled(false);Indicators, listed as well as added
What is on the chart and what may be. The first is how you remove one indicator rather than all of them; the second is how you build your own picker.
| Call it as | Wire method | What it does |
|---|---|---|
chart.getStudies() | getStudies | What is on the chart right now, as { id, name } each. The id is what removeEntity takes, which is how you remove one indicator rather than all of them. Accepted as getAllStudies too. |
chart.getAvailableStudies() | getAvailableStudies | What MAY be added, straight from the engine’s registry. Build your own indicator picker from it instead of hardcoding a list that drifts from whatever we ship. |
// Remove ONE indicator rather than all of them.
const studies = await chart.getStudies(); // [{ id, name }]
const rsi = studies.find((s) => s.name === 'RSI');
if (rsi) await chart.removeEntity(rsi.id);// Build your own indicator picker from what the engine actually ships.
const available = await chart.getAvailableStudies(); // [{ id, name }]
for (const study of available) addRow(study.name, () => chart.createStudy(study.id));The bars behind the picture
The visible series as CSV. It narrows what is already loaded and never fetches, so on your own datafeed we never spend your rate limit on a call you did not make.
| Call it as | Wire method | What it does |
|---|---|---|
chart.exportData({ from, to }) | exportData | The bars behind the picture, as CSV. It narrows what is already loaded and never fetches, so on your own datafeed we never spend your rate limit on a call you did not make. null when the chart has no bars yet. |
const csv = await chart.exportData({ includeTime: true });
if (csv) download('bars.csv', csv);Badges under the bars
The round B / S pastille where a fill happened, with a tooltip on hover. One call carries the whole set, and each mark names the bar it belongs to.
| Call it as | Wire method | What it does |
|---|---|---|
chart.setMarks(marks) | setMarks | Put a round badge under a bar: the B / S pastille where a fill happened, with a tooltip on hover. Replaces the whole set, so send the list you want on screen rather than one mark at a time. time is epoch SECONDS and must be the bar’s OWN opening time: a mark is looked up by the bar it belongs to rather than placed at a pixel, so one that falls between bars draws nothing and says nothing. Take it from a bar you have rather than from a clock, and mind that a daily bar’s anchor is the venue’s, which is not always midnight UTC. |
chart.clearMarks() | clearMarks | Take them all off. Distinct from sending an empty array only in that it takes no argument; both leave the chart with no badges. |
// One badge per fill, under the bar it happened on.
await chart.setMarks([
{
id: 'fill-1',
time: 1767225600, // SECONDS, and the BAR's own time
label: 'B', // 1-3 characters, drawn inside the badge
color: '#26a69a',
labelFontColor: '#ffffff',
text: ['Bought 0.5 BTC @ 61,240', 'Filled - market'],
},
{
id: 'fill-2',
time: 1767312000,
label: 'S',
color: '#ef5350',
labelFontColor: '#ffffff',
text: 'Sold 0.5 BTC @ 64,980',
},
]);await chart.clearMarks();Your own control in the toolbar
The one place your page's UI can live inside the frame. A label and a press; the frame draws it in the chart's own style. Needs the toolbar on.
| Call it as | Wire method | What it does |
|---|---|---|
chart.createToolbarButton({ text }) | createToolbarButton | Put one of YOUR controls in the chart’s own header. Resolves to a handle; the press arrives as toolbarButtonClick. Needs data-toolbar="1", and says so rather than making a button nobody can see. |
button.set(props) | updateToolbarButton | Change a button’s label, tooltip or pressed state. |
button.remove() | removeToolbarButton | Take it back off. Removing one already gone is not an error. |
const watch = await chart.createToolbarButton({
text: 'Watch',
tooltip: 'Add this instrument to your watchlist',
align: 'right', // left puts it among the chart's own controls
});await watch.set({ text: 'Watching', active: true });
// …or the sugar, for the two a button changes most
await watch.setText('Watching');
await watch.setActive(true);await watch.remove(); // removing one already gone is not an errorOrders and positions
Levels drawn in the price scale rather than on top of it, so they move with the chart and can be dragged.
| Call it as | Wire method | What it does |
|---|---|---|
chart.setTrading({ quantity, unitLabel }) | setTrading | Declare that this page can carry an order. Adds Buy / Sell rows to the price scale’s ⊕ menu, which emit orderSubmit. null withdraws them. Without it the menu offers alerts and drawings only, because the frame never assumes its host trades. |
chart.createOrderLine(props) | createOrderLine | A price level, optionally draggable and cancellable. |
chart.createPositionLine(props) | createPositionLine | An entry with its take-profit, stop and breakeven legs. |
chart.createExecutionShape(props) | createExecutionShape | A buy or sell marker at one bar. |
handle.set(props) | updateLine | Change any subset of a line’s properties. One message, one repaint. |
handle.remove() | removeLine | Take a line off the chart. Removing one already gone is not an error. |
await chart.setTrading({ quantity: 0.5, unitLabel: 'BTC' });
chart.on('orderSubmit', (e) => {
// A REQUEST, not a fill. Your page decides.
placeOrder(e.side, e.type, e.price, e.quantity, e.symbol);
});
await chart.setTrading(null); // withdraw the rows againconst bid = await chart.createOrderLine({
price: 64500,
text: 'Buy limit',
quantity: '1.5',
lineColor: '#26a69a',
editable: true, // the reader may drag it
cancellable: true, // …and dismiss it
});const pos = await chart.createPositionLine({
price: 64000,
text: 'Long 2 BTC',
quantity: '2',
takeProfit: 68000,
stopLoss: 62500,
});await chart.createExecutionShape({
time: 1767225600, // SECONDS. A bar's own time field is milliseconds
price: 64100,
direction: 'buy',
text: 'Filled 1.5',
arrowColor: '#26a69a',
});await pos.set({ stopLoss: 63000 }); // trail it up
await pos.set({ takeProfit: null }); // cancel the target
await bid.setPrice(64200); // sugar for set({ price })await bid.remove(); // idempotent, so cleanup need not track what is leftDrawings
Anything you would otherwise draw by hand, levels, arrows, notes, trend lines, zones — and a list of what is there, so you can take one away without taking all of them.
| Call it as | Wire method | What it does |
|---|---|---|
chart.getShapes() | getShapes | Every drawing on the chart, as { id, name } each, where the name is the tool. The id is what removeEntity takes, so this is how you remove one drawing rather than all of them. Ids and tool names only; getDrawings is the one that hands back geometry. Accepted as getAllShapes too. |
chart.createShape(point, opts) | createShape | A single-point drawing: level, arrow, flag, note. |
chart.createMultipointShape(points, opts) | createMultipointShape | Two to eight points: trend line, zone, channel, retracement. |
chart.removeEntity(id) | removeEntity | Remove one drawing or one indicator by the id you were given. |
chart.removeAllShapes() | removeAllShapes | Clear every drawing YOU placed. |
// Remove ONE drawing rather than all of them.
const shapes = await chart.getShapes(); // [{ id, name }]
for (const shape of shapes) {
if (shape.name === 'horizontal_line') await chart.removeEntity(shape.id);
}const level = await chart.createShape(
{ time: 1767225600, price: 65000 },
{ shape: 'horizontal_line', text: 'Resistance', color: '#ef5350' },
);const trend = await chart.createMultipointShape(
[{ time: 1766620800, price: 61000 },
{ time: 1767830400, price: 66000 }],
{ shape: 'trend_line', color: '#787b86' },
);await chart.removeEntity(level); // the id createShape or createStudy gave youawait chart.removeAllShapes(); // only the drawings YOU placedIndicators
Built-in studies, by name, with their inputs.
| Call it as | Wire method | What it does |
|---|---|---|
chart.createStudy(name, inputs) | createStudy | Add an indicator. Up to 16 inputs. |
chart.removeAllStudies() | removeAllStudies | Clear the indicators you added. |
const ma = await chart.createStudy('Simple Moving Average', [20]);
const rsi = await chart.createStudy('RSI', [14]);
// Names resolve case-insensitively, and an id works: 'sma' is the same study.await chart.removeAllStudies();Reading the chart back
Every getter here returns a promise, because the chart is in another document and the answer crosses an origin boundary. The one exception is the price scale, which is pushed to you instead and can therefore be read synchronously.
| Call it as | Wire method | What it does |
|---|---|---|
chart.getSymbol() | getSymbol | The instrument on screen. |
chart.getInterval() | getInterval | The current resolution. |
chart.getChartType() | getChartType | The current chart type. |
chart.getVisibleRange() | getVisibleRange | { from, to } in seconds. |
chart.getVisiblePriceRange() | getVisiblePriceRange | The price-to-pixel mapping, fetched once: the visible bounds, the plot band in the frame’s own pixels, and whether the scale is log or inverted. Resolves to null while the chart has not laid out. For a layer that FOLLOWS the chart, use the synchronous priceToY and subscribe to priceScaleChanged instead. |
const symbol = await chart.getSymbol(); // 'BTC-USDT'const interval = await chart.getInterval(); // '60'const type = await chart.getChartType(); // 'candles'const { from, to } = await chart.getVisibleRange(); // secondsconst scale = await chart.getVisiblePriceRange();
if (scale) {
console.log(scale.from, scale.to); // visible prices
console.log(scale.top, scale.height); // the plot band, in frame pixels
console.log(scale.scale, scale.inverted); // 'linear' | 'log', and the flip
}Where prices are, in pixels
To draw a layer of your own over the chart, a band, a rail of labels, a set of levels in your own canvas, you need to know where a price lands. These five methods answer that. They are the only synchronous methods on the chart object, and that is deliberate: an overlay is repainted inside a requestAnimationFrame callback while the reader pans, and a promise cannot be spent there. Rather than answering one price at a time, the frame publishes the whole mapping whenever it moves and the loader keeps the last one.
| Method | Returns | What it does |
|---|---|---|
chart.getPriceScale() | PriceScale or null | The live mapping: visible bounds, the plot band in the frame’s own pixels, and whether the scale is log or inverted. See Objects. Read-only. |
chart.priceToY(price) | number or null | A price as pixels from the IFRAME’s top edge, which is the coordinate space an element positioned over the iframe already uses. |
chart.yToPrice(y) | number or null | The price under a y measured from the iframe’s top edge. |
chart.priceToCoordinate(price) | number or null | The same as priceToY, measured from the top of the PLOT BAND rather than the iframe. The difference is exactly getPriceScale().top. |
chart.coordinateToPrice(y) | number or null | The inverse of priceToCoordinate. |
null until the chart has laid out and sent its first priceScaleChanged, which is a frame or two after mount. Draw nothing on a null rather than falling back to a guess: the next event carries real numbers. The plot band is also not the iframe, the price gutter, the time axis and any indicator pane sit outside it, so a layer measured against the iframe’s own height lands slightly high on every level.const chart = vpEmbed.chart(document.querySelector('.vp-chart'));
const canvas = document.querySelector('#overlay'); // positioned over the iframe
function draw() {
const scale = chart.getPriceScale();
if (!scale) return; // not laid out yet
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#22c55e';
for (const level of myLevels) {
const y = chart.priceToY(level); // synchronous, no round trip
ctx.beginPath();
ctx.moveTo(scale.left, y);
ctx.lineTo(scale.left + scale.width, y);
ctx.stroke();
}
}
chart.on('priceScaleChanged', draw); // fires once at mount, then on every movepriceScaleChanged is the only subscription this needs. It fires when the range moves, when the iframe is resized, and when the reader switches to a log or inverted scale, at most once per animation frame and never repeating a mapping you already hold. Redrawing from it rather than from your own timer means your layer moves on the same frames the candles do.
Deliberately absent
Some of what a charting library usually offers is not here. The line is between what your reader made and what they were merely doing: a drawing is deliberate, on screen, and theirs to keep, so you can read it back and store it. Where their pointer went is not.
| Not offered | Why |
|---|---|
crossHairMoved | Cursor position is something a reader is doing rather than something they created. It is not visible to them as data, and no host feature requires it. |
| Anything touching identity, storage or navigation | The frame's whole safety model is that it cannot reach them. A method that could would make the iframe decorative. |
Aliases
Two methods answer to a second name, so that code written against the conventional spelling runs unchanged: getAllShapes is getShapes, and getAllStudies is getStudies. The frame resolves the alias before validating, so each pair has one validator and one implementation and the two spellings cannot answer differently.
getAllShapes was previously on this list. The position was inconsistent: getDrawings was already available and returns every anchor, price and any text the reader typed, while the refused method returned only tool names. Reading drawings is now permitted and named as such. getShapes returns ids and tool names; getDrawings returns geometry, including your reader's own text, which is worth knowing before you store or log it.A colour may not be url(...) either. A canvas is not an injection surface, but a CSS colour that fetches is a beacon we would be hosting.