JSPM

@elitechart/core

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

Framework-agnostic charting engine, renderer, and data layer for ChartForge.

Package Exports

  • @elitechart/core
  • @elitechart/core/package.json

Readme

@elitechart/core

npm version types bundle size license: MIT

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: createChart requires a DOM. Mount it from a Next.js client component ('use client' + useEffect) or from any other on-mount lifecycle. There is no top-level window access at module scope, so importing this package from a Server Component is safe.

Install

pnpm add @elitechart/core

No 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 / document access at module scope; createChart runs only when called.
  • Tree-shakeable ESM + CJS dual emit, named exports only, sideEffects: false.

License

MIT — see LICENSE.