API reference
Public surface of the three top-level classes: Chart, ChartWidget, and ChartGrid.
Chart
Headless renderer. Bring your own UI; subscribe to events; mutate state imperatively.
Construction
new Chart(host: HTMLElement, options?: ChartOptions) Data
| Method | Purpose |
|---|---|
setData(data) | Replace the entire series. |
appendBar(bar) | Append a new bar; auto-scroll if enabled. |
appendBars(bars) | Batch append; recalculates indicators once. |
updateLastBar(bar) | Mutate the current forming bar. |
updateLastBarFromTick(tick) | Merge a tick into the last bar. |
getData() | Read the raw OHLC series. |
Chart type & theme
| Method | Purpose |
|---|---|
setChartType(type) | One of 17 types — see Chart types. |
setTheme(name) | Switch between built-in themes. |
setTimeframe(tf) | Switch active timeframe; rewires the live stream. |
Indicators
| Method | Purpose |
|---|---|
addIndicator(id, params?, position?) | Adds an overlay or panel indicator. Returns instance id. |
updateIndicator(instanceId, params) | Mutate a live indicator. |
removeIndicator(instanceId) | Remove and tear down. |
Axis & scale
The price axis (right strip) and time axis (bottom strip) accept direct pointer interaction — same gestures as TradingView:
| Gesture | Effect |
|---|---|
| Drag price axis up / down | Compress / expand the vertical price range (disables auto-scale). |
| Drag time axis left / right | Zoom in / out on the time axis. |
| Double-click price axis | Re-enable auto-scale. |
| Double-click time axis | Fit all data to the viewport. |
Timezone. Time-axis labels and the crosshair time pill follow the browser's local zone by default; switch to a fixed UTC offset (or back to local) from the settings sheet or directly:
chart.setTimezoneOffset(-300) // EST (UTC-5), in minutes
chart.setTimezoneOffset(330) // IST (UTC+5:30)
chart.setTimezoneOffset(null) // back to browser-local The same effects are also available programmatically:
chart.setAutoScale(false) // freeze the current price range
chart.setLogScale(true) // switch to logarithmic price scale
chart.fitContent() // zoom out to all data
chart.scrollToEnd() Price scale modes. Beyond regular and logarithmic, the axis
can rebase its labels against the first visible bar — percentage shows % change, indexedTo100 rebases the baseline to 100. Regular,
percentage, and indexed share the same linear geometry; only the labels
differ. Settable from the chart-settings panel or directly:
chart.setScaleMode('percentage') // axis labels: +12.34% from first visible bar
chart.setScaleMode('indexedTo100') // first visible bar reads as 100
chart.setScaleMode('logarithmic')
chart.getScaleMode() Volume Profile
Horizontal histogram of traded volume bucketed by price over the visible range. Off by default — toggle programmatically or via the widget settings sheet:
chart.setVolumeProfileVisible(true)
chart.setVolumeProfileConfig({
buckets: 48, // resolution of the histogram
widthRatio: 0.18, // % of chart width
opacity: 0.32,
highlightPoC: true, // mark the highest-volume bucket
}) Swing markers (pivots)
Mark fractal swing highs/lows with small triangles (▼ above a confirmed pivot high, ▲ below a pivot low). The strength controls how many bars must be lower on each side. Toggle from the settings sheet, or:
chart.setPivotMarkersVisible(true)
chart.setPivotMarkersConfig({ left: 5, right: 5, showLabels: true })
// market-structure labels (HH / HL / LH / LL) instead of price
chart.setPivotMarkersConfig({ structureLabels: true })
// pure detection + classification are exported
import { findPivots, classifyPivots } from '@tradecanvas/core'
const pivots = findPivots(bars, 5, 5) // [{ index, price, type }]
const structure = classifyPivots(pivots) // adds label: 'HH'|'LH'|'HL'|'LL' Session shading (regular trading hours)
Dim bars outside the regular session (pre/post-market or the overnight break) so the cash session stands out. Defaults to US equity RTH (09:30–16:00 ET); configure the window in minutes-of-day plus a timezone offset:
chart.setSessionShadingVisible(true)
chart.setSessionShadingConfig({
startMinute: 9 * 60 + 30, // 09:30
endMinute: 16 * 60, // 16:00 (end-exclusive)
tzOffsetMinutes: -300, // EST; handles overnight sessions when end < start
}) Prior-period levels (PDH / PDL / PDC)
Draw the prior day's (or week's) high, low, and close plus the current period's open as labelled horizontal lines — the support/resistance levels intraday traders watch. Toggle from the settings sheet, or directly:
chart.setPeriodLevelsVisible(true)
chart.setPeriodLevelsPeriod('week') // 'day' (PDH/PDL/PDC) | 'week' (PWH/PWL/PWC)
// pure computation is exported
import { computePeriodLevels } from '@tradecanvas/core'
const levels = computePeriodLevels(bars, 'day') // [{ id, label, price }] Market Profile (TPO)
A time-at-price histogram: each bar contributes one TPO to every price bucket its range touched, surfacing the Point of Control (busiest price) and the value area (≈70% of TPOs). Distinct from Volume Profile — it weights by time, not volume — and is left-pinned so both can show together. Off by default; toggle from the settings sheet or directly:
chart.setMarketProfileVisible(true)
chart.setMarketProfileConfig({
buckets: 48,
widthRatio: 0.18,
opacity: 0.32,
valueAreaPct: 0.7, // fraction of TPOs in the value area
highlightPoC: true, // dashed line at the point of control
})
// split into one mini-profile per calendar-day session
chart.setMarketProfileConfig({ splitBySession: true })
// classic TPO letters per session (when zoomed in enough to be legible)
chart.setMarketProfileConfig({ splitBySession: true, letters: true })
// pure computation is exported too
import { computeMarketProfile, computeSessionProfiles } from '@tradecanvas/core'
const profile = computeMarketProfile(bars, priceMin, priceMax, { buckets: 48 })
const sessions = computeSessionProfiles(bars, priceMin, priceMax) // per-day TPO Touch & mobile
| Gesture | Action |
|---|---|
| 1-finger drag (chart area) | Pan + move crosshair |
| 2-finger pinch | Zoom around the midpoint |
| Long-press (~500 ms) | Pin OHLC tooltip at the bar (mobile equivalent of Alt-click) |
| 1-finger drag inside price / time axis strip | Scale the corresponding axis |
Modals (settings, hotkey sheet, command palette, symbol search) automatically switch to a bottom-sheet pattern with a grab handle and safe-area-aware padding under 640 px viewports.
Measure tool
Hold Shift and drag on the chart to measure bars × price between two points — the overlay shows price Δ (absolute + %), bar count, and time span. The overlay clears as soon as the mouse is released; it does not persist into saved state.
Events
All events are typed via ChartEventMap:
chart.on('orderPlace', e => /* OrderPlacePayload */)
chart.on('orderModify', e => /* OrderModifyPayload */)
chart.on('signalMarkerAdd', e => /* { marker } */)
chart.on('tradeZoneAdd', e => /* { zone } */)
chart.on('dataUpdate', e => /* { length } */) ChartWidget
Wraps Chart in a complete UI. Same instance is available via widget.chart.
import { ChartWidget } from '@tradecanvas/chart/widget'
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
timeframe: '5m',
theme: 'dark',
adapter: new BinanceAdapter(),
historyLimit: 500,
trading: true,
features: { drawings: true, indicators: true },
onReady: (chart) => { /* ... */ },
})
widget.chart.setData(...)
widget.destroy() Widget keyboard shortcuts
| Shortcut | Action |
|---|---|
| Ctrl / ⌘ + K | Command palette (indicators, chart types, drawings…) |
| Ctrl / ⌘ + P | Symbol search — fuzzy picker over the configured symbol list |
| ? | Show the keyboard shortcuts sheet |
| Alt + click chart | Pin OHLC tooltip at the hovered bar (delta to live crosshair shown) |
| Esc | Unpin tooltip / cancel drawing |
| Click symbol in toolbar | Opens the symbol search modal |
| Click play in toolbar | Opens the bar replay scrubber (play/step/seek/speed) |
Update the searchable catalog at runtime with widget.setSymbols(['BTCUSDT', 'ETHUSDT', …]).
Data Window
A floating readout of the exact O/H/L/C/V, bar change, and every active indicator's value at the hovered bar — updates live as you move the crosshair. Toggle it from the command palette (Ctrl/⌘ K → "Toggle Data Window").
Shareable view (deep links)
Encode the whole view — symbol, timeframe, chart type, price scale,
indicators (with params), and drawings — into a compact, URL-safe string for
deep-linking. With shareUrl: true the widget restores a #tcw=… hash on load and the "Share View" command palette action
copies a link to the clipboard.
const widget = new ChartWidget(host, { shareUrl: true })
const token = widget.exportState() // portable string
await widget.importState(token) // restore a view
await widget.copyShareLink() // copy "<url>#tcw=<token>" Saved layouts
Persist per-symbol indicator stacks, drawings, alerts, and chart type
to localStorage automatically:
new ChartWidget(host, {
symbol: 'BTCUSDT',
symbols: ['BTCUSDT', 'ETHUSDT', 'SOLUSDT'],
adapter: new BinanceAdapter(),
persistLayouts: true, // or { keyPrefix: 'myapp:', debounceMs: 2000 }
})
// Reset a single symbol's layout
widget.clearSavedLayout('BTCUSDT') Layouts flush on symbol switch and on widget destroy so nothing is lost when the user navigates away.
Drag-and-drop data import
Drop a CSV or JSON file onto the chart to load it instantly. Enabled by
default — disable with dragDropImport: false. The parser
handles common column layouts (time, open, high, low, close, volume),
ISO 8601 timestamps, and unix seconds/ms.
// Programmatic use
import { parseOHLCV } from '@tradecanvas/chart'
const { data, rowCount, skipped } = parseOHLCV(csvText)
chart.setData(data) Timeframe resampling
Feed the widget your finest-resolution series with widget.setData() and the toolbar timeframe buttons aggregate it on the client — one dataset
drives every resolution, no refetch. Active whenever no live adapter is
attached; opt out with resampleTimeframes: false. Weekly buckets
anchor to Monday by default (weekStartsOn: 0 for Sunday).
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
timeframe: '1h',
timeframes: ['5m', '15m', '1h', '4h', '1d', '1w'],
})
widget.setData(oneMinuteBars) // base series; clicking 4h/1d/1w resamples it
// Or use the pure function directly
import { resampleOHLCV, inferTimeframeMs } from '@tradecanvas/chart'
const hourly = resampleOHLCV(oneMinuteBars, '1h') // OHLC merged, volume summed
const fourHour = resampleOHLCV(oneMinuteBars, '4h', { weekStartsOn: 1 }) Calendar-aware bucketing: intraday and daily frames anchor to UTC epoch boundaries, weeks to the configured week start, and months / quarters / years to calendar boundaries. Input bars are never mutated.
Watchlist sidebar
Opt-in right-side panel showing all configured symbols with last price, % change, and a mini sparkline:
new ChartWidget(host, {
symbol: 'BTCUSDT',
symbols: ['BTCUSDT', 'ETHUSDT', 'SOLUSDT'],
adapter: new BinanceAdapter(),
watchlist: true,
})
// Feed non-active rows from your own data source
widget.setWatchlistEntry('ETHUSDT', {
lastPrice: 3245.12,
refPrice: 3180.50,
sparkline: [3180, 3195, 3210, ...],
}) Drawing favorites
Pin frequently-used drawing tools to a strip at the top of the sidebar.
Right-click any tool (in a group flyout or the strip itself) to pin or unpin
it; the set persists to localStorage. Seed the initial pins with drawingFavorites:
new ChartWidget(host, {
drawingFavorites: ['trendLine', 'horizontalLine', 'fibRetracement', 'rectangle'],
}) Drawing style & templates
The palette button on the drawing sidebar opens a style popover — pick colour, line width, and line style for the next drawing (and the selected one), and save named templates persisted to localStorage for one-click reuse. Programmatic equivalents:
chart.setDrawingStyle({ color: '#f23645', lineWidth: 2, lineStyle: 'dashed' })
chart.getDrawingStyle()
chart.setSelectedDrawingStyle({ color: '#089981' }) // restyle the selected drawing Object tree
The toolbar layers button opens an object-tree panel listing every active
indicator and drawing. Indicators can be removed; drawings get per-item
show / hide, lock / unlock, and delete. Enabled by default — disable with objectTree: false. The drawing controls map to:
chart.getDrawings() // DrawingState[] (id, type, visible, locked)
chart.setDrawingVisible(id, false) // hide a single drawing
chart.setDrawingLocked(id, true) // lock it from edits
chart.removeDrawing(id)
chart.getActiveIndicators() // active indicator instances
chart.updateIndicator(instanceId, { period: 50 }) // re-tune params live
chart.removeIndicator(instanceId) The gear button on each indicator row opens a settings dialog that introspects the indicator's parameters (numbers, toggles, colors) and
applies edits live via updateIndicator — no need to remove and
re-add to change a period or colour.
The object tree's Compare section overlays other symbols as
normalized lines. With a live adapter, the + button opens the symbol picker,
fetches that symbol's history via adapter.fetchHistory, and adds
it in percent mode (so mixed-price symbols share one axis). Comparisons
refetch automatically on timeframe change. Programmatic equivalents:
widget.addCompareSymbol('ETHUSDT') // fetches + overlays (needs an adapter)
// or drive the chart directly with your own data
chart.addCompareSymbol('ETHUSDT', 'ETH', ethBars, '#627eea')
chart.setCompareMode('percent') // 'percent' | 'absolute'
chart.removeCompareSymbol('ETHUSDT') Price alerts
The toolbar bell opens a floating panel to add, list, and delete price
alerts; a toast fires when one triggers. Alert lines are also draggable — grab one on the chart and slide it to re-price
(moving an alert re-arms it). Enabled by default — disable with alerts: false. Drive it programmatically via the Chart API and the typed alert events:
// Add from code (condition: 'crossing' | 'crossingUp' | 'crossingDown'
// | 'greaterThan' | 'lessThan')
const id = chart.addAlert(64200, 'crossingUp', 'breakout')
chart.removeAlert(id)
chart.getAlerts() // PriceAlert[]
chart.saveAlerts('tcw:alerts:BTCUSDT') // localStorage persistence
chart.loadAlerts('tcw:alerts:BTCUSDT')
// React to triggers
chart.on('alertTriggered', (e) => {
console.log('hit', e.payload.price, e.payload.message)
})
// also: 'alertAdd' / 'alertRemove' / 'alertUpdate' (fired on drag)
// Indicator alerts: bind to an indicator line via channel '<instanceId>:<key>'.
// In the widget, the alerts panel's source dropdown lists every active line.
const ema = chart.addIndicator('rsi')
chart.addAlert(70, 'crossingUp', 'RSI overbought', `${ema}:rsi`, 'RSI') Opt into a sound and/or desktop notification when an alert fires (both off by
default). sound: true plays a built-in beep; pass a URL for a
custom one. desktop: true uses the Notification API and asks
permission on first use.
new ChartWidget(host, {
alertNotifications: { sound: true, desktop: true },
}) ChartGrid
Synchronized multi-chart layouts.
import { ChartGrid } from '@tradecanvas/chart'
const grid = new ChartGrid(host, { layout: '2x2', theme: 'dark' })
await grid.connectAll(new BinanceAdapter(), ['BTCUSDT','ETHUSDT','SOLUSDT','BNBUSDT'], '5m')
grid.setLayout('1x2') Layouts: '1x2', '2x2', '2x3', '3x3'.