此頁面尚未翻譯為繁體中文,因此以英文顯示。

Plugins

Extend the chart with custom indicators, drawing tools, chart types, and overlays — registered globally or per chart.

Registration

Three ways to register, in precedence order: global defaults, the constructor, then imperative instance calls.

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

// 1) Global — every Chart created afterward inherits it
registerPlugin({ kind: 'indicator', plugin: new MyIndicator() })

// 2) Per-chart at construction
const chart = new Chart(el, { plugins: [{ kind: 'overlay', plugin: myHeatmap }] })

// 3) Imperative on an instance
chart.plugins.register({ kind: 'chartType', plugin: myCandles })
chart.plugins.unregister('chartType:my-candles')
chart.plugins.list()

Plugin kinds

KindContractRenders
indicatorIndicatorPlugin — calculate() + render()overlay or panel
drawingDrawingPlugin — render() + hitTest()overlay layer
chartTypeChartTypePlugin — createRenderer() + optional transform()main series
overlayOverlayPlugin — render(ctx, { viewport, data, theme })main / overlay / ui

Custom indicator

Extend IndicatorBase for the drawing helpers, then register and add it like any built-in:

import { IndicatorBase, IndicatorValueMap, registerPlugin } from '@tradecanvas/chart'

class DoubleSMA extends IndicatorBase {
  descriptor = {
    id: 'double-sma', name: 'Double SMA',
    placement: 'overlay', defaultConfig: { fast: 10, slow: 30 },
  }
  calculate(data, config) { /* values = new IndicatorValueMap(); return { values, series } */ }
  render(ctx, output, viewport, style) { /* draw lines */ }
}

registerPlugin({ kind: 'indicator', plugin: new DoubleSMA() })
chart.addIndicator('double-sma', { fast: 10, slow: 30 })

For values, use new IndicatorValueMap() instead of new Map(): it's a drop-in Map that is several times cheaper to fill one bar at a time in time order, which is what every symbol or timeframe switch does over the whole history.

Fast live updates (optional)

On every live tick only the forming bar changes. Implement update() to recompute just the bars from from onward instead of the whole history — the chart uses it on ticks and new bars, and falls back to calculate() when it's missing or returns null. It must produce the same values calculate() would.

update(data, config, prev, from) {
  if (!this.canResume(data, prev, from)) return null   // IndicatorBase helper
  for (let i = from; i < data.length; i++) {
    this.writePoint(prev, data, i, { value: /* recompute bar i */ 0 })
  }
  return prev
}

Custom drawing tool

Extend DrawingBase for its helpers (anchors to pixels, line styles, handles), describe the tool, and register it. Settings listed in descriptor.options show up in the widget's settings dialog and are saved with the drawing.

import { DrawingBase, registerPlugin } from '@tradecanvas/chart'

class TargetLine extends DrawingBase {
  descriptor = {
    type: 'targetLine', name: 'Target Line', requiredAnchors: 1,
    options: { label: { kind: 'text', label: 'Label', default: 'Target' } },
  }
  render(ctx, state, viewport, selected) { /* at this.anchorToPixel(state.anchors[0], viewport) */ }
  hitTest(point, state, viewport, tolerance) { /* is the pointer on it? */ return false }
  // optional: the prices of its lines, so an alert can follow them
  priceAt(state) { return [state.anchors[0].price] }
}

registerPlugin({ kind: 'drawing', plugin: new TargetLine() })
chart.setDrawingTool('targetLine')

The descriptor can also say how the tool is drawn (creation: 'clicks', 'freehand' or 'path', with maxAnchors) and whether the dialog offers a fill (fill) or text (text). A tool can move handles that are not anchors with moveHandle(), and receives the chart's bars through setDataGetter() when it draws from them.

Custom chart type

A ChartTypePlugin supplies a renderer and an optional data transform; switch to it like a built-in:

registerPlugin({
  kind: 'chartType',
  plugin: {
    descriptor: { type: 'my-bricks', name: 'My Bricks' },
    createRenderer: () => new MyBrickRenderer(),
    transform: (raw) => toBricks(raw),   // optional
  },
})

chart.setChartType('my-bricks')

Custom overlay

An OverlayPlugin draws each frame on the layer you choose, receiving the live viewport, data, and theme:

registerPlugin({
  kind: 'overlay',
  plugin: {
    descriptor: { id: 'vwap-band', name: 'VWAP Band', layer: 'main' },
    render(ctx, { viewport, data, theme }) {
      // draw onto the main layer with the current viewport + data
    },
  },
})

See the API reference for the full plugin type signatures.