@elitechart/core
v0.1.1
Published
Framework-agnostic charting engine, renderer, and data layer for ChartForge.
Maintainers
Readme
@elitechart/core
Framework-agnostic charting engine for ChartForge — a Professional-grade trading chart in TypeScript. Candle / line / area / bar series with Heikin-Ashi, Renko, Kagi, Point & Figure, Range Bars, Line Break, and Elder-Impulse transforms; 30+ technical indicators; 46 drawing tools; bar replay; compare-symbol overlays; magnet snap; corporate-event markers; price alerts; trade ticket; and a full keyboard-shortcut help overlay — all on a layered Canvas 2D backend that handles 100k+ bars at 60fps.
SSR note:
createChartrequires a DOM. Mount it from a Next.js client component ('use client'+useEffect) or from any other on-mount lifecycle. There is no top-levelwindowaccess at module scope, so importing this package from a Server Component is safe.
Install
pnpm add @elitechart/coreNo required peer dependencies. @elitechart/themes is optional but recommended for built-in dark / light palettes.
Quick example
import { createChart, asPrice, asTickSize, asTimestampMs, asVolume } from '@elitechart/core';
import { darkTheme } from '@elitechart/themes';
const container = document.getElementById('chart')!;
const chart = createChart(container, {
series: {
symbol: {
id: 'BTCUSDT',
ticker: 'BTCUSDT',
description: 'Bitcoin / Tether',
type: 'crypto',
tickSize: asTickSize(0.01),
priceScale: 2,
},
resolution: { kind: 'minutes', value: 1 },
bars: [
{
time: asTimestampMs(Date.now() - 60_000),
open: asPrice(67_000),
high: asPrice(67_120),
low: asPrice(66_980),
close: asPrice(67_080),
volume: asVolume(12),
},
// …more bars
],
},
kind: 'candle',
theme: darkTheme,
});
// Live tick (e.g. from a websocket)
chart.tickLastBar({
/* OHLCV update for the in-progress bar */
});
// Append a completed bar
chart.appendBar({
/* finalized bar */
});
// Always dispose on teardown
// chart.dispose();Concepts
ChartForge separates concerns into three independent layers. Understanding the boundary makes the rest of the API predictable.
Engine (createChart)
The engine owns the ChartHandle — a long-lived object that wires together the renderer, viewport, history stack, drawings, alerts, replay state, and event bus. Mutating methods (setSeries, setKind, setTheme, addDrawing, tickLastBar, …) push imperatively into the engine and flag the affected layers for repaint. The engine never re-renders the world from scratch — repaint is layer-scoped and frame-coalesced.
Renderer
A layered Canvas 2D backend with a frame scheduler, virtualized series painter (binary-search the visible bar slice; O(log N + M) per frame), memoized indicator overlays, and rolling perf instrumentation accessible via chart.getPerfStats(). The plugin API exposes the Paint seal so third-party renderers can hook into the same scheduler without touching internals.
Datafeed
ChartForge does not bundle a transport. The engine consumes a Series synchronously today; embedders fetch bars from any backend and call chart.setSeries(...) to swap. The Datafeed interface exists as a contract for future first-party adapters and for @elitechart/elitechart's real-time wiring — see "Datafeed contract" below.
What's in the box
Series kinds
candle, line, area, bar, heikin-ashi, hollow-candle, renko, line-break, kagi, point-figure, range-bars, elder-impulse. Switch at runtime via chart.setKind(kind) — aggregations recompute lazily and the viewport stays anchored.
Drawing tools (in @elitechart/drawings)
46 tools — Trend, Ray, Extended, Horizontal, Horizontal Ray, Vertical, Parallel Channel, Arrow, Rectangle, Ellipse, Fib Retrace / Ext / Channel / Time Zone, Trend-Based Fib Time, Price Range, Date Range, Date+Price, Text, Brush, Highlighter, Path, Polyline, Triangle, Circle, Arc, Rotated Rectangle, Callout, Note, Price Label, Flag Mark, Signpost, Arrow Marker (+up/down/left/right), Long Position, Short Position, Position Forecast, Pitchfork (Andrews / Schiff / Modified Schiff / Inside), Anchored VWAP, Fixed-Range Volume Profile.
Indicators (in @elitechart/indicators)
30+ — SMA, EMA, WMA, VWMA, DEMA, TEMA, HullMA, Bollinger + %B, Keltner, Donchian, StDev, RSI, Stochastic, MACD, CCI, ROC, Williams %R, Aroon, TSI, ATR, ADX, PSAR, SuperTrend, OBV, VWAP, MFI, CMF, Pivots, Ichimoku.
UX
- Drag / wheel pan + zoom with momentum scrolling (honors
prefers-reduced-motion) - Pinch-zoom + touch pan + long-press context menu on mobile (44px+ touch targets)
- Drag price-axis to rescale Y, drag time-axis to rescale X, double-click gutter to re-engage auto-fit
- Crosshair with price + time pills, magnet-snap to OHLC or drawing endpoints (off / weak / strong)
- OHLCV legend at top-left showing the hovered bar's values (+ compare-series rows)
- Last-price pill at the right gutter with bull/bear coloring and a dashed connector
- Axis "+" quick-action menu for Add alert / Draw horizontal line / Buy / Sell
- Replay mode with drag-to-select range, play/pause, step ±1/±10 bars, Space/←/→ shortcuts
- Marquee select (Shift+drag), arrow-key nudge (1px / 10px with Shift), full undo/redo
- Settings dialog for candle colors / background / grid / font
- Alerts management dialog with per-alert edit/delete
- PNG / JPEG / WebP export via
chart.exportPng() - Keyboard shortcut help overlay — press
?to see every binding
Performance
@elitechart/core ships under the 150KB gzipped budget with all UI components included. Drawings + indicators are separate, tree-shakeable packages. chart.getPerfStats() exposes rolling last/avg/peak ms + frame count for hot-path verification.
Plugin API
Custom indicators / overlays / drawing tools via IndicatorPlugin, OverlayPlugin, DrawingPlugin. Hot-swappable renderer backend (Canvas 2D today; WebGL pluggable through the Paint seal).
SSR & headless usage
createChart requires a DOM. From Next.js App Router:
'use client';
import { useEffect, useRef } from 'react';
import { createChart, type ChartHandle } from '@elitechart/core';
import { darkTheme } from '@elitechart/themes';
export function Chart({ series }: { series: Parameters<typeof createChart>[1]['series'] }) {
const ref = useRef<HTMLDivElement>(null);
const chartRef = useRef<ChartHandle | null>(null);
useEffect(() => {
if (ref.current === null) return;
chartRef.current = createChart(ref.current, { series, theme: darkTheme });
return () => chartRef.current?.dispose();
}, [series]);
return <div ref={ref} style={{ height: 480 }} />;
}For a friendlier React experience, use @elitechart/react which wraps this boilerplate behind <Chart /> + useChart.
For unit tests + Web Workers, the data validation, math (LinearScale, LogScale, priceBounds, niceTicks), and series-aggregation helpers (computeHeikinAshi, computeRenkoBricks, computeKagi, computePointFigure) are all pure functions with no DOM dependency — import them directly without booting an engine.
Datafeed contract
import type { Datafeed } from '@elitechart/core';
const feed: Datafeed = {
async getBars({ symbol, resolution, from, to, signal }) {
const res = await fetch(`/api/bars?s=${symbol.id}&from=${from}&to=${to}`, { signal });
return res.json();
},
subscribe({ symbol, resolution }, onBar) {
const ws = new WebSocket(`wss://example.com/${symbol.id}`);
ws.onmessage = (e) => onBar(JSON.parse(e.data));
return () => ws.close();
},
searchSymbols: async (q) => fetch(`/api/search?q=${q}`).then((r) => r.json()),
resolveSymbol: async (id) => fetch(`/api/symbol/${id}`).then((r) => r.json()),
};Works with REST + polling, WebSocket streams, or any custom transport. @elitechart/elitechart will consume this contract first-party in a coming phase; today the engine pulls bars synchronously from Series and the embedder owns transport.
Layout persistence
const layout = chart.serializeLayout({ savedAt: Date.now() });
localStorage.setItem('my-chart', JSON.stringify(layout));
const saved = JSON.parse(localStorage.getItem('my-chart')!);
chart.loadLayout(saved, drawingToolRegistry);Bars are NOT serialized — the embedder re-fetches them using the symbol + resolution fields on the layout.
Theming
CSS variable tokens (--cf-color-*, --cf-space-*, --cf-font-*). Runtime swap via chart.setTheme(theme) or patch tokens individually via chart.patchTheme({ seriesUp: '#26a69a' }). Dark + light themes ship in @elitechart/themes; custom themes implement the same token map.
Compatibility
- Node 20.11+ (build / SSR import). DOM-free helpers are Node-clean.
- Browsers Chrome / Safari / Firefox / Edge (evergreen), iOS Safari 16+, Android Chrome. Pointer Events throughout — unifies mouse / touch / pen handling.
- TypeScript 5.0+ recommended for branded-primitive narrowing.
- SSR safe. No
window/documentaccess at module scope;createChartruns only when called. - Tree-shakeable ESM + CJS dual emit, named exports only,
sideEffects: false.
Links
- Source
- Changelog
- GitHub issues
@elitechart/reactadapter@elitechart/elitechartUI shell@elitechart/indicators·@elitechart/drawings·@elitechart/themes
License
MIT — see LICENSE.
