Appearance

Personalize your experience

Mode
Accent Color
Layout
Direction
Charts API/Documentation
@quatick/quatick-charts v2.0.0

Charts API Reference

@quatick/quatick-charts · Version 2.0.0 · WebGL-accelerated

Complete reference for the Quatick Charts SDK. Professional-grade GPU-accelerated charting engine designed for institutional trading platforms. Render 10M+ data points at 60fps with sub-10ms WebSocket-to-pixel latency. TypeScript-native, plugin-first architecture built on WebGL 2.0 and Web Workers.

Getting Started

Installation

Install via npm, yarn, or pnpm. The package ships as an ES module with full TypeScript declarations.

bash
npm install @quatick/quatick-charts
# or
pnpm add @quatick/quatick-charts
# or
yarn add @quatick/quatick-charts
Peer dependencies: React ≥ 18, TypeScript ≥ 5.0. WebGL 2.0 is required for GPU rendering. The library gracefully falls back to Canvas 2D on unsupported hardware.

Chart Constructor

Import Chart or use the convenience factory createChart(). Both accept the same configuration object.

PropTypeDefaultDescription
container*string | HTMLElementCSS selector or DOM element to mount the chart canvas into.
chartTypeRendererType"candlestick"Initial chart type. Can be changed at runtime via chart.setChartType().
theme"dark" | "light""dark"Built-in theme preset. Custom themes via ThemeAPI.
renderer"webgl" | "canvas""webgl"Rendering backend. WebGL 2.0 for maximum performance, Canvas 2D as fallback.
showVolumebooleantrueRender volume histogram bars in the lower section of the main pane.
debugbooleanfalseEnable debug overlay showing FPS, render times, and WebGL diagnostics.
xAxisIXAxisConfig{}Time-axis: visible, height, fontSize, timezone, candleInterval (seconds).
yAxisIYAxisConfig{}Price-axis: visible, position ('right'|'left'), fontSize, precision, logScale.
gridIGridConfig{}Grid lines: visible, horizontalVisible, verticalVisible, horizontalColor, verticalColor.
crosshairICrosshairConfig{}Crosshair: visible, mode ('free'|'magnetic'), lineColor, label styles.
interactionsIInteractionConfig{}enableZoom, enablePan, enableCrosshair, minBarsVisible, maxBarsVisible.
showResetButtonbooleantrueShow built-in 'Reset View' button on the canvas.
performanceHint"default"|"performance"|"low""default"Rendering optimization level. Use 'performance' on low-end devices.
typescript
import { Chart, createChart } from '@quatick/quatick-charts';

// Factory function (recommended)
const chart = createChart({
  container: document.getElementById('my-chart'),
  chartType: 'candlestick',
  theme: 'dark',
  renderer: 'webgl',
  showVolume: true,
  xAxis: {
    visible: true,
    height: 26,
    timezone: 'Asia/Kolkata',
    candleInterval: 60, // 1-minute candles in seconds
  },
  yAxis: {
    visible: true,
    position: 'right',
    precision: 2,
  },
  grid: { visible: true, horizontalVisible: true, verticalVisible: true },
  crosshair: { visible: true, mode: 'magnetic' },
  interactions: {
    enableZoom: true,
    enablePan: true,
    enableCrosshair: true,
    minBarsVisible: 5,
    maxBarsVisible: 5000,
  },
});

// Or use the class directly
const chart2 = new Chart({
  container: '#chart-container',
  theme: 'light',
});

setData() — Loading Candle Data

Pass an array of Candle objects to the chart. The data must be sorted chronologically by time (Unix timestamp in seconds).

PropTypeDefaultDescription
time*numberUnix timestamp in seconds (number of seconds since epoch).
open*numberOpening price of the bar.
high*numberHighest price of the bar.
low*numberLowest price of the bar.
close*numberClosing price.
volume*numberVolume traded during the bar period.
typescript
import { Chart } from '@quatick/quatick-charts';

const chart = createChart({ container: '#chart', theme: 'dark' });

// Candle data: sorted array of { time, open, high, low, close, volume }
const candleData = [
  { time: 1710000000, open: 43200, high: 43850, low: 43100, close: 43750, volume: 1250 },
  { time: 1710003600, open: 43750, high: 44200, low: 43600, close: 44100, volume: 1800 },
  // ... more candles
];

// Load data (replaces existing)
chart.setData(candleData);

// Render the chart
chart.render();

// Update a single bar in real-time (append or update last bar)
chart.updateCandle({
  time: Date.now() / 1000,
  open: 44100,
  high: 44300,
  low: 44050,
  close: 44250,
  volume: 320,
});

setChartType() — Switching Chart Types

Change chart type at runtime without losing data or indicators:

typescript
// Switch chart type at runtime
chart.setChartType('renko');   // Renko bricks (ATR auto-sizing)
chart.setChartType('kagi');    // Kagi lines
chart.setChartType('candlestick');  // Back to candlestick

// Supported types:
type RendererType =
  | 'candlestick'
  | 'hollow-candle'
  | 'volume-candle'
  | 'line'
  | 'step-line'
  | 'baseline'
  | 'heikin-ashi'
  | 'area'
  | 'renko'
  | 'line-break'
  | 'point-figure'
  | 'kagi';

// Programmatic view control
chart.resetView();               // Reset zoom to fit all data
chart.fitContent();              // Fit data to viewport

// Get current visible time range
const range = chart.getVisibleRange();
// Returns: { from: number, to: number, barCount: number }

// Set visible range explicitly
chart.setVisibleRange({ from: 1710000000, to: 1710086400 });

Plugin Architecture

The chart engine is built entirely around composable plugins: renderers, indicators, overlays, interactions. The PluginRegistry manages the plugin lifecycle.

Renderers

  • Candlestick
  • Hollow Candle
  • Renko
  • Kagi
  • Line Break
  • P&F
  • Volume Candle
  • Area
  • Heikin-Ashi

Indicators

  • MA/EMA/SMA/WMA
  • Bollinger Bands
  • RSI
  • MACD
  • Stochastic
  • ATR
  • ADX
  • VWAP
  • Ichimoku
  • Supertrend

Overlays & Tools

  • Crosshair + Tooltip
  • Grid
  • Trendlines
  • Fibonacci
  • Annotations
  • Volume Profile
  • Order Book
typescript
import { PluginRegistry, PluginState } from '@quatick/quatick-charts';

// Access the plugin registry
const registry = chart.getPluginRegistry();

// Check if a plugin is loaded
const state = registry.getPluginState('rsi-14');
// PluginState: UNLOADED | LOADING | INITIALIZED | ACTIVE | ERROR | DESTROYED

// List all registered plugins
const plugins = registry.getAllPlugins();
plugins.forEach(p => {
  console.log(p.id, p.category, p.state);
});

Chart Types & Renderers

All 12 chart types are first-class renderer plugins. Switch between them at runtime with chart.setChartType(). Each renderer performs intelligent data transformation on the underlying OHLCV stream.

Candlestick

Traditional OHLCV representation. Bodies represent open–close range; wicks extend to high and low. WebGL-optimised for 60fps at any zoom depth.

PropTypeDefaultDescription
upColorstring"#34d399"Fill color for bullish candles (close ≥ open).
downColorstring"#f87171"Fill color for bearish candles (close < open).
wickUpColorstringautoWick color for bullish bars. Inherits upColor if unset.
wickDownColorstringautoWick color for bearish bars. Inherits downColor if unset.
borderWidthnumber1Candle body border stroke width in pixels.
wickWidthnumber1Wick stroke width in pixels.
typescript
chart.setChartType('candlestick');

// Color overrides via ThemeAPI
chart.api.candlestick.setColors({
  upColor: '#34d399',
  downColor: '#f87171',
  wickUpColor: '#34d399',
  wickDownColor: '#f87171',
});

Hollow Candle

Bullish continuation bars are hollow (outline only), bearish bars are filled. Strong trend signals at a glance with zero additional indicator overhead.

typescript
chart.setChartType('hollow-candle');

// Hollow candle uses the same color API
chart.api.candlestick.setColors({
  upColor: '#34d399',
  downColor: '#f87171',
});

Volume Candle

Candle body width is proportional to normalised volume across visible bars. Instantly identifies high-conviction moves.

PropTypeDefaultDescription
maxBodyWidthnumber0Maximum body width in pixels. 0 = auto (fills bar spacing).
minBodyWidthnumber2Minimum body width in pixels to keep thin candles visible.
typescript
chart.setChartType('volume-candle');

Line & Step Line

Close-price line chart. step-line uses horizontal segments between bars (no interpolation) — ideal for discrete data series.

PropTypeDefaultDescription
lineColorstring"#8b5cf6"Line stroke color.
lineWidthnumber2Stroke width in pixels.
field"open"|"high"|"low"|"close""close"OHLCV field to plot.
smoothingnumber0Bezier smoothing factor (0 = sharp, 1 = smooth).
typescript
chart.setChartType('line');         // Smooth close-price line
chart.setChartType('step-line');   // Stepped (no interpolation)

chart.api.theme.setLineOptions({
  lineColor: '#8b5cf6',
  lineWidth: 2,
  field: 'close',
});

Area Chart

Gradient-filled area below the close line. Effective for overlaying moving averages on cumulative data.

PropTypeDefaultDescription
lineColorstring"#8b5cf6"Top line color.
areaTopColorstring"rgba(139,92,246,0.3)"Fill color at the top of the area.
areaBottomColorstring"rgba(139,92,246,0)"Fill color at the bottom (fades to transparent).
lineWidthnumber2Stroke width of the top line.
typescript
chart.setChartType('area');

Baseline

Values above a reference price render green, below render red. Perfect for relative-performance or mean-reversion strategy overlays.

PropTypeDefaultDescription
baseline*numberReference price level (horizontal baseline).
topFillColorstring"rgba(52,211,153,0.15)"Fill above baseline.
bottomFillColorstring"rgba(248,113,113,0.15)"Fill below baseline.
topLineColorstring"#34d399"Line color above baseline.
bottomLineColorstring"#f87171"Line color below baseline.
typescript
chart.setChartType('baseline');

// Set baseline dynamically
chart.api.theme.setBaselineOptions({ baseline: 44000 });

Heikin-Ashi

Modified OHLC calculation that smooths price noise. The engine transforms raw OHLCV data internally. All candlestick color options apply.

HA formulas: HA_close = (O+H+L+C)/4 · HA_open = prev(HA_open+HA_close)/2 · HA_high = max(H, HA_open, HA_close) · HA_low = min(L, HA_open, HA_close)
typescript
chart.setChartType('heikin-ashi');

Renko

Time-independent bricks. New bricks only form when price moves by a fixed amount. ATR auto-sizing is supported.

PropTypeDefaultDescription
brickSizenumber0Brick size in price units. 0 = automatic ATR-based sizing.
method"close" | "hl""close""close" tracks closing prices; "hl" allows high/low reversals.
atrPeriodnumber14ATR look-back period used when brickSize = 0.
upColorstring"#34d399"Rising brick fill color.
downColorstring"#f87171"Falling brick fill color.
typescript
chart.setChartType('renko');

// Fixed brick size
chart.api.theme.setRendererOptions({
  brickSize: 100,     // $100 per brick
  method: 'hl',       // High-Low Renko
});

// ATR auto-sizing (default when brickSize = 0)
chart.api.theme.setRendererOptions({ brickSize: 0, atrPeriod: 14 });

Line Break

N-line break reversal pattern. Classic is 3-line break — a new block only forms when price breaks the highest (or lowest) of the previous N lines.

PropTypeDefaultDescription
linesnumber3Number of prior lines that must be broken for a reversal. Classic = 3.
upColorstring"#34d399"Rising block fill color.
downColorstring"#f87171"Falling block fill color.
typescript
chart.setChartType('line-break');
chart.api.theme.setRendererOptions({ lines: 3 });

Point & Figure

Classic X (rising) and O (falling) column chart. Price-driven, ignores time. ATR auto-sizing keeps box sizes relevant across symbols and timeframes.

PropTypeDefaultDescription
boxSizenumber0Box size in price units. 0 = dynamic ATR-based.
reversalnumber3Number of boxes required for a column reversal.
xColorstring"#34d399"X (rising column) color.
oColorstring"#f87171"O (falling column) color.
typescript
chart.setChartType('point-figure');
chart.api.theme.setRendererOptions({ boxSize: 0, reversal: 3 });

Kagi

Thick Yang lines for bullish breakouts, thin Yin lines for bearish. Time-independent. Identifies support/resistance without the noise of regular candlestick charts.

PropTypeDefaultDescription
reversalPctnumber0.03Reversal as a fraction (0.03 = 3% price move triggers reversal).
reversalAbsnumber0Absolute price reversal. Takes precedence if > 0.
yangColorstring"#34d399"Yang (thick/rising) line color.
yinColorstring"#f87171"Yin (thin/falling) line color.
lineWidthnumber2Base line width. Yang is rendered 2× thicker.
typescript
chart.setChartType('kagi');
chart.api.theme.setRendererOptions({
  reversalPct: 0.03,
  yangColor: '#34d399',
  yinColor: '#f87171',
});

Indicators & Technical Analysis

The IndicatorApi controls all technical indicators. 30+ built-in indicators with Web Worker computation — indicator math never blocks the main thread. Custom indicators extend BaseIndicator.

addIndicator()

Add a technical indicator via the chart's indicator API. The engine calculates and overlays the result automatically.

PropTypeDefaultDescription
type*stringIndicator ID string: "sma", "ema", "rsi", "macd", "bbands", "vwap", etc.
paramsobject{}Indicator-specific params: { period: 20, source: 'close', multiplier: 2 }.
panenumber0Pane index. 0 = main price pane. 1+ = separate sub-pane.
colorstringautoPlot line color. Auto-assigned from theme palette if omitted.
overlaybooleantrueOverlay on main pane (true) or in separate sub-chart pane (false).
visiblebooleantrueInitial visibility toggle.
idstringautoCustom stable ID for referencing this indicator instance.
typescript
// SMA-20 overlay on main pane
const sma = chart.api.indicator.add({
  type: 'sma',
  params: { period: 20, source: 'close' },
  color: '#8b5cf6',
  pane: 0,
  overlay: true,
});

// EMA with custom ID
chart.api.indicator.add({
  type: 'ema',
  params: { period: 50 },
  color: '#f59e0b',
  id: 'ema-50',
});

// Bollinger Bands in main pane
chart.api.indicator.add({
  type: 'bbands',
  params: { period: 20, stdDev: 2 },
  pane: 0,
});

// RSI in sub-pane
chart.api.indicator.add({
  type: 'rsi',
  params: { period: 14, source: 'close' },
  pane: 1,
  overlay: false,
});

// MACD in sub-pane
chart.api.indicator.add({
  type: 'macd',
  params: { fast: 12, slow: 26, signal: 9 },
  pane: 2,
  overlay: false,
});

// Remove by ID
chart.api.indicator.remove('ema-50');

// Toggle visibility
chart.api.indicator.setVisible('rsi', false);

Built-in Indicator Reference

Trend Following

  • SMA (Simple MA)
  • EMA (Exponential MA)
  • WMA (Weighted MA)
  • DEMA
  • TEMA
  • HMA (Hull MA)
  • VWAP
  • ALMA
  • SMMA

Volatility

  • Bollinger Bands
  • Keltner Channel
  • Donchian Channel
  • ATR
  • Std Dev
  • Chaikin Volatility

Momentum

  • RSI (14)
  • Stochastic %K/%D
  • CCI
  • MFI
  • ROC
  • Williams %R
  • TSI
  • CMO
  • Momentum

Oscillators

  • MACD (12,26,9)
  • DMI/ADX
  • Aroon (Up/Down)
  • Parabolic SAR
  • Supertrend
  • Ultimate Oscillator

Volume Analysis

  • OBV (On-Balance)
  • A/D Line
  • CMF
  • VWMA
  • RVOL
  • Volume Delta
  • Cumulative Delta

Advanced / AI

  • Ichimoku Cloud
  • Pivot Points
  • ZigZag
  • Elliott Wave
  • Support/Resistance
  • Market Structure

Common Indicator Parameters

PropTypeDefaultDescription
periodnumberLook-back period for calculation (e.g., 14 for RSI-14).
source"open"|"high"|"low"|"close"|"hl2"|"hlc3"|"ohlc4""close"Price source field to feed into the calculation.
fastnumberFast period (MACD, Stochastic, ADX).
slownumberSlow period (MACD, PSAR, etc).
signalnumberSignal smoothing period (MACD).
stdDevnumberStandard deviation multiplier (Bollinger Bands, Keltner).
multipliernumberATR multiplier (Supertrend, Keltner).
kPeriodnumberStochastic %K smoothing.
dPeriodnumberStochastic %D period.
stepSizenumberPSAR acceleration step increment.
maxStepnumberPSAR maximum acceleration factor.

Custom Indicator

Extend BaseIndicator to create custom indicators. The calculate() method runs inside a Web Worker.

typescript
import { BaseIndicator } from '@quatick/quatick-charts';

export class ATRStopIndicator extends BaseIndicator {
  get name() { return 'ATR Stop'; }
  get id() { return 'atr-stop'; }
  get defaultParams() { return { period: 14, multiplier: 2 }; }

  calculate(candles, params) {
    const { period, multiplier } = params;
    const atr = this.calculateATR(candles, period);

    return candles.map((bar, i) => {
      if (i < period) return null;
      const hiLookback = candles.slice(i - period, i + 1).map(b => b.high);
      const hi = Math.max(...hiLookback);
      return {
        time: bar.time,
        value: hi - atr[i] * multiplier,
      };
    }).filter(Boolean);
  }

  // Helper: True Range → ATR
  private calculateATR(candles, period) {
    const trs = candles.map((b, i) => {
      if (i === 0) return b.high - b.low;
      const prev = candles[i - 1];
      return Math.max(b.high - b.low, Math.abs(b.high - prev.close), Math.abs(b.low - prev.close));
    });
    // Wilder's smoothing
    const atr = new Array(candles.length).fill(0);
    atr[period - 1] = trs.slice(0, period).reduce((a, b) => a + b, 0) / period;
    for (let i = period; i < trs.length; i++) {
      atr[i] = (atr[i - 1] * (period - 1) + trs[i]) / period;
    }
    return atr;
  }
}

// Register and use
import { IndicatorRegistry } from '@quatick/quatick-charts';
IndicatorRegistry.register(ATRStopIndicator);

chart.api.indicator.add({
  type: 'atr-stop',
  params: { period: 14, multiplier: 2 },
  color: '#f59e0b',
  pane: 0,
});

VWAP & Anchored VWAP

VWAP (Volume Weighted Average Price) is a special indicator that resets at session open. Anchored VWAP resets at a user-defined time/price anchor.

typescript
// Standard daily VWAP
chart.api.indicator.add({
  type: 'vwap',
  params: { resetType: 'session' },  // 'session' | 'week' | 'month'
  color: '#8b5cf6',
  pane: 0,
});

// Anchored VWAP (anchored to a specific bar's timestamp)
chart.api.indicator.add({
  type: 'vwap',
  params: {
    resetType: 'anchored',
    anchorTime: 1710000000,  // Unix timestamp to anchor from
    stdDevBands: [1, 2],     // Show ±1 and ±2 std dev bands
  },
  color: '#f59e0b',
  pane: 0,
});

Streaming & Real-Time Data

The DataApi abstracts data sources behind a unified interface. Supports WebSocket, Server-Sent Events, REST polling, and custom feed implementations. Incremental rendering ensures 60fps is maintained even at 1,000 ticks/second.

updateCandle() — Live Tick Updates

The simplest streaming API — push individual bar updates directly. The engine decides whether to update the last bar or append a new one based on the timestamp and candle interval.

typescript
// Feed real-time ticks from any source
function onWebSocketMessage(event) {
  const tick = JSON.parse(event.data);
  chart.updateCandle({
    time: tick.timestamp,  // Unix seconds
    open: tick.open,
    high: tick.high,
    low: tick.low,
    close: tick.close,
    volume: tick.volume,
  });
}

// Connect WebSocket manually
const ws = new WebSocket('wss://api.example.com/stream/BTCUSDT/1m');
ws.onmessage = onWebSocketMessage;

// Or with Binance-style kline format
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  const k = msg.k; // kline object
  chart.updateCandle({
    time: Math.floor(k.t / 1000),  // Convert ms → seconds
    open: parseFloat(k.o),
    high: parseFloat(k.h),
    low: parseFloat(k.l),
    close: parseFloat(k.c),
    volume: parseFloat(k.v),
  });
};

DataApi Connection Config

PropTypeDefaultDescription
protocol*"websocket"|"sse"|"rest"Data transport protocol.
url*stringEndpoint URL.
apiKeystringundefinedBearer token or API key for authentication.
symbol*stringMarket symbol identifier.
timeframe*stringCandle interval: "1m", "5m", "15m", "1h", "4h", "1D", "1W".
reconnectbooleantrueAutomatically reconnect on disconnect.
maxRetriesnumber5Maximum reconnection retry attempts.
retryDelaynumber1000Base delay in ms between retries (exponential back-off).
pollIntervalnumber5000Poll interval in ms (REST protocol only).
pingIntervalnumber30000WebSocket heartbeat ping interval in ms.
transformFunctionpassthroughCustom message transform fn: (raw) => Candle.
typescript
// WebSocket (recommended for live trading)
await chart.api.data.connect({
  protocol: 'websocket',
  url: 'wss://stream.binance.com:9443/ws/btcusdt@kline_1m',
  symbol: 'BTCUSDT',
  timeframe: '1m',
  reconnect: true,
  // Custom transform for Binance kline format
  transform: (msg) => {
    const k = JSON.parse(msg).k;
    return {
      time: Math.floor(k.t / 1000),
      open: parseFloat(k.o),
      high: parseFloat(k.h),
      low: parseFloat(k.l),
      close: parseFloat(k.c),
      volume: parseFloat(k.v),
    };
  },
});

// Server-Sent Events
await chart.api.data.connect({
  protocol: 'sse',
  url: 'https://api.example.com/stream/NIFTY/1m',
  symbol: 'NIFTY50',
  timeframe: '1m',
});

// REST polling (fallback)
await chart.api.data.connect({
  protocol: 'rest',
  url: 'https://api.example.com/klines',
  symbol: 'ETHUSD',
  timeframe: '5m',
  pollInterval: 5000,
});

// Disconnect
chart.api.data.disconnect();

Chart Events

Subscribe to chart-level events using the event bus. All callbacks receive typed event objects.

typescript
import { EventBus } from '@quatick/quatick-charts';

// Crosshair movement (fires on every mouse move)
chart.on('crosshairMove', ({ time, price, bar }) => {
  console.log('Price:', price.toFixed(2), '| Volume:', bar?.volume);
  // Update custom tooltip, status bar, order entry, etc.
});

// Click on chart canvas
chart.on('click', ({ time, price, screenX, screenY }) => {
  console.log('Clicked:', new Date(time * 1000).toISOString(), price);
});

// Double-click → reset view
chart.on('dblClick', () => {
  chart.resetView();
});

// Visible range changed (after zoom/pan)
chart.on('visibleRangeChange', ({ from, to, barCount }) => {
  console.log(`Showing ${barCount} bars from ${new Date(from * 1000).toDateString()}`);
  // Trigger lazy loading of older historical data
});

// New tick arrived from stream
chart.on('tick', (candle) => {
  console.log('New bar:', candle);
});

// Indicator calculation completed
chart.on('indicatorUpdate', ({ indicatorId, data }) => {
  console.log('Indicator computed:', indicatorId, data.length, 'points');
});

// Remove listener
const handler = ({ price }) => console.log(price);
chart.on('crosshairMove', handler);
chart.off('crosshairMove', handler);

Historical Data Loading

Combine historical data with live streaming for seamless full-history charts.

Best practice: Load historical data first with chart.setData(), then start the live stream with chart.updateCandle(). The chart engine merges new ticks with historical candles automatically — no deduplication logic required.
typescript
// 1. Fetch historical data from your API
const response = await fetch('/api/candles?symbol=BTCUSDT&interval=1m&limit=500');
const historical = await response.json();

// 2. Load into chart (must be sorted by time ascending)
chart.setData(historical.map(c => ({
  time: c.timestamp,  // Unix seconds
  open: c.open, high: c.high, low: c.low, close: c.close, volume: c.volume,
})));

chart.render();

// 3. Open WebSocket for live updates
const ws = new WebSocket('wss://stream.exchange.com/btcusdt@kline_1m');
ws.onmessage = ({ data }) => {
  const { k } = JSON.parse(data);
  chart.updateCandle({
    time: Math.floor(k.t / 1000),
    open: +k.o, high: +k.h, low: +k.l, close: +k.c, volume: +k.v,
  });
};

// 4. Lazy-load older history when user scrolls left
chart.on('visibleRangeChange', async ({ from }) => {
  if (from <= chart.getEarliestTime() + 3600) {
    const older = await fetchOlderHistory(from - 86400);
    chart.prependData(older);   // Prepend without re-rendering
  }
});

Custom Overlays & Drawings

The DrawingApi provides both built-in drawing tools and a low-level Canvas 2D API for fully custom overlays. Extend BaseOverlay for arbitrary rendering on top of the chart canvas.

DrawingApi — Built-in Tools

PropTypeDefaultDescription
setTool(type)voidActivate drawing mode: "trendline" | "horizontal" | "vertical" | "rectangle" | "arrow" | "fibonacci" | "text" | "pitchfork".
clearTool()voidExit drawing mode and deactivate cursor.
getDrawings()Drawing[]Return all current drawing objects.
removeDrawing(id)voidRemove a specific drawing by its stable ID.
clearAll()voidRemove all drawings from the chart.
exportDrawings()objectSerialize all drawings to a JSON-serializable object.
importDrawings(state)voidRestore drawings from a previously exported state.
typescript
// Enable trendline drawing mode
chart.api.drawing.setTool('trendline');

// Listen for new drawings
chart.on('drawing', (drawing) => {
  console.log('Added:', drawing.type, drawing.id);
  // Persist to backend
  await fetch('/api/drawings', {
    method: 'POST',
    body: JSON.stringify(drawing),
  });
});

// Fibonacci retracement
chart.api.drawing.setTool('fibonacci');
chart.on('drawing', (fib) => {
  console.log('Fib levels:', fib.levels);  // [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]
});

// Save / restore drawings
const state = chart.api.drawing.exportDrawings();
localStorage.setItem('chart-drawings', JSON.stringify(state));

// Later, on page reload:
const saved = JSON.parse(localStorage.getItem('chart-drawings') || '{}');
chart.api.drawing.importDrawings(saved);

// Clear all
chart.api.drawing.clearAll();

BaseOverlay — Custom Canvas Rendering

For fully custom rendering extend BaseOverlay. The render() method is called every frame with a coordinate-transform context.

typescript
import { BaseOverlay } from '@quatick/quatick-charts';

class VolumeProfileOverlay extends BaseOverlay {
  get name() { return 'Volume Profile'; }
  get id() { return 'volume-profile'; }

  render(ctx: CanvasRenderingContext2D, state: ILayerRenderContext) {
    const { visibleBars, priceToY, canvasWidth, canvasHeight } = state;
    const profile: Record<number, number> = {};

    // Build profile from visible bars
    for (const bar of visibleBars) {
      const priceLevel = Math.round(bar.close * 10) / 10;
      profile[priceLevel] = (profile[priceLevel] || 0) + bar.volume;
    }

    const maxVol = Math.max(...Object.values(profile));
    const maxBarWidth = canvasWidth * 0.15;  // max 15% of chart width

    for (const [priceStr, vol] of Object.entries(profile)) {
      const y = priceToY(parseFloat(priceStr));
      const barWidth = (vol / maxVol) * maxBarWidth;
      ctx.fillStyle = 'rgba(139, 92, 246, 0.25)';
      ctx.fillRect(canvasWidth - barWidth, y - 3, barWidth, 6);
    }
  }
}

chart.api.drawing.addOverlay(new VolumeProfileOverlay());

Render State / Coordinate API

PropTypeDefaultDescription
visibleBarsCandle[]Array of candle objects currently in the visible viewport.
priceToY(p)(price: number) => numberConvert a price to canvas Y-coordinate (pixels).
yToPrice(y)(y: number) => numberConvert canvas Y-coordinate to price.
timeToX(t)(time: number) => numberConvert Unix timestamp to canvas X-coordinate.
xToTime(x)(x: number) => numberInverse: canvas X to Unix timestamp.
barWidthnumberCurrent width of a single bar in pixels.
canvasWidthnumberTotal canvas width in pixels.
canvasHeightnumberTotal canvas height in pixels.
dprnumberDevice pixel ratio (1 on standard, 2 on retina).

Overlay Examples

typescript
// Support/Resistance line overlay
class SRLinesOverlay extends BaseOverlay {
  constructor(private levels: number[]) { super(); }

  render(ctx, { priceToY, canvasWidth }) {
    for (const level of this.levels) {
      const y = priceToY(level);
      ctx.save();
      ctx.strokeStyle = 'rgba(139, 92, 246, 0.6)';
      ctx.setLineDash([6, 4]);
      ctx.lineWidth = 1;
      ctx.beginPath();
      ctx.moveTo(0, y);
      ctx.lineTo(canvasWidth, y);
      ctx.stroke();

      ctx.fillStyle = 'rgba(139, 92, 246, 0.9)';
      ctx.font = '10px -apple-system, sans-serif';
      ctx.fillText(level.toFixed(2), 8, y - 4);
      ctx.restore();
    }
  }
}

chart.api.drawing.addOverlay(new SRLinesOverlay([44000, 43500, 42800]));

// Order-book depth overlay
class DepthOverlay extends BaseOverlay {
  update(bids: [number, number][], asks: [number, number][]) {
    this._bids = bids;
    this._asks = asks;
  }

  render(ctx, { priceToY, canvasWidth }) {
    const maxQty = Math.max(
      ...this._bids.map(b => b[1]),
      ...this._asks.map(a => a[1])
    );
    const scale = (canvasWidth * 0.12) / maxQty;

    for (const [price, qty] of this._bids) {
      const y = priceToY(price);
      ctx.fillStyle = 'rgba(52,211,153,0.15)';
      ctx.fillRect(0, y, qty * scale, 2);
    }

    for (const [price, qty] of this._asks) {
      const y = priceToY(price);
      ctx.fillStyle = 'rgba(248,113,113,0.15)';
      ctx.fillRect(0, y, qty * scale, 2);
    }
  }
}

User Interactions

The InteractionApi manages all user input: zoom, pan, crosshair, drawing tools, and keyboard shortcuts. All interactions can be enabled/disabled at runtime.

Zoom & Pan

PropTypeDefaultDescription
enableZoombooleantrueMouse-wheel and pinch-to-zoom.
enablePanbooleantrueClick-drag to pan the chart.
minBarsVisiblenumber5Minimum number of bars visible when zoomed in.
maxBarsVisiblenumber5000Maximum number of bars visible when zoomed out.
zoomSensitivitynumber1.0Mouse wheel sensitivity multiplier.
enableDoubleClickbooleantrueDouble-click to reset chart view.
enableKeyboardbooleantrueArrow-key panning. +/- keys for zoom.
typescript
// Configure via constructor
const chart = createChart({
  container: '#chart',
  interactions: {
    enableZoom: true,
    enablePan: true,
    minBarsVisible: 5,
    maxBarsVisible: 2000,
    zoomSensitivity: 1.2,
    enableDoubleClick: true,
    enableKeyboard: true,
  },
});

// Programmatic control
chart.api.interaction.zoomIn(1.5);      // 1.5x zoom in
chart.api.interaction.zoomOut(2);       // 2x zoom out
chart.api.interaction.resetZoom();      // Reset to default range
chart.api.interaction.fitContent();     // Fit all data in view
chart.api.interaction.pan(-50);         // Pan 50 pixels left

// Set precise visible range (Unix timestamps in seconds)
chart.api.interaction.setVisibleRange({
  from: Date.now() / 1000 - 7 * 86400,   // Last 7 days
  to: Date.now() / 1000,
});

// Lock/unlock interaction
chart.api.interaction.setEnabled(false);  // Freeze chart (presentation mode)
chart.api.interaction.setEnabled(true);   // Re-enable

Crosshair & Tooltip

PropTypeDefaultDescription
visiblebooleantrueShow/hide crosshair.
mode"free" | "magnetic""magnetic""free" follows cursor exactly; "magnetic" snaps to bar centers.
lineColorstringautoCrosshair line color.
lineWidthnumber1Crosshair line width in pixels.
lineDashnumber[][4, 4]Dash pattern for crosshair lines.
showPriceLabelbooleantrueShow price label on the y-axis.
showTimeLabelbooleantrueShow time label on the x-axis.
labelBackgroundstringautoBackground color of axis labels.
typescript
// Configure crosshair
chart.api.crosshair.configure({
  visible: true,
  mode: 'magnetic',
  lineColor: 'rgba(139, 92, 246, 0.5)',
  lineWidth: 1,
  lineDash: [4, 4],
  showPriceLabel: true,
  showTimeLabel: true,
});

// Crosshair move event — build custom tooltip
chart.on('crosshairMove', (event) => {
  const { time, price, bar } = event;

  if (!bar) return;

  // Update a custom HUD/overlay
  document.getElementById('price-hud').textContent =
    `O: ${bar.open.toFixed(2)} H: ${bar.high.toFixed(2)} L: ${bar.low.toFixed(2)} C: ${bar.close.toFixed(2)} V: ${bar.volume.toLocaleString()}`;
});

// Sync crosshairs across multiple chart instances
chart1.on('crosshairMove', ({ time }) => {
  chart2.api.crosshair.moveToTime(time);
});

Axis Configuration

PropTypeDefaultDescription
xAxis.visiblebooleantrueShow or hide the time axis.
xAxis.heightnumber26Time axis bar height in pixels.
xAxis.timezonestring"UTC"Timezone for label formatting (IANA timezone name).
yAxis.visiblebooleantrueShow or hide the price axis.
yAxis.position"right" | "left""right"Which side the price axis renders on.
yAxis.precisionnumber2Decimal places for price labels.
yAxis.logScalebooleanfalseEnable logarithmic price scale.
yAxis.invertedbooleanfalseInvert the y-axis (price increases downward).
yAxis.autoScalebooleantrueAuto-fit price range to visible bars.
typescript
// Update axes at runtime
chart.api.axis.configureX({
  visible: true,
  height: 26,
  timezone: 'Asia/Kolkata',  // IST
});

chart.api.axis.configureY({
  visible: true,
  position: 'right',
  precision: 2,
  logScale: false,
  autoScale: true,
});

// Fit visible price range to current data
chart.api.axis.fitYToContent();

Grid Configuration

typescript
chart.api.grid.configure({
  visible: true,
  horizontalVisible: true,
  verticalVisible: true,
  horizontalColor: 'rgba(255,255,255,0.04)',
  verticalColor: 'rgba(255,255,255,0.03)',
  horizontalStyle: 'dashed',   // 'solid' | 'dashed' | 'dotted'
  verticalStyle: 'dashed',
});

Performance Optimisation Tips

  • renderer: 'webgl' — Use WebGL for maximum GPU-accelerated throughput (default)
  • performanceHint: 'performance' — Reduces visual quality for best FPS on low-end devices
  • Limit visible indicator count. Use sub-panes for oscillators to reduce main pane overdraw.
  • Call chart.api.data.prependData() for history loading — avoids full re-render.
  • Avoid calling chart.setData() during streaming — use chart.updateCandle() for tick updates.
  • Destroy charts when unmounted: chart.destroy() frees GPU memory, WebSocket, and Workers.
  • For dashboards with 10+ charts, use renderer: 'canvas' on background charts to save GPU budget.
typescript
// Performance-tuned config for embedded/widget use
const chart = createChart({
  container: '#chart',
  renderer: 'webgl',
  performanceHint: 'performance',
  showVolume: false,             // Disable volume to save one render pass
  grid: { visible: false },      // Disable grid lines
  crosshair: { visible: false }, // Disable crosshair if not needed
  interactions: {
    enableZoom: false,           // Lock chart in presentation mode
    enablePan: false,
    enableCrosshair: false,
  },
});

// Cleanup on unmount (React / Vue)
useEffect(() => {
  return () => chart.destroy();
}, []);

Options Payoff Chart

PayoffChart is a fully standalone 2D Canvas renderer for options strategy payoff diagrams. It ships separately from the financial chart and requires no OHLCV data. It renders P&L curves, breakeven lines, a spot price marker, standard deviation bands, crosshair tooltips, and supports pan/zoom interactions out of the box.

PayoffChart is exported directly from @quatick/quatick-charts. It does not depend on the financial Chart class and can be used independently.

Live Strategy Demo

Select a strategy below to see its payoff curve, breakevens, and 1σ / 2σ standard deviation bands. Spot: 19 500 · IV: 15% · DTE: 30.

Iron CondorShort two OTM options + long two further OTM options. Max profit in a range-bound market.

Quick Start

typescript
import { createPayoffChart, calculatePayoffChartData } from '@quatick/quatick-charts';

// 1. Create the chart
const chart = createPayoffChart({
  container: document.getElementById('payoff'),
  height: 400,
  theme: 'dark',           // 'dark' | 'light' | IPayoffChartTheme
  animation: { enabled: true, fadeInCurves: true, duration: 600 },
  crosshair: { enabled: true },
  tooltip:   { enabled: true },
});

// 2. Define strategy legs
const legs = [
  // Short put (lower wing)
  { id: 'l1', instrumentType: 'option', optionType: 'put',  positionType: 'short', strike: 19300, expiry: '2025-06-26', quantity: 1, premium: 150, lotSize: 50, iv: 0.15 },
  // Long put (further OTM)
  { id: 'l2', instrumentType: 'option', optionType: 'put',  positionType: 'long',  strike: 19100, expiry: '2025-06-26', quantity: 1, premium:  70, lotSize: 50, iv: 0.15 },
  // Short call (upper wing)
  { id: 'l3', instrumentType: 'option', optionType: 'call', positionType: 'short', strike: 19700, expiry: '2025-06-26', quantity: 1, premium: 160, lotSize: 50, iv: 0.15 },
  // Long call (further OTM)
  { id: 'l4', instrumentType: 'option', optionType: 'call', positionType: 'long',  strike: 19900, expiry: '2025-06-26', quantity: 1, premium:  75, lotSize: 50, iv: 0.15 },
];

// 3. Calculate payoff data
const data = calculatePayoffChartData(legs, 19500, 0.15, 30, {
  numPoints: 200,
  priceRangePercent: 0.12,   // ±12% around spot
});

// 4. Add SD bands (optional)
const sigma1 = 19500 * 0.15 * Math.sqrt(30 / 365);
data.sdBands = [
  { sigma: 1, lower: 19500 - sigma1, upper: 19500 + sigma1, color: '#42a5f5', opacity: 0.12, label: '±1σ' },
  { sigma: 2, lower: 19500 - sigma1 * 2, upper: 19500 + sigma1 * 2, color: '#ab47bc', opacity: 0.08, label: '±2σ' },
];

// 5. Render
chart.setData(data);

Chart Configuration — IPayoffChartConfig

PropTypeDefaultDescription
containerHTMLElement | stringMount element or CSS selector.
heightnumber400Chart height in pixels. Width fills the container.
theme"dark" | "light" | IPayoffChartTheme"dark"Color scheme. Pass a full IPayoffChartTheme object for full control.
animationIPayoffAnimationConfig­—Controls fade-in duration and easing for curves. enabled, fadeInCurves, duration, easing.
crosshairIPayoffCrosshairConfigenabled, color, width, style. Enables hover crosshair with price/PnL readout.
tooltipIPayoffTooltipConfigenabled, backgroundColor, textColor, borderColor, borderRadius, padding, fontSize.
xAxisIPayoffXAxisConfigvisible, height, fontSize, gridLines, tickCount, format (function).
yAxisIPayoffYAxisConfigvisible, width, fontSize, position ('left'|'right'), format (function).
interactionsIPayoffInteractionConfigenablePan, enableZoom, zoomSensitivity, kineticScrolling.
oiOverlayIOIOverlayConfigOpen interest bar overlay. enabled, showCalls, showPuts, heightRatio, position.

Strategy Leg — IStrategyLeg

PropTypeDefaultDescription
idstringUnique identifier for this leg.
instrumentType"option" | "future" | "perpetual""option"Instrument type.
optionType"call" | "put"Required when instrumentType is 'option'.
positionType"long" | "short"Direction of the leg.
strikenumberStrike price for option legs.
expirystringISO-8601 expiry date string (e.g. '2025-06-26').
quantitynumber1Number of lots.
premiumnumberPremium per unit (in the same currency as price). Used to compute net cost.
lotSizenumber1Contract lot size. P&L is scaled by quantity × lotSize.
ivnumberImplied volatility as a decimal (e.g. 0.15 for 15%). Used for Greeks and target-date curve.
greeksIGreeksautoOptional pre-computed Greeks (delta, gamma, theta, vega, rho). If omitted, calculated from iv.

Calculator — calculatePayoffChartData()

Helper that takes strategy legs and market params and returns a complete IPayoffChartData object ready to pass to chart.setData(). Computes the at-expiry curve, a target-date curve, breakevens, max profit/loss, net premium and net Greeks.

typescript
import { calculatePayoffChartData } from '@quatick/quatick-charts';

const data = calculatePayoffChartData(
  legs,            // IStrategyLeg[]
  spotPrice,       // Current underlying price
  impliedVol,      // IV as a decimal (0.15 = 15%)
  daysToExpiry,    // Days until front-month expiry
  {
    numPoints:         200,   // Resolution of the curve
    priceRangePercent: 0.12,  // ±% around spot for the X-axis
    daysToExpiry:       15,   // DTE for the target-date curve
    ivChange:           0,    // IV shift for scenario analysis
  },
);

// data.curves[0]       → At-Expiry curve (green/filled)
// data.curves[1]       → Target-date curve (blue/dashed)
// data.breakevens      → Array of breakeven prices
// data.maxProfit       → Max profit (per lot)
// data.maxLoss         → Max loss (per lot)
// data.netPremium      → Net premium received/paid
// data.netGreeks       → Aggregate Greeks for the strategy

Data Object — IPayoffChartData

PropTypeDefaultDescription
legsIStrategyLeg[]Strategy leg definitions (passed through from input).
curvesIPayoffCurve[]At-expiry and target-date payoff curves. Each curve has id, label, points[], color, lineWidth, dash, fillArea, fillOpacity.
spotPricenumberCurrent underlying price used to draw the spot marker.
breakevensnumber[][]Prices at which strategy P&L equals zero.
maxProfitnumberMax profit per lot. Infinity for unlimited-profit strategies.
maxProfitPricenumberUnderlying price at which max profit occurs.
maxLossnumberMax loss per lot (negative). -Infinity for unlimited-risk strategies.
netPremiumnumberNet premium received (positive) or paid (negative).
netGreeksIGreeksAggregate delta, gamma, theta, vega, rho for the strategy.
sdBandsIStdDevBand[][]Standard deviation bands. Each band has sigma, lower, upper, color, opacity, label.
daysToExpirynumberDays to front-month expiry.
impliedVolatilitynumberIV used for the target-date curve calculation.
oiDataIOIDataPoint[][]Optional open interest data per strike for the OI overlay bar chart.

Chart Methods

PropTypeDefaultDescription
setData(data)voidSet or update the full payoff data. Triggers fade-in animation for new curves.
setOIData(oiData)voidUpdate the open interest overlay independently without re-computing payoff curves.
setOIEnabled(enabled)voidToggle the OI overlay visibility.
setConfig(config)voidApply partial config updates at runtime (theme, axis visibility, etc.).
setTheme(theme)voidSwitch dark/light or apply a custom IPayoffChartTheme.
requestRender()voidForce an immediate re-render (e.g. after external state change).
on(event, callback)voidSubscribe to events: 'hover', 'breakevenHover', 'resize', 'destroy'.
off(event, callback)voidUnsubscribe a previously registered event listener.
destroy()voidTear down the chart, remove the canvas from the DOM, and release all resources.

Standard Deviation Bands

SD bands visually show the expected move for the underlying over the life of the strategy. Calculate σ from IV and DTE and pass them via data.sdBands.

typescript
// Annualised IV × sqrt(DTE/365) gives single-sigma expected move
const spot = 19500;
const iv   = 0.15;   // 15%
const dte  = 30;     // days

const sigma1 = spot * iv * Math.sqrt(dte / 365);
const sigma2 = sigma1 * 2;

data.sdBands = [
  {
    sigma:   1,
    lower:   spot - sigma1,
    upper:   spot + sigma1,
    color:   '#42a5f5',
    opacity: 0.12,
    label:   '±1σ',
  },
  {
    sigma:   2,
    lower:   spot - sigma2,
    upper:   spot + sigma2,
    color:   '#ab47bc',
    opacity: 0.08,
    label:   '±2σ',
  },
];

Open Interest Overlay

Render call and put open interest as horizontal bars below the payoff curves. Useful for identifying support/resistance clusters through OI concentration.

typescript
// Provide OI data per strike
data.oiData = [
  { strike: 19100, callOI: 45000, putOI: 120000 },
  { strike: 19200, callOI: 62000, putOI: 98000  },
  { strike: 19300, callOI: 88000, putOI: 210000 },
  { strike: 19500, callOI: 105000, putOI: 95000 },
  { strike: 19700, callOI: 145000, putOI: 48000 },
  { strike: 19900, callOI: 72000,  putOI: 31000 },
];

chart.setConfig({
  oiOverlay: {
    enabled:      true,
    showCalls:    true,
    showPuts:     true,
    position:     'bottom',   // 'bottom' | 'overlay'
    heightRatio:  0.25,       // 25% of chart height
    barWidthRatio: 0.7,
    callColor:    '#42a5f5',
    putColor:     '#f87171',
    opacity:      0.75,
  },
});

Events

typescript
// Hover — fired on every mousemove with nearest P&L value
chart.on('hover', (e) => {
  console.log('price:', e.price, 'pnl:', e.pnl);
});

// Breakeven hover — fired when crosshair crosses a breakeven line
chart.on('breakevenHover', (e) => {
  console.log('breakeven at:', e.price);
});

// Resize — fired when the chart redraws due to container resize
chart.on('resize', (e) => {
  console.log('new size:', e.width, e.height);
});

// Clean up
chart.on('destroy', () => {
  console.log('chart destroyed');
});
For React usage, wrap createPayoffChart in a useEffect and call chart.destroy() in the cleanup function. Dynamic import with import('@quatick/quatick-charts') avoids SSR issues.

Advanced API Reference

Chart Instance Methods

Complete list of methods on each Chart instance.

PropTypeDefaultDescription
setData(candles)voidLoad or replace entire dataset. Input must be sorted by time.
updateCandle(candle)voidAppend or update the last bar with a live tick. O(1) update.
prependData(candles)voidPrepend historical candles without re-rendering the view.
setChartType(type)voidSwitch renderer at runtime. Preserves data and indicators.
render()voidForce a full re-render.
resetView()voidReset zoom/pan to full data view.
fitContent()voidFit all data into the visible viewport.
getVisibleRange()RangeReturn { from, to, barCount } of the current viewport.
getEarliestTime()numberReturn Unix timestamp of earliest candle in dataset.
getLatestTime()numberReturn Unix timestamp of most recent candle.
on(event, cb)voidRegister event listener.
off(event, cb)voidRemove event listener.
getMetrics()MetricsReturn performance metrics object (FPS, render time, memory).
enableProfiling(bool)voidEnable/disable performance profiling.
screenshot()stringExport chart as PNG data URL.
destroy()voidDestroy chart, release GPU/Worker/WebSocket resources.

API Module Map

chart.api.candlestick

Set candle/bar colors, hollow mode, wick styles.

chart.api.theme

Switch theme preset or apply custom color overrides.

chart.api.axis

Configure X/Y axes, precision, log scale, inversion.

chart.api.grid

Toggle grid lines, colors, dash patterns.

chart.api.crosshair

Configure crosshair appearance and mode.

chart.api.indicator

Add/remove/configure technical indicators.

chart.api.drawing

Drawing tools, custom overlays, import/export.

chart.api.interaction

Zoom, pan, keyboard shortcuts, interaction lock.

chart.api.layout

Pane management, margins, multi-pane layout.

chart.api.data

Data feed connection, live streaming, disconnect.

chart.api.export

Screenshot, SVG export, JSON data export.

Core TypeScript Types

typescript
// Candle / bar object
interface Candle {
  time: number;     // Unix timestamp (seconds)
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
}

// Extended candle with optional metadata
interface ExtendedCandle extends Candle {
  color?: string;   // Override bar color
  text?: string;    // Annotation label
  [key: string]: any;
}

// Time range
interface TimeRange {
  from: number;     // Start Unix timestamp
  to: number;       // End Unix timestamp
}

// Performance metrics
interface FrameStats {
  fps: number;
  renderTimeMs: number;
  visibleBars: number;
  memoryMb: number;
  indicatorCalcMs: number;
  workerCpuPercent: number;
}

// Renderer types
type RendererType =
  | 'candlestick' | 'hollow-candle' | 'volume-candle'
  | 'line' | 'step-line' | 'baseline' | 'area'
  | 'heikin-ashi' | 'renko' | 'kagi' | 'line-break'
  | 'point-figure';

Complete Event Reference

typescript
// Lifecycle events
chart.on('ready',       () => {});
chart.on('destroy',     () => {});

// Data events
chart.on('tick',        (candle: Candle) => {});
chart.on('dataLoad',    ({ candles, fromCache }) => {});
chart.on('dataError',   (err: Error) => {});

// Stream events
chart.on('connected',   ({ protocol, url }) => {});
chart.on('disconnect',  ({ reason }) => {});
chart.on('reconnect',   ({ attempt }) => {});

// User interaction events
chart.on('click',       ({ time, price, screenX, screenY }) => {});
chart.on('dblClick',    ({ time, price }) => {});
chart.on('rightClick',  ({ time, price, screenX, screenY }) => {});
chart.on('crosshairMove', ({ time, price, bar }) => {});
chart.on('crosshairLeave', () => {});
chart.on('zoom',        ({ factor, centerTime }) => {});
chart.on('pan',         ({ direction, deltaTime }) => {});
chart.on('visibleRangeChange', ({ from, to, barCount }) => {});

// Drawing events
chart.on('drawing',         (drawing) => {});
chart.on('drawingChange',   (drawing) => {});
chart.on('drawingRemove',   ({ id }) => {});
chart.on('drawingsClear',   () => {});

// Indicator events
chart.on('indicatorAdd',    ({ id, type }) => {});
chart.on('indicatorRemove', ({ id }) => {});
chart.on('indicatorUpdate', ({ id, data }) => {});

// Chart-type events
chart.on('chartTypeChange', ({ from, to }) => {});

// Performance events
chart.on('profile',     ({ frame, totalMs, renderMs, overlayMs }) => {});

Plugin Development

Every chart feature is a plugin. Create renderer plugins, indicator plugins, or overlay plugins by extending the appropriate base class and registering with PluginRegistry.

typescript
import { BasePlugin, PluginState, PluginCategory, PluginRegistry } from '@quatick/quatick-charts';

export class OrderFlowPlugin extends BasePlugin {
  get name() { return 'Order Flow Footprint'; }
  get id() { return 'order-flow'; }
  get category() { return PluginCategory.RENDERER; }

  async initialize(context) {
    this.context = context;
    this.state = PluginState.INITIALIZED;
    this._registerEventHandlers();
  }

  async render(series, renderState) {
    const { visibleBars, priceToY, timeToX, barWidth, canvasHeight } = renderState;
    const ctx = this.context.canvas.getContext('2d');

    for (const bar of visibleBars) {
      const x = timeToX(bar.time);
      const y = priceToY(bar.close);
      const delta = (bar.buyVolume || 0) - (bar.sellVolume || 0);
      const color = delta >= 0 ? '#34d399' : '#f87171';

      ctx.fillStyle = color;
      ctx.font = '9px monospace';
      ctx.fillText(Math.abs(delta).toFixed(0), x - barWidth / 2 + 2, y - 4);
    }
  }

  async cleanup() {
    this.state = PluginState.DESTROYED;
  }
}

// Register globally
PluginRegistry.register(OrderFlowPlugin);

// Use in chart
chart.api.indicator.add({ type: 'order-flow' });

Performance Profiling

typescript
// Access runtime metrics
const metrics = chart.getMetrics();
console.log({
  fps:                 metrics.fps,
  renderTimeMs:        metrics.renderTimeMs,
  visibleBars:         metrics.visibleBars,
  memoryMb:            metrics.memoryMb,
  indicatorCalcMs:     metrics.indicatorCalcMs,
  workerUtilization:   metrics.workerCpuPercent,
});

// Enable frame-by-frame profiling
chart.enableProfiling(true);
chart.on('profile', (data) => {
  console.log({
    totalMs:     data.totalMs,
    geometryMs:  data.geometryMs,
    renderMs:    data.renderMs,
    overlayMs:   data.overlayMs,
    indicatorMs: data.indicatorMs,
  });
});

Export API

typescript
// Export chart as PNG data URL
const dataUrl = await chart.api.export.screenshot();
const link = document.createElement('a');
link.href = dataUrl;
link.download = 'chart.png';
link.click();

// Export chart data as JSON
const data = chart.api.export.getData();
// Returns: { candles: Candle[], indicators: Record<id, IndicatorValue[]>, range: TimeRange }

// Export chart config (for persistence)
const config = chart.api.export.getConfig();
localStorage.setItem('chart-config', JSON.stringify(config));

// Restore from saved config
const saved = JSON.parse(localStorage.getItem('chart-config')!);
chart.api.export.applyConfig(saved);

Multi-Pane Layout

The layout engine supports multiple stacked panes, each with their own price scale. Sub-panes are created automatically when indicators use pane: 1 or higher.

PropTypeDefaultDescription
panes[n].heightnumber | string"auto"Pane height in pixels or '%'. Main pane fills remaining space.
panes[n].visiblebooleantrueToggle pane visibility.
panes[n].resizablebooleantrueAllow user to drag-resize the pane boundary.
panes[n].minHeightnumber60Minimum pane height in pixels.
typescript
// Configure pane heights
chart.api.layout.configurePanes([
  { height: '70%', resizable: true },    // Main pane: 70%
  { height: '15%', minHeight: 60 },      // RSI sub-pane
  { height: '15%', minHeight: 60 },      // MACD sub-pane
]);

// Listen for pane resize
chart.on('paneResize', ({ paneIndex, newHeight }) => {
  console.log('Pane', paneIndex, 'resized to', newHeight, 'px');
});

Memory & Resource Management

  • Always call chart.destroy() when unmounting (React useEffect cleanup, Vue onUnmounted).
  • Large datasets: use prependData() for lazy history loading. Avoid replacing the entire dataset during streaming.
  • Web Workers: Indicator calculations run off-main-thread by default. No special config required.
  • IndexedDB: Use the optional @quatick/cache-adapter to cache historical data locally.
  • GPU context loss: The chart listens for WebGL context-loss events and automatically recovers.

Quatick Charts is MIT licensed and maintained by the Quatick core team. GitHub · Discord

← Charts API Overview Browse Indicators Marketplace