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

MethodPurpose
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

MethodPurpose
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

MethodPurpose
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:

GestureEffect
Drag price axis up / downCompress / expand the vertical price range (disables auto-scale).
Drag time axis left / rightZoom in / out on the time axis.
Double-click price axisRe-enable auto-scale.
Double-click time axisFit 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

GestureAction
1-finger drag (chart area)Pan + move crosshair
2-finger pinchZoom 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 stripScale 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

ShortcutAction
Ctrl / + KCommand palette (indicators, chart types, drawings…)
Ctrl / + PSymbol search — fuzzy picker over the configured symbol list
?Show the keyboard shortcuts sheet
Alt + click chartPin OHLC tooltip at the hovered bar (delta to live crosshair shown)
EscUnpin tooltip / cancel drawing
Click symbol in toolbarOpens the symbol search modal
Click play in toolbarOpens 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'.