DocumentationTrading API

Trading API

chart.trading is a data-driven trade layer. Your app pushes exchange state - positions, resting orders, bracket orders (TP/SL), and trade fills - and the chart renders broker-style segmented pill groups ([badge][qty][label][✕]) and fill markers, with hover/dragging states and a drag ghost built in. When the user drags an order or clicks the ✕ segment, the chart emits a trading:* event for your app to relay to the exchange.

Your app ──setOrders()──▶ chart renders line + pill

   user drags the order   ▼
Your app ◀─'trading:order_modify'── chart  ──▶ Exchange API

It is created on first access (chart.trading), works with any data source, and has no framework dependencies.

Live example

A long position pill, a resting limit order, TP/SL brackets, and two fill markers - all from chart.trading. Drag the order lines; the ✕ boxes fire cancel/close events.

const chart = lib.createChart(el);
const bars = lib.generateBars(1700000000, 140, 3600);
chart.addSeries('candlestick').setData(bars);
const last = bars[bars.length - 1].close;

chart.trading.setPositions([
{ id: 'p1', side: 'long', entryPrice: last * 0.985, size: 1.5, pnlText: '+$1,240', pnlPercent: '+2.4%' },
]);
chart.trading.setOrders([
{ id: 'o1', type: 'limit', side: 'buy', price: last * 0.95, size: 1 },
{ id: 'tp', type: 'limit', side: 'sell', price: last * 1.04, size: 1.5, parentId: 'p1', bracketRole: 'tp' },
{ id: 'sl', type: 'stop',  side: 'sell', price: last * 0.93, size: 1.5, parentId: 'p1', bracketRole: 'sl' },
]);
chart.trading.setTrades([
{ id: 'f1', side: 'buy',  price: bars[40].close, size: 1, timestamp: bars[40].time * 1000, variant: 'chevron' },
{ id: 'f2', side: 'sell', price: bars[95].close, size: 1, timestamp: bars[95].time * 1000, variant: 'bubble' },
]);

chart.trading.on('trading:order_modify', function (e) {
// send an amend to your exchange with e.newPrice
});

chart.timeScale.fitContent(bars.length);
return chart;
live
Rendering live chart…
Position + resting order + TP/SL brackets + buy/sell fill markers — segmented pills, all draggable/cancelable with hover states and a drag ghost.

Quick start

import { createChart } from 'openalgo-charts';
 
const chart = createChart(el);
chart.addSeries('candlestick').setData(bars);
 
chart.trading.syncState({
  positions: [{ id: 'pos_1', side: 'long', entryPrice: 50000, size: 1, pnlText: '+$100.00' }],
  orders:    [{ id: 'ord_1', type: 'limit', side: 'buy', price: 48000, size: 0.5 }],
  trades:    [{ id: 'fill_1', side: 'buy', price: 50000, size: 1, timestamp: Date.now() }],
});
 
chart.trading.on('trading:order_cancel', ({ orderId }) => exchange.cancel(orderId));

Data models

TradingPosition

FieldTypeDescription
idstringunique id
side'long' | 'short'direction (line color)
entryPricenumberthe pill is drawn at this price
sizenumberposition size (shown on the pill)
pnlTextstring?pre-formatted P&L shown on the pill (e.g. '+$100.00')
pnlPercentstring?pre-formatted P&L percent
colorstring?per-position line color override
readOnlyboolean?hide the close (✕) button
variant'standard' | 'line-only'line-only renders just the line + axis tag (no pill)

TradingOrder

FieldTypeDescription
idstringunique id
type'limit' | 'stop' | 'stop_limit'order type (shown on the pill)
side'buy' | 'sell'direction
pricenumberorder price
sizenumberorder size
parentIdstring?link to a position/order (bracket child)
bracketRole'tp' | 'sl'take-profit / stop-loss (colors + TP/SL label)
color / lineStyle / lineWidthstring? / 'solid'|'dashed'|'dotted' / number?line styling
readOnlyboolean?hide the cancel (✕) button
draggableboolean?allow drag-to-modify (default: !readOnly)
variant'standard' | 'line-only'line-only skips the pill

TradingTrade

FieldTypeDescription
idstringunique id
side'buy' | 'sell'marker color
pricenumbermarker price
sizenumberfill size (used for count VWAP)
timestampnumberexecution time in milliseconds
variant'chevron' | 'bubble' | 'count'marker style (default chevron)
color / labelstring?per-marker overrides

API methods

Access via chart.trading.

MethodDescription
setPositions(positions) / setOrders(orders) / setTrades(trades)replace all of that kind
syncState({ positions?, orders?, trades? })push several kinds in one call
upsertOrder(order)add or update one order
removeOrder(id)remove an order (and its bracket children)
addTrade(trade)add one fill
updatePositionPnl(id, pnl, pnlText?, pnlPercent?)refresh a position’s P&L pill without recreating the line
getPositions() / getOrders() / getTrades()current state
clear()remove everything
setSettings(colors) / getSettings()global trading colors
on(event, cb) / off(event, cb)subscribe / unsubscribe (on returns an unsubscribe fn)

Events

Subscribe with chart.trading.on(event, cb).

EventPayloadFires when
trading:order_modify{ orderId, newPrice, previousPrice }an order line is dragged and released
trading:bracket_modify{ parentId, bracketRole, newPrice }a TP/SL bracket line is dragged
trading:order_cancel{ orderId }the ✕ on an order pill is clicked
trading:position_close{ positionId }the ✕ on a position pill is clicked
trading:order_click{ order }an order pill body is clicked
trading:position_click{ position }a position pill body is clicked

Settings

Global colors override the defaults and re-render existing lines/markers:

chart.trading.setSettings({
  longColor: '#00C853', shortColor: '#AA00FF',
  orderColor: '#7C4DFF', tpColor: '#00BCD4', slColor: '#FF1744',
  buyColor: '#26a69a', sellColor: '#ef5350',
});

Patterns

WebSocket sync - push a full snapshot in one frame:

ws.on('account', (a) => chart.trading.syncState({ positions: a.positions, orders: a.orders }));

High-frequency P&L - updatePositionPnl only refreshes the pill text:

ws.on('mark', ({ price }) => {
  const pnl = (price - entry) * size;
  chart.trading.updatePositionPnl('pos_1', pnl, (pnl >= 0 ? '+' : '') + '$' + pnl.toFixed(2));
});

Confirm-then-render modify - keep the exchange as the source of truth:

chart.trading.on('trading:order_modify', async ({ orderId, newPrice }) => {
  const ok = await exchange.amend(orderId, newPrice);
  if (!ok) chart.trading.setOrders(currentOrders); // revert to server state
});

Timestamps on trades are milliseconds (markers snap to the nearest bar). Positions and orders are drawn at their price with the pill anchored near the right (price) axis. For the lower-level order engine, depth ladder, and validation, see On-Chart Trading.