Skip to content

Reference

Events

What the chart tells you, and the exact shape of each message. Subscribe once; every event arrives on the same handler.

Subscribing

One handler, every event
const chart = vpEmbed.chart(document.querySelector('.vp-chart'));

const off = chart.on('orderMove', ({ handle, price }) => {
  // The reader dragged a line. Nothing has happened to your order yet.
  void updateMyOrder(handle, price);
});

off();   // on unmount. There is no off(name, fn).
on RETURNS the unsubscriber rather than offering an off(name, fn), because that form needs the caller to hand back the same function reference and the failure when they do not is a handler firing twice, which nobody notices until it does something twice. In a component, call it from the teardown. Nothing else unsubscribes: a chart that is removed from the page takes its listeners with it, but a chart that is kept and re-subscribed to does not.
Writing your own bridge instead? These arrive as an event message with { name, payload }, see Wire messages.

State changes

Fired whether the change came from you or from the reader, so a handler cannot tell them apart, and should not need to. Nothing here expects a reply.

EventPayloadWhen it fires
symbolChanged{ symbol }You or the reader changed instrument.
intervalChanged{ interval }The resolution changed, from either side.
chartTypeChanged{ type }The chart type changed.
dataLoaded{}A page of bars has been drawn. A good moment to place your lines.
visibleRangeChanged{ from, to }The reader panned or zoomed. Seconds.
priceScaleChanged{ from, to, top, height, left, width, scale, inverted }The price-to-pixel mapping moved: a new range, a resized iframe, or the reader switching to a log or inverted scale. Subscribe to keep your own overlay pinned to the chart. Fires once as soon as the chart has laid out, so a page can draw at mount rather than waiting for the reader to move something, and at most once per animation frame.
dataLoaded is the moment worth waiting for before placing lines. Bars are drawn, so a price you place has somewhere to sit; before it, the chart has a symbol and an empty plot.

Trading interactions

These are the whole reason the chart can carry orders. Every one is a request from the reader, not a thing that has happened: the frame has no venue, no account and no idea whether you accepted it.

EventPayloadWhen it fires
orderSubmit{ side, type, price, quantity, symbol }The reader picked Buy or Sell at a level. A request rather than a fill: the frame has no venue and no account, so your page decides. type is limit or stop, derived from which side of the market the level is on. Only ever fires after setTrading.
orderMove{ handle, price }An editable order line was dragged. Fires on RELEASE, never during the drag.
orderModify{ handle, price }An order line’s body was CLICKED rather than dragged, a press that never travelled. Distinct from orderMove on purpose: one says the reader put the level somewhere new, this says they want whatever ticket you keep for it.
orderCancel{ handle }The cancel button on a cancellable order line was pressed.
positionClose{ handle }A closeable position line was closed.
positionModify{ handle, price }A position line’s body was clicked. Open whatever ticket you keep for that position.
positionReverse{ handle }The reverse control was pressed. Only ever fires on a line you made reversible.
positionMove{ handle, price }An editable position line was dragged, on RELEASE. Absent on an ordinary position line, which does not drag at all.
positionAddTakeProfit{ handle, price }The reader pulled the TP handle off the pill and let go. price is where they RELEASED, not the position’s entry. A request, exactly like orderSubmit: nothing is drawn as a result, and the way you answer is to set takeProfit on the line and turn canAddTakeProfit off, which keeps what is on the chart equal to what your venue actually holds.
positionAddStopLoss{ handle, price }The same gesture on the stop handle. Answer it by setting stopLoss and clearing canAddStopLoss.
Nothing on the chart moves on its own. A dragged order line reports where it was dropped and stays where the reader put it, we do not know whether your venue accepted the move. Your handler decides, and calls handle.set() to confirm or to put it back. A chart that snapped the line to a price the exchange rejected would be showing a resting order that does not exist.
orderMove fires on RELEASE, never during the drag. A per-pixel stream would be a request per frame at your endpoint, and the reader has not decided anything until they let go.
Four of these fire only on a control you asked for. positionReverse needs reversible, positionMove needs editable, and the two bracket handles need canAddTakeProfit / canAddStopLoss. A line drawn with none of them reports only positionClose and positionModify, which is what an embed drew before those properties existed. See Objects.

Your own control

One event, and it is the whole of what a toolbar button does: the frame draws your label in the chart's own header and tells you when it was pressed. Every consequence is yours. See createToolbarButton on Methods you call.

EventPayloadWhen it fires
toolbarButtonClick{ handle }A toolbar button you added was pressed, by pointer or by keyboard. The whole of what such a button does: the frame draws a label and reports the press, and every consequence is yours.

The reader's preferences

The embed writes no storage, so a timeframe the reader stars is gone on the next page load unless you keep it. The list also rides saveState under favoriteIntervals, and loadState puts it back, so a page that already saves the chart needs nothing more than to save on this event too.

EventPayloadWhen it fires
favoriteIntervalsChanged{ intervals }The reader starred or unstarred a timeframe in the interval menu. intervals is the whole list, in menu order. The embed keeps nothing between page loads, so this is your cue to save it, by itself or through saveState. Restoring a list with loadState does not fire it.

The handle field

Four of the trading events carry handle, the same value the create* call resolved to. It is how you know WHICH of your lines the reader touched, so keep it beside whatever it represents in your own state.

Matching an event back to your order
const orders = new Map();

const line = await chart.createOrderLine({ price: 42000, text: 'Bid', editable: true });
orders.set(line.handle, myOrderId);

chart.on('orderMove', ({ handle, price }) => {
  const id = orders.get(handle);          // ← which order this was
  void amendOrder(id, price);
});
It is not the field a first draft of this page claimed. That draft wrote { id, price } from memory where the wire sends { handle, price }, it destructures to undefined and looks completely reasonable while doing so. Every payload above is now compared against the emitter key by key on every build.