Trading overlay
Render positions, orders, signal markers, and trade zones directly on the chart. Designed to integrate with both manual and algorithmic trading flows.
Disabling trading
Trading is opt-out, not opt-in. Projects that don't need trading affordances have two off-switches:
// Drop the entire trading subsystem (no orders, no positions, no overlay, no menu)
new Chart(host, { features: { trading: false } })
// Keep positions and orders visible, but remove the right-click order menu
new Chart(host, { features: { tradingContextMenu: false } }) With either flag set, native browser right-click works on the chart as expected (previously the custom menu suppressed it unconditionally — fixed in 0.8.1).
Positions
chart.addPosition({
id: 'pos-1',
side: 'long',
entry: 65_200,
quantity: 0.5,
closedQuantity: 0.1, // partial-close band on the left edge
stopLoss: 64_800,
takeProfit: 66_000,
}) Orders
chart.addOrder({
id: 'ord-1',
side: 'sell',
type: 'limit',
price: 65_500,
quantity: 0.25,
}) Drag the price line to modify; subscribe via chart.on('orderModify', ...).
Live execution (connect an adapter)
By default the chart emits order/position intents (orderPlace, orderModify, orderCancel, positionModify, positionClose) for your backend — it never trades itself. Connect an ExecutionAdapter and the chart instead routes those intents into the
adapter and renders the authoritative orders/positions it emits back (the adapter is
the single source of truth).
import { PaperExecutionAdapter } from '@tradecanvas/chart'
chart.connectExecution(new PaperExecutionAdapter({ markPrice: 64_000 }))
chart.on('executionError', (e) => toast(e.payload.message))
// chart.disconnectExecution() Implement ExecutionAdapter (it mirrors DataAdapter) to wire a
real broker / OMS: placeOrder, modifyOrder, cancelOrder, modifyPosition, closePosition, plus orders / positions / fill / error events. PaperExecutionAdapter is a virtual-fill sandbox for demos and tests.
Drag-to-create orders
Start a single draggable order line, drag it to a price, and confirm — the order type
(limit vs stop) is inferred from where you drop it relative to the current price. Pairs
with connectExecution so a confirmed draft fills immediately.
chart.startOrderDraft('buy') // draggable line at the latest close
chart.confirmOrderDraft() // emits orderPlace -> a connected adapter fills it
chart.cancelOrderDraft() Bracket orders (drag to place)
Start a draggable bracket — entry plus stop-loss and take-profit zones — then
drag the three lines to tune entry, risk, and reward. Confirm with Enter (or the Place button), cancel with Esc. In the
widget, the green/red toolbar arrows start a long/short bracket. The chart
emits a single bracketPlace event for your backend to act on —
it never places orders itself.
chart.startBracket('buy') // entry defaults to the latest close
chart.startBracket('sell', 64_800) // or pin the entry price
chart.on('bracketPlace', (e) => {
const { side, entry, stopLoss, takeProfit, riskReward } = e.payload
// submit to your OMS, then reflect fills back via chart.setOrders/setPositions
})
chart.confirmBracket() // same as Enter
chart.cancelBracket() // same as Esc Depth ladder (click to trade)
An opt-in depth-of-market ladder renders the order book as price rows with
bid/ask size columns — click an ask cell to buy, a bid cell to sell at that
price. Enable with depthLadder: true and feed the book via widget.setDepth; clicks emit orderPlace intents for
your OMS (the chart never trades itself). The same data also drives the
on-chart depth overlay.
const widget = new ChartWidget(host, { depthLadder: true })
widget.setDepth({
bids: [{ price: 64_190, volume: 3.1 }, { price: 64_185, volume: 5.4 }],
asks: [{ price: 64_205, volume: 2.0 }, { price: 64_210, volume: 8.7 }],
})
widget.getChart().on('orderPlace', (e) => {
// { side, type: 'limit', price } — submit to your backend
}) Liquidity heatmap
Accumulate order-book snapshots into a heatmap behind the candles — each
snapshot is a vertical strip where resting size lights up per price level
(bids green, asks red). Liquidity walls that persist over time stand out.
Toggle from the settings sheet (or chart.setDepthHeatmapVisible); widget.setDepth records a snapshot on every book update.
chart.setDepthHeatmapVisible(true)
chart.setDepthHeatmapConfig({ opacity: 0.7, capacity: 240 })
// each book update both draws the overlay/ladder and records a heatmap column
widget.setDepth(orderBook)
// low-level: chart.pushDepthSnapshot(orderBook) · chart.clearDepthHeatmap() Signal markers
Bot or signal-trading integrations can place directional arrows on the overlay.
chart.addSignalMarker({
id: 'sig-12',
time: bar.time,
price: bar.close,
direction: 'long',
confidence: 0.86,
source: 'momentum-bot',
label: 'EMA cross',
}) Trade zones
Visualize entry → exit rectangles with P&L coloring and direction badges.
chart.addTradeZone({
id: 'tz-1',
side: 'long',
entryTime: openedAt,
exitTime: closedAt,
entryPrice: 65_100,
exitPrice: 65_800,
status: 'closed',
}) Position label tokens
Customize the on-chart label per position. positionLabel accepts a
template string or a function returning a string.
new ChartWidget(host, {
trading: true,
positionLabel: '{side} {qty} @ {entry} · {pnlSign}{pnlPct}%',
}) Available tokens: {side}, {qty}, {openQty}, {closedQty}, {entry}, {price}, {pnl}, {pnlPct}, {pnlSign}.
P&L gradient stops
new ChartWidget(host, {
trading: true,
pnlThresholds: [
{ pnlPct: -0.02, color: '#ef4444' },
{ pnlPct: 0, color: '#94a3b8' },
{ pnlPct: 0.02, color: '#10b981' },
],
})