JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 17
  • Score
    100M100P100Q90419F
  • License MIT

High-performance canvas trading chart with built-in indicators, drawing tools, and real-time streaming. Zero external dependencies.

Package Exports

  • @tradecanvas/chart

Readme

@tradecanvas/chart

High-performance canvas trading chart with built-in indicators, drawing tools, and real-time streaming. Zero external dependencies.

Live Demo | GitHub | npm

Why TradeCanvas?

Most chart libraries make you choose: pretty charts with no trading features, or trading features with an ugly API. TradeCanvas gives you both.

  • 33+ built-in indicators — MA, EMA, Hull MA, RSI, MACD, Bollinger, Ichimoku, Pivot Points, Anchored VWAP, ZigZag, Linear Regression Channel, Awesome / Chaikin Oscillator, and more. No separate calculation library needed.
  • 10+ drawing tools — Trendlines, Fibonacci retracement, horizontal/vertical lines, rectangles, channels, Elliott waves, Gann fans. With undo/redo.
  • Trading overlay — Render open positions with entry line, P&L zone, and SL/TP markers. Orders as dashed lines. Users can drag SL/TP to modify.
  • Real-time streaming — Built-in Binance adapter. Plug in your own data source with the adapter interface.
  • Save/load chart state — Persist drawings, indicators, theme, and chart type to JSON. Restore with one call.
  • Replay mode — Step through historical data bar-by-bar for backtesting visualization.
  • Zero dependencies — The entire library is self-contained. No d3, no chart.js, no fancy-canvas.

Install

npm install @tradecanvas/chart
# or
pnpm add @tradecanvas/chart
# or
yarn add @tradecanvas/chart

Quick Start

import { Chart, BinanceAdapter } from '@tradecanvas/chart'

// Create a chart
const chart = new Chart(document.getElementById('chart')!, {
  theme: 'dark',
  autoScale: true,
  features: {
    drawings: true,
    indicators: true,
    trading: true,
    volume: true,
  },
})

// Connect to live Binance data
const adapter = new BinanceAdapter()
chart.connect({
  adapter,
  symbol: 'BTCUSDT',
  timeframe: '5m',
  historyLimit: 300,
})

That's it. A full-featured trading chart with live data in 15 lines.

Widget (Complete UI)

For a complete TradingView-like experience with built-in toolbar, drawing tools, and settings — no UI code needed:

import { ChartWidget } from '@tradecanvas/chart/widget'
import { BinanceAdapter } from '@tradecanvas/chart'

const widget = new ChartWidget(document.getElementById('chart')!, {
  symbol: 'BTCUSDT',
  timeframe: '5m',
  adapter: new BinanceAdapter(),
  theme: 'dark',
})

That's it. Full toolbar, drawing sidebar, settings modal, and status bar — all included.

Widget Options

Option Type Default Description
symbol string 'BTCUSDT' Initial trading symbol
timeframe TimeFrame '5m' Initial timeframe
theme 'dark' | 'light' | Theme 'dark' Chart theme
adapter DataAdapter Data source adapter
toolbar boolean true Show top toolbar
drawingTools boolean true Show left drawing sidebar
settings boolean true Show settings button
trading boolean true Enable trading overlay
statusBar boolean true Show bottom status bar
symbols string[] BTC/ETH/SOL/BNB Available symbols
timeframes TimeFrame[] 1m to 1d Available timeframes
chartTypes ChartType[] 7 types Available chart types
onSymbolChange (symbol) => void Symbol change callback
onTimeframeChange (tf) => void Timeframe change callback
onReady (chart) => void Fired when chart is ready

Widget vs Headless

Chart (headless) ChartWidget
Import @tradecanvas/chart @tradecanvas/chart/widget
UI included None — build your own Complete toolbar, sidebar, settings
Bundle impact ~50 KB gzip ~65 KB gzip (includes UI)
Framework Any (React, Vue, Svelte, vanilla) Vanilla JS DOM (works everywhere)
Customization Full control Toggle sections on/off
Advanced access Direct API widget.getChart() for direct API

Features

Chart Types

Type Description
Candlestick Standard OHLC candles
Hollow Candle Open/close determines fill
Bar (OHLC) Classic open-high-low-close bars
Line Close price line
Area Filled area below close
Baseline Two-tone area split at a reference price
Heikin-Ashi Smoothed candles for trend identification
Renko Fixed-size bricks that ignore time
Kagi Reversal-based line chart
Point & Figure X/O columns for supply/demand analysis
Line Break Three-line break charts
Range Bars Fixed price-range bars — each bar's high − low equals a configured range

Finance Charts

Chart Description
SparklineChart Tiny inline line/area chart from a number array — for dashboards and KPI cards
DepthChart Bid/ask order book visualization with cumulative volume areas
EquityCurveChart Portfolio equity line with drawdown shading and benchmark comparison
HeatmapChart Colored cell grid with treemap layout — for sector/market performance
WaterfallChart Running cumulative bars — P&L attribution, revenue bridge, cash flow
GaugeChart Speedometer-style gauge — KPIs, risk scores, Fear & Greed index
import {
  SparklineChart, DepthChart, EquityCurveChart, HeatmapChart,
  WaterfallChart, GaugeChart,
} from '@tradecanvas/chart'

// Sparkline in a 120x48 container
new SparklineChart(el, { data: [100, 102, 98, 105, 103], mode: 'area', color: '#26A69A' })

// Equity curve with drawdown
new EquityCurveChart(el, { data: equityPoints, drawdown: true, benchmark: spyData })

// Order book depth
new DepthChart(el, { data: { bids, asks }, crosshair: true })

// Market heatmap (treemap weighted by market cap)
new HeatmapChart(el, { data: cells, weighted: true })

// P&L waterfall
new WaterfallChart(el, {
  data: [
    { label: 'Start', value: 10000, type: 'total' },
    { label: 'Gain', value: 1850 },
    { label: 'Loss', value: -620 },
    { label: 'End', value: 11230, type: 'total' },
  ],
})

// Fear & Greed gauge
const gauge = new GaugeChart(el, {
  value: 72,
  zones: [
    { from: 0, to: 25, color: '#ef4444' },
    { from: 75, to: 100, color: '#10b981' },
  ],
})
gauge.setValue(85) // animates smoothly

Indicators (built-in)

Overlay (drawn on the price chart): SMA, EMA, Hull MA, Bollinger Bands, Keltner Channel, Donchian Channel, Ichimoku Cloud, Parabolic SAR, Supertrend, VWAP, Anchored VWAP, Pivot Points (Classic), ZigZag, Linear Regression Channel

Panel (separate sub-chart): RSI, MACD, Stochastic, ATR, ADX, CCI, CMF, MFI, OBV, ROC, TSI, Williams %R, Awesome Oscillator, Chaikin Oscillator, Volume Profile, VROC, Standard Deviation, Accumulation/Distribution, Aroon

All indicator parameters are validated at runtime — invalid values (NaN, Infinity, non-numeric strings, missing keys) fall back to documented defaults instead of silently propagating to calculations.

Drawing Tools

Trendline, Horizontal Line, Vertical Line, Ray, Extended Line, Parallel Channel, Fibonacci Retracement, Fibonacci Extension, Rectangle, Ellipse, Triangle, Arrow, Pitchfork, Gann Fan, Gann Box, Elliott Wave, Regression Channel, Date Range, Price Range, Measure, Anchored VWAP, Volume Profile Range, Text Annotation

All drawing tools support:

  • Click-to-place with magnet snapping to OHLC values
  • Undo / redo (Ctrl+Z / Ctrl+Y)
  • Serialization for save/load
  • Custom styles (color, width, dash pattern)

Trading Overlay

Render open positions and pending orders directly on the chart, like MT4/MT5.

import type { TradingPosition, TradingOrder } from '@tradecanvas/chart'

chart.setPositions([{
  id: 'pos-1',
  side: 'buy',
  entryPrice: 3500,
  quantity: 1.5,
  closedQuantity: 0.5,   // partial close — visualized as a left-edge dim band
  stopLoss: 3400,
  takeProfit: 3700,
}])

chart.setOrders([{
  id: 'order-1',
  side: 'sell',
  type: 'limit',
  price: 3800,
  quantity: 0.5,
  label: 'TP',
  draggable: true,
}])

// Customize the position zone color via P&L thresholds
chart.setTradingConfig({
  pnlThresholds: [
    { pnl: -Infinity, color: '#b91c1c' },
    { pnl: 0,         color: '#94a3b8' },
    { pnl: 50,        color: '#16a34a' },
    { pnl: 200,       color: '#15803d' },
  ],
  // Custom label template — tokens: {side} {qty} {openQty} {closedQty} {entry} {price} {pnl} {pnlPct} {pnlSign}
  positionLabel: '{side} {openQty}/{qty} @ {entry} | {pnlSign}{pnl} ({pnlPct})',
})

// Listen for user drag-to-modify
chart.on('positionModify', (e) => console.log('SL/TP moved:', e.payload))
chart.on('orderModify', (e) => console.log('Order moved:', e.payload))

Real-Time Streaming

// Built-in Binance adapter (free, no API key)
chart.connect({
  adapter: new BinanceAdapter(),
  symbol: 'ETHUSDT',
  timeframe: '1m',
  historyLimit: 500,
})

// Or manual data feed
chart.setData(historicalBars)
chart.appendBar(newBar)
chart.updateLastBar(updatedBar)
chart.setCurrentPrice(3500.42)

Web Worker indicator pipeline

Heavy charts (1,000+ bars × 10+ indicators) can stutter when calculate() runs on the main thread. IndicatorWorkerHost offloads calculation to a worker so the render loop stays smooth.

import { IndicatorWorkerHost } from '@tradecanvas/core'

// Bundler-supported worker URL (Vite, webpack 5, esbuild, etc.)
const worker = new Worker(
  new URL('@tradecanvas/core/dist/indicator.worker.js', import.meta.url),
  { type: 'module' },
)
const host = new IndicatorWorkerHost(worker, { timeoutMs: 30_000 })

const output = await host.calculate(
  'rsi',
  { id: 'rsi', instanceId: 'rsi-1', params: { period: 14 } },
  bars,
)

// Health check / cleanup
await host.ping()
host.terminate()

No worker available (SSR, tests, or as a safety net)? Pass null and register fallback plugins for synchronous calculation:

import { IndicatorWorkerHost, RSIIndicator } from '@tradecanvas/core'

const host = new IndicatorWorkerHost(null)
host.registerFallbackPlugin(new RSIIndicator())
const output = await host.calculate('rsi', config, bars)  // runs synchronously

Render still happens on the main thread (it needs CanvasRenderingContext2D). Only the heavy compute moves off-thread.

Save / Load

const json = chart.saveState()
localStorage.setItem('my-chart', json!)

chart.loadState(localStorage.getItem('my-chart')!)

// Download / upload files
chart.downloadState('my-chart.json')
await chart.loadStateFromFile()

Themes

import { DARK_THEME, LIGHT_THEME, DARK_TERMINAL } from '@tradecanvas/chart'

// Built-in presets: DARK_THEME, LIGHT_THEME, DARK_TERMINAL
chart.setTheme(DARK_TERMINAL)  // fintech terminal: #0E0E0E bg, #00FF87/#FF3B4D candles, monospace

// Or customize any preset
chart.setTheme({
  ...DARK_THEME,
  candleUp: '#26A69A',
  candleDown: '#EF5350',
  background: '#0a0a0f',
})

Events

chart.on('crosshairMove', (e) => { /* { point, bar, barIndex, indicatorValues } */ })
chart.on('barClick', (e) => { /* { bar, barIndex, point } */ })
chart.on('visibleRangeChange', (e) => { /* { from, to } */ })
chart.on('drawingCreate', (e) => { /* ... */ })
chart.on('orderModify', (e) => { /* ... */ })
chart.on('positionModify', (e) => { /* ... */ })

Replay Mode

chart.replayStart({ data: historicalBars, speed: 2, startIndex: 100 })
chart.replayPause()
chart.replayResume()
chart.replayStop()
const { current, total, percent } = chart.getReplayProgress()

Comparison

Feature @tradecanvas/chart lightweight-charts chart.js Highcharts Stock
Chart types 12 + 6 finance 4 8 (non-financial) 10+
Finance charts Sparkline, Depth, Equity, Heatmap, Waterfall, Gauge None None Some
Built-in indicators 33+ 0 0 ~30
Drawing tools 23 0 0 Some
Trading overlay Full (pos + orders + drag) None None None
Real-time streaming Built-in (Binance) Manual Manual Built-in
Save/load state Yes No No Yes
Replay mode Yes No No No
Multi-panel Yes No No Yes
Bundle (gzip) ~50 KB ~45 KB ~70 KB ~200 KB
Dependencies 0 1 0 0
Widget (complete UI) Yes (ChartWidget) No No No
License MIT Apache 2.0 MIT Commercial

API Overview

new Chart(container, options)

const chart = new Chart(element, {
  chartType: 'candlestick',
  theme: DARK_THEME,
  autoScale: true,
  rightMargin: 5,
  numberLocale: 'en-US',  // or 'de-DE', 'vi-VN', etc. — BCP 47 locale
  crosshair: { mode: 'magnet' },
  features: { drawings: true, indicators: true, trading: true, volume: true },
})

// Change locale at runtime
chart.setNumberLocale('de-DE')  // 65.234,00

Key Methods

Method Description
setData(bars) Load historical OHLCV data
appendBar(bar) Append a new candle
appendBars(bars) Bulk append (reconnect catch-up)
updateLastBar(bar) Update the in-progress candle
setCurrentPrice(price, pulseColor?) Show a live price line
connect(config) Connect to a real-time data source
setTimeframe(tf) Switch timeframe on active stream
setChartType(type) Switch chart type
setTheme(theme) Apply a theme (DARK_THEME, LIGHT_THEME, DARK_TERMINAL)
setNumberLocale(locale) Set number format locale (en-US, de-DE, vi-VN)
setStatusText(text) Show status in legend area ("LIVE · 8ms")
addIndicator(id, params?) Add a technical indicator
removeIndicator(instanceId) Remove an indicator
setDrawingTool(tool) Activate a drawing tool
setPositions(positions) Render trading positions
setOrders(orders) Render pending orders
saveState(key?) Serialize chart state
loadState(json) Restore chart state
screenshot() Download chart as image
on(event, handler) Subscribe to events
destroy() Clean up all resources

Data Format

interface OHLCBar {
  time: number    // Unix timestamp (seconds)
  open: number
  high: number
  low: number
  close: number
  volume: number
}

Examples

Example Description
Live Demo Full-featured demo with live Binance data
examples/basic Vanilla JS + live Binance streaming
examples/vanilla-static Vanilla JS + static data (offline)
examples/react React 19 integration
examples/svelte Svelte 5 integration
examples/vue Vue 3 integration

Browser Support

Chrome 80+, Firefox 80+, Safari 14+, Edge 80+

Framework Integration

TradeCanvas is framework-agnostic. The Chart class takes a DOM element and manages its own canvas layers.

React:

import { useEffect, useRef } from 'react'
import { Chart, BinanceAdapter } from '@tradecanvas/chart'

function TradingChart() {
  const ref = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const chart = new Chart(ref.current!, {
      theme: 'dark',
      features: { indicators: true, drawings: true },
    })
    chart.connect({
      adapter: new BinanceAdapter(),
      symbol: 'BTCUSDT',
      timeframe: '5m',
    })
    return () => chart.destroy()
  }, [])

  return <div ref={ref} style={{ width: '100%', height: 500 }} />
}

Svelte:

<script lang="ts">
  import { onMount, onDestroy } from 'svelte'
  import { Chart, BinanceAdapter, DARK_THEME } from '@tradecanvas/chart'
  import type { TimeFrame } from '@tradecanvas/chart'

  interface Props { symbol?: string; timeframe?: TimeFrame }
  let { symbol = 'BTCUSDT', timeframe = '5m' }: Props = $props()

  let container: HTMLDivElement
  let chart: Chart | null = null

  onMount(() => {
    chart = new Chart(container, {
      chartType: 'candlestick',
      theme: DARK_THEME,
      autoScale: true,
      features: { indicators: true, drawings: true, volume: true },
    })
    chart.connect({ adapter: new BinanceAdapter(), symbol, timeframe })
  })

  onDestroy(() => chart?.destroy())

  $effect(() => {
    if (!chart) return
    chart.disconnectStream()
    chart.connect({ adapter: new BinanceAdapter(), symbol, timeframe })
  })
</script>

<div bind:this={container} style="width: 100%; height: 600px" />

Vue:

<template>
  <div ref="chartContainer" style="width: 100%; height: 600px" />
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { Chart, BinanceAdapter, DARK_THEME } from '@tradecanvas/chart'

const chartContainer = ref<HTMLDivElement>()
let chart: Chart | null = null

onMounted(() => {
  if (!chartContainer.value) return
  chart = new Chart(chartContainer.value, {
    chartType: 'candlestick',
    theme: DARK_THEME,
    autoScale: true,
    features: { indicators: true, drawings: true, volume: true },
  })
  chart.connect({ adapter: new BinanceAdapter(), symbol: 'BTCUSDT', timeframe: '5m' })
})

onUnmounted(() => chart?.destroy())
</script>

Architecture

Multi-layer canvas for optimal rendering — only dirty layers repaint each frame:

  UI Layer      (price axis, legend, live price)     z=3
  Overlay Layer (drawings, trading positions/orders) z=2
  Main Layer    (candles, indicators, volume)         z=1
  Background    (grid, watermark)                     z=0

License

MIT