Skip to content
EliteChart

Indicators

How indicators compute, how they render (overlay vs pane), and how to add or remove them at runtime.

An indicator is a pure function over ReadonlyArray<Bar> that returns one or more output series. ChartForge ships 30+ indicators in @elitechart/indicators; you can write your own following the same shape.

Quick example

code
'use client';
import { EliteChart, useChartStore } from '@elitechart/elitechart';
import { useEffect } from 'react';
import '@elitechart/elitechart/styles.css';

export default function Page() {
  useEffect(() => {
    useChartStore.getState().addIndicator({
      id: 'ema-20',
      name: 'EMA',
      params: { period: 20 },
    });
  }, []);
  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <EliteChart symbol="BTCUSD" timeframe="1h" />
    </div>
  );
}
View source on GitHub →

How it works

Indicators run in a topological pipeline. Each one declares its inputs (a series — close, hlc3, etc.) and its outputs (one or more series). The pipeline:

  1. Collects active indicators from useChartStore.indicators.
  2. Sorts them by dependency order (RSI on top of EMA depends on the EMA result).
  3. Calls each indicator's compute(bars, params) function.
  4. Hands the resulting series back to the renderer, which paints them either as an overlay on the price pane or in a separate pane below the price.

The compute function is pure. The same (bars, params) always returns the same result — which makes indicators deterministic, backtestable, and worker-portable when we ship Workers.

Overlay vs pane

The indicator's paneKind field decides where it renders.

  • 'overlay' — drawn on the same pane as the price (EMA, SMA, Bollinger Bands, VWAP).
  • 'pane' — drawn in a separate pane below the price (RSI, MACD, Stochastic, OBV).

Pane indicators stack vertically. Each pane has its own price axis and crosshair readout.

Variations

Remove an indicator

code
import { useChartStore } from '@elitechart/elitechart';
useChartStore.getState().removeIndicator('ema-20');

Update parameters at runtime

code
useChartStore.getState().patchIndicator('ema-20', { period: 50 });

Subpath imports for tree-shaking

Only the indicators you actually use should reach your bundle:

code
import { rsi } from '@elitechart/indicators/rsi';
import { macd } from '@elitechart/indicators/macd';

API

SymbolFromNotes
IndicatorPlugin@elitechart/corethe contract
useChartStore.addIndicator@elitechart/elitechartmutate the active set
useChartStore.removeIndicator
useChartStore.patchIndicatorpatch params