Guides
Orders and positions
Put a working order, a position and its protective legs on the chart, draggable, cancellable, and reported back to your page when the reader acts. The chart draws; your venue decides.
The model, before the code
One idea does most of the work here and getting it wrong produces a chart that quietly lies: the frame has no venue, no account and no idea what your exchange said.
So every interaction is a request. The reader drags a line, the chart reports where they dropped it and leaves it there. Whether that becomes an amended order is your page’s decision, and the chart only reflects it once you say so.
reader drags the line
→ chart fires orderMove { handle, price }
→ your page amendOrder(id, price)
→ your venue accepted? → handle.set({ price }) ← confirm
rejected? → handle.set({ price: old }) ← put it backA venue you can make say no
The paragraph above is the whole model and it is hard to believe from a code block, so here it is running. Draw the lines, then drag the dashed bid with the pretend venue set to accepts and again with it set to rejects. Inside the frame the two drags are identical, which is the point: the chart is not the thing deciding.
Drag the dashed bid line. On accepts it stays where you let go. On rejects it snaps back, and the line below shows the call that put it there. The chart did neither on its own.
The snap back on a rejection is handle.set({ price }) from the page, printed in the log as it happens. Delete that one line and a rejected amendment leaves a line on the chart at a price your venue never accepted, with nothing on screen saying so.
The three things you can draw
| What it is | Method | |
|---|---|---|
| Order line | A working order at a price, a resting bid, a stop, a take-profit you placed. | createOrderLine |
| Position line | An open position at its entry, with its protective legs and live P&L. | createPositionLine |
| Execution marker | A fill that already happened, pinned to one bar. | createExecutionShape |
All three are drawn IN the price scale rather than on top of the plot, so they move with the chart, survive a pan and a zoom, and can be dragged. Every property of each is on Objects.
A working order
editable makes it draggable; cancellable gives it an ✕. Neither does anything to your venue, they decide which events you can receive.
const chart = vpEmbed.chart(document.querySelector('.vp-chart'));
const orders = new Map();
const bid = await chart.createOrderLine({
price: 42000,
text: 'Bid · 0.5',
quantity: '0.5',
editable: true, // the reader may drag it
cancellable: true, // …and cancel it
lineColor: '#22c55e',
lineStyle: 2, // 0 solid · 1 dotted · 2 dashed · 3 large dash · 4 sparse
});
orders.set(bid.handle, myOrderId);
chart.on('orderMove', async ({ handle, price }) => {
const id = orders.get(handle);
const ok = await amendOrder(id, price); // YOUR venue
if (!ok) await bid.set({ price: priceOf(id) }); // rejected, put it back
});
chart.on('orderCancel', async ({ handle }) => {
await cancelOrder(orders.get(handle));
await bid.remove();
});A position with a full bracket
Entry, stop and target as one object. The legs are properties of the position rather than three separate lines, so they move together and cannot drift apart.
const pos = await chart.createPositionLine({
price: 41850, // entry
text: 'Long 0.5',
quantity: '0.5',
stopLoss: 41200, // protective stop
takeProfit: 43000, // target
breakeven: 41850, // optional: where the trade stops losing
closeable: true, // the close control, on by default
reversible: true, // the reverse control, OFF by default
});
chart.on('positionClose', ({ handle }) => closePosition(orders.get(handle)));
chart.on('positionReverse', ({ handle }) => reversePosition(orders.get(handle)));
// A press on the body that never travelled is a CLICK, not a drag: open
// whatever ticket you keep for the position rather than moving anything.
chart.on('positionModify', ({ handle }) => openTicket(orders.get(handle)));Letting the reader drag a bracket out
A position line can grow two small handles that a reader pulls away from the pill and releases at a price. It is the same kind of thing as setTrading: a REQUEST, not a fill. Nothing is drawn as a result, because the chart does not know whether your venue accepted it.
const pos = await chart.createPositionLine({
price: 79649,
text: 'Long 0.5',
quantity: '0.5',
side: 'long', // which way it points
canAddTakeProfit: true, // both are OFF by default
canAddStopLoss: true,
});
chart.on('positionAddTakeProfit', async ({ handle, price }) => {
// 'price' is where the reader RELEASED, not the position's entry.
const ok = await placeTakeProfit(orders.get(handle), price);
if (!ok) return; // nothing on the chart moved
// Answer by drawing the leg AND withdrawing the handle that made it.
await pos.set({ takeProfit: price, canAddTakeProfit: false });
});side whenever you offer the handles. It is what makes the gesture honest: a long takes profit above its entry and stops below it, so the draft CLAMPS to the entry and draws an arrow when the pointer is in the half that cannot work. With side unset the drag constrains nothing, and a reader can place a take-profit into a loss and find out from your venue's rejection.protectFillOpacity: 0 turns it off. Styling has the knob.Both handles, both leg colours, the leg names and the button labels are styled the same way everything else on the line is. Styling has the rounded, branded version of the object above.
Place lines after dataLoaded
A price needs a plot to sit in. Before the first bars are drawn the chart has a symbol and an empty canvas, so a line placed then has no scale to be positioned against.
chart.on('dataLoaded', () => {
void drawMyOrders(); // bars exist, the scale is real
});Letting the reader start an order
Everything above draws orders YOU already have. setTrading is the other direction: it adds Buy and Sell rows to the price scale's ⊕ menu, so a reader can pick a level and ask for an order that does not exist yet.
await chart.setTrading({ quantity: '0.5', unitLabel: 'BTC' });
chart.on('orderSubmit', ({ side, type, price, quantity, symbol }) => {
// A REQUEST. 'type' is limit or stop, derived from which side of the
// market the level is on. Nothing has been sent anywhere.
void confirmWithTheUser({ side, type, price, quantity, symbol });
});
await chart.setTrading(null); // withdraw the rows againsetTrading the ⊕ menu offers alerts and drawings only, and orderSubmit can never fire. A chart on a news article should not be offering to buy anything.Clearing up
| Call | What it removes |
|---|---|
handle.remove() | One line. Removing one already gone is not an error. |
chart.removeAllShapes() | Every DRAWING you placed, not your lines, and never the reader's own. |
chart.setTrading(null) | The Buy / Sell rows, leaving everything drawn in place. |
removeAllLines. Your lines correspond to real orders and positions, so a single call that erased the lot is far more likely to be a bug than an intention, and the chart would then disagree with your book with nothing on screen saying so.Every property and every event
This page is the shape of the thing. The exhaustive lists are Objects for what you pass, Methods you call for what you can ask, and Events for what comes back.