← ROMACO romaco-charts · documentation
← Demo
v1.0.0-beta.6
BETA
01 — GETTING STARTED

ROMACO CHARTS

High-performance trading charts built with TypeScript and HTML5 Canvas. Optimized Canvas rendering with automatic data conflation (LTTB) for 100K+ candles at 60 FPS.

Blazing Fast

Optimized Canvas rendering with automatic LTTB conflation — 100K+ candles at 60 FPS.

29+ Indicators

EMA, SMA, MACD, RSI, BOLL and many more. Tree-shakeable — bundle only what you use.

Drawing Tools

Professional tools: trendlines, Fibonacci, Elliott Waves, parallel channels, and more.

Plugin System

Hook into the render cycle, add overlays, react to events with a clean plugin API.

AI Integration

ChartAgentController exposes a stable AI-facing API for LLM and agent workflows.

Minimal Deps

Lean core with optional React and Web Component integrations.

FeatureRomaco ChartsOther Libraries
Built-in Indicators 29+ Varies
Drawing Tools 14+ Limited
Data Conflation Automatic (LTTB) Varies
AI Agent API ChartAgentController None
Plugin System Full lifecycle hooks Varies
License BUSL-1.1 / Commercial Varies

Installation

Package Manager

npm
npm install romaco-charts
yarn
yarn add romaco-charts
pnpm
pnpm add romaco-charts

CDN (quick prototyping)

html
<script type="module">
  import { createChart } from 'https://unpkg.com/romaco-charts@latest/dist/index.js';
  const chart = createChart('#chart', { preset: 'dark', data: yourData });
</script>

Entry Points

Use only the entry points you need — bundlers tree-shake the rest.

typescript
import { createChart } from 'romaco-charts'
import { Chart, MinimalChart, TradingTerminal, ChartGrid, useRomacoChart } from 'romaco-charts/react'
import { ChartAgentController } from 'romaco-charts/agent'
import type { IDatafeed } from 'romaco-charts/datafeed'
import { FootprintRenderer } from 'romaco-charts/renderers'
import type { PersistableChartState } from 'romaco-charts/profile'
import type { ReplayStatus } from 'romaco-charts/replay'

The root package re-exports many common types, but dedicated subpaths keep imports narrow and intent explicit:

Entry PointPurpose
romaco-charts Core chart, indicators, renderers, paper trading, presets, and drawings.
romaco-charts/react React wrappers, terminal UI, hooks, grid helpers, and datafeed type re-exports.
romaco-charts/agent AI controller and action/context types.
romaco-charts/datafeed Datafeed contracts, history types, realtime status, and createNoopRealtimeSubscription helper.
romaco-charts/drawings Drawing manager, toolbar, templates, and drawing types.
romaco-charts/indicators Tree-shakeable indicator entry point.
romaco-charts/profile State persistence, visual state, and validation helpers.
romaco-charts/replay Replay controller types and session state.
romaco-charts/renderers Standalone renderers and renderer option types.
romaco-charts/ui Vanilla UI components and tokens.
romaco-charts/web-components Custom element wrapper for non-React embeds.

Peer Dependencies

PackageRequiredPurpose
litOptionalWeb Components (&lt;romaco-chart&gt;)
reactOptionalReact integration (romaco-charts/react)
react-domOptionalReact rendering peer

Quick Start

Create your first chart in under 5 minutes.

1. Create a Container

html
<div id="chart" style="width: 800px; height: 400px;"></div>

2. Import and Create

typescript
import { createChart } from 'romaco-charts';

const chart = createChart('#chart', {
  preset: 'dark',
  data: [
    { time: 1672531200000, open: 100, high: 105, low: 98, close: 103, volume: 1000 },
    { time: 1672617600000, open: 103, high: 110, low: 101, close: 108, volume: 1500 },
  ]
});

Adding Indicators

typescript
const chart = createChart('#chart', {
  preset: 'dark',
  data: candleData,
  indicators: ['EMA', 'RSI', 'MACD']
});

Enabling Drawing Tools

typescript
import { allTemplates } from 'romaco-charts/drawings';

const chart = createChart('#chart', {
  preset: 'dark',
  data: candleData,
  drawings: {
    templates: allTemplates,
    showToolbar: true,
    toolbarVisibility: 'edge-hover',
  }
});

Large Datasets — Data Conflation

typescript
const chart = createChart('#chart', {
  preset: 'dark',
  data: largeDataset,    // 100,000 candles
  dataConflation: true   // Automatic LTTB downsampling
});

Complete Example

typescript
import {
  createChart,
  allTemplates,
  registerIndicators,
  EMAIndicator,
  RSIIndicator,
  MACDIndicator,
} from 'romaco-charts';

registerIndicators(EMAIndicator, RSIIndicator, MACDIndicator);

const chart = createChart('#chart', {
  preset: 'professional',
  data: candleData,
  dataConflation: true,
  indicators: [
    { name: 'EMA', params: [12, 26] },
    { name: 'RSI', params: [14] },
    'MACD'
  ],
  drawings: {
    templates: allTemplates,
    position: 'left'
  },
  onCandleClick: (candle, index) => {
    console.log('Clicked candle:', candle);
  }
});
02 — GUIDE

Chart Types

11 renderer styles — match the chart to the analysis task instead of forcing every workflow into a standard candle view.

RendererBest ForNotes
CandleRenderer Detailed OHLC analysis Default. Most trading screens.
HeikinAshiRenderer Trend filtering Smooths noise, highlights sustained direction.
VolumeRenderer Dedicated volume panes Focuses on participation without overlaying price candles.
LineRenderer Clean trend overview Uses close values only.
AreaRenderer Lightweight dashboards Adds visual emphasis under the close line.
BaselineRenderer Relative move vs anchor Good for above/below reference displays.
HollowCandleRenderer Candle detail, lighter visual Useful for dense layouts.
StepLineRenderer Event-like price changes Keeps discrete transitions visible.
RenkoRenderer Noise reduction Focuses on movement size instead of time.
FootprintRenderer Order flow and imbalance views Volume-at-price and bid/ask detail.
SessionRenderer Session overlays Highlights market sessions and intraday structure.

Switching Renderer at Runtime

typescript
import {
  RomacoChart,
  CandleRenderer,
  HeikinAshiRenderer,
  LineRenderer,
} from 'romaco-charts';

const chart = RomacoChart.create('#chart', { data: candleData });
const canvas = document.querySelector('#chart canvas');
const ctx = canvas?.getContext('2d');

if (ctx) {
  chart.setRenderer(new HeikinAshiRenderer(ctx));
  chart.setRenderer(new LineRenderer(ctx, { lineColor: '#0f766e' }));
}

Practical Guidance

  • Use candles when you need precise execution context.
  • Use Heikin-Ashi when raw noise is making trend reading harder than it should be.
  • Use line, area, or volume when chart density is high or the view is mostly presentational.
  • Use footprint and session renderers when the workflow depends on order-flow structure or market-session context rather than plain candles alone.
  • Use Renko and baseline only when the analysis model explicitly benefits from them.

Indicators

Register only what you need — the catalog is tree-shakeable.

typescript
import { RomacoChart } from 'romaco-charts';
import { EMAIndicator, RSIIndicator, MACDIndicator } from 'romaco-charts/indicators';

RomacoChart.registerIndicators(EMAIndicator, RSIIndicator, MACDIndicator);

Built-in Catalog

Trend & Overlay

EMASMAMABBIBOLLSARAVPAVWAPDMA

Oscillators

RSIMACDKDJKDCCIDMIROCMTMTRIXWRAOPSYBIASBRARCR

Volume Studies

VOLOBVEMVPVTVR

Add at Runtime

typescript
const macdId = chart.addIndicator('MACD', { params: [12, 26, 9] });
chart.setIndicatorVisible(macdId, false);
chart.toggleIndicatorVisible(macdId);

chart.updateIndicator(macdId, {
  params: [10, 24, 8],
  styles: {
    macd: { color: '#0ea5e9', lineWidth: 2 },
    signal: { color: '#f97316', lineWidth: 2 },
  },
});

chart.removeIndicator(macdId);

Create-time Configuration

typescript
const chart = RomacoChart.create('#chart', {
  data: candleData,
  indicators: [
    { name: 'EMA', params: [12, 26] },
    { name: 'RSI', params: [14] },
    'MACD'
  ]
});

Placement Behavior

FamilyPlacementExamples
Trend overlays Main price pane EMA, SMA, MA, BOLL, SAR, AVP, BBI, AVWAP
Oscillators Dedicated subpanel RSI, MACD, KDJ, CCI, DMI, ROC, TRIX, WR, AO
Volume studies Dedicated subpanel VOL, OBV, EMV, PVT, VR

Drawing Tools

Drawing operations are handled through the DrawingManager — not flat methods on the chart instance.

typescript
import { RomacoChart } from 'romaco-charts';
import { allTemplates } from 'romaco-charts/drawings';

const chart = RomacoChart.create('#chart', {
  data: candleData,
  drawings: {
    templates: allTemplates,
    showToolbar: true,
    toolbarVisibility: 'edge-hover',
  },
});

const drawingManager = chart.getDrawingManager();
const toolbar = chart.getDrawingToolbar();

DrawingManager API

getDrawing(id)Retrieve a single drawing by ID.
getAllDrawings()Return all active drawings.
updateDrawing(id, override)Update drawing properties.
removeDrawing(id)Remove a single drawing.
clearAllDrawings() / clearAll()Remove all drawings.
undo() / redo()Undo/redo drawing operations.
canUndo() / canRedo()Check undo/redo availability.
setActiveTool(name)Activate a drawing tool programmatically.
setMode(mode) / getMode()Drawing mode control.
exportDrawings() / importDrawings(json)Serialize and restore drawings.
addDrawingFromState(type, points, styles?)Add drawing from raw state.

Chart-level Drawing Helpers

typescript
chart.setDrawingToolbarVisibilityMode(mode);
chart.setDrawingLineColor(color);
chart.setDrawingLineWidth(width);
chart.setDrawingStyles(styles);
chart.updateDrawingStyle(drawingId, styles);

Persistence-friendly Workflows

typescript
const json = drawingManager?.exportDrawings();
if (json) {
  localStorage.setItem('romaco:drawings', json);
}

const saved = localStorage.getItem('romaco:drawings');
if (saved) {
  drawingManager?.importDrawings(saved);
}

This is the preferred app-level API. AI workflows can still use addDrawing, removeDrawing, and clearDrawings as protocol actions through ChartAgentController.

Themes

Built-in Presets

PresetDescription
dark Dark background with vibrant colors.
light Light background for daytime use.
bloomberg Black terminal with amber accents and monospace text.
neon Vibrant neon colors on dark background.
professional Clean, muted tones for professional use.
classic MT4-style monochrome chart with dashed grid lines.
romaco ROMACO brand — dark void, gold accents, JetBrains Mono.
typescript — preset
const chart = RomacoChart.create('#chart', {
  preset: 'romaco',
  data: candleData
});

Custom Colors

typescript
const chart = RomacoChart.create('#chart', {
  preset: 'dark',
  bullishColor: '#00ff88',
  bearishColor: '#ff4466',
  background: '#0a0a1a'
});

Full Theme Configuration

typescript
const chart = new RomacoChart(container, {
  theme: {
    background: '#050505',
    textColor: '#d4c5a0',
    fontFamily: 'JetBrains Mono, monospace',
    fontSize: 12,
    crosshair: {
      color: '#b8954f',
      lineStyle: 'dashed'
    },
    border: {
      color: 'rgba(42,42,42,0.6)',
      width: 1,
      radius: 0
    },
    xAxis: {
      gridColor: 'rgba(42, 42, 42, 0.6)',
      labelColor: '#888888'
    },
    yAxis: {
      gridColor: 'rgba(42, 42, 42, 0.6)',
      labelColor: '#888888'
    }
  }
});

Runtime Theme Changes

typescript
chart.setTheme({
  background: '#000000',
  textColor: '#ffffff'
});

chart.applyPreset('romaco');

Subpanels

Oscillators and volume studies automatically get dedicated panes when they don't share the main price scale.

typescript — EMA stays on main, others get subpanels
chart.addIndicator('EMA', { params: [20] });   // main pane
chart.addIndicator('MACD', { params: [12, 26, 9] }); // subpanel
chart.addIndicator('RSI', { params: [14] });       // subpanel
chart.addIndicator('VOL', { params: [5, 10] });    // subpanel

Inspect and Resize

typescript
const panels = chart.getPanels();

panels.forEach((panel) => {
  console.log(panel.id, panel.indicatorIds);
});

// Resize a panel to 30% of chart height
if (panels[0]) {
  chart.setPanelHeight(panels[0].id, 0.3);
}

Users can resize panels interactively by dragging panel separators — the chart recalculates layout and redraws immediately.

Data Conflation

Data conflation (downsampling) reduces render work while preserving visual appearance. When you have 100,000+ candles, rendering all of them is wasteful — you can't see 100K points on a 1000px canvas.

How It Works

Romaco uses the LTTB (Largest Triangle Three Buckets) algorithm:

  1. Divides data into buckets
  2. For each bucket, selects the point that forms the largest triangle with neighbors
  3. Preserves peaks, valleys, and significant price movements

Simple Mode

typescript
const chart = RomacoChart.create('#chart', {
  data: largeDataset,
  dataConflation: true // Uses LTTB, threshold 2x
});

Advanced Configuration

typescript
const chart = RomacoChart.create('#chart', {
  data: largeDataset,
  dataConflation: {
    enabled: true,
    algorithm: 'lttb',    // 'lttb' (accurate) or 'decimation' (fast)
    threshold: 2          // Downsample when points > (canvas width × threshold)
  }
});

Algorithms

AlgorithmSpeedVisual QualityBest For
lttb Fast Excellent Most cases
decimation Faster Good Real-time data

Manual Optimization

typescript
import { optimizeDataLTTB, optimizeData } from 'romaco-charts';

const optimized = optimizeDataLTTB(largeData, 1000);
const decimated = optimizeData(largeData, 1000);

chart.setData(optimized);

Performance

Data PointsWithout ConflationWith LTTB
10,000 60 FPS 60 FPS
50,000 45 FPS 60 FPS
100,000 20 FPS 60 FPS
500,000 5 FPS 60 FPS

Optimization

Romaco Charts is designed to benefit from tree-shaking, selective registration, and runtime performance controls.

Keep the Bundle Small

Core Only

typescript
import { createChart } from 'romaco-charts'

Selective Drawing Templates

typescript
import {
  trendlineTemplate,
  horizontalLineTemplate,
  rectangleTemplate,
} from 'romaco-charts/drawings'

createChart('#chart', {
  data: candleData,
  drawings: {
    templates: [trendlineTemplate, horizontalLineTemplate, rectangleTemplate],
  },
})

Selective Indicator Registration

typescript
import { RomacoChart, EMAIndicator, RSIIndicator } from 'romaco-charts'

RomacoChart.registerIndicators(EMAIndicator, RSIIndicator)

Do not register every indicator or import allTemplates unless the screen truly needs the full catalogue.

Large Dataset Controls

typescript
createChart('#chart', {
  data: candleData,
  dataConflation: true,
  renderBackend: 'auto',
  quality: 'high',
})
  • dataConflation downsamples large visible windows.
  • renderBackend: 'auto' lets the chart decide between Canvas2D and WebGPU.
  • quality trades visual fidelity for frame budget.

Developer Diagnostics

typescript
createChart('#chart', {
  developerMode: true,
  performanceMonitoring: true,
  performanceDebug: {
    enabled: true,
    position: 'top-right',
    updateIntervalMs: 500,
  },
})

Use this only for internal diagnostics. These tools are intentionally gated behind developerMode.

Practical Advice

  • Start with quality: "high" for dense dashboards.
  • Use "magnifique" only where visual polish matters more than raw frame headroom.
  • Register only the indicators and drawing templates required by the current product surface.
  • Combine selective imports with panel-aware indicator layouts so the chart is cheaper to read and cheaper to render.

Plugin System

Extend functionality with custom plugins. Hook into the render cycle, add overlays, react to events.

ChartPlugin Interface

typescript
interface ChartPlugin extends ChartPluginHooks {
  name: string;
  version?: string;
  description?: string;
}

Lifecycle Hooks

onInstall(ctx)Called once when the plugin is registered.
onBeforeRender(ctx, state)Before the chart renders each frame.
onAfterStaticRender(ctx, state)After static elements are drawn.
onAfterRender(ctx, state)After the full render cycle completes.
onDataUpdate(data)When the chart data changes.
onResize(width, height)When the canvas is resized.
onZoom(zoomLevel)When zoom level changes.
onPan(panX, panY)When pan offset changes.
onDestroy()When the plugin or chart is destroyed.

Plugin Context

typescript
interface ChartPluginContext {
  getData(): CandleData[];
  getZoomLevel(): number;
  getPan(): { x: number; y: number };
  getDimensions(): { width: number; height: number };
  priceToY(price: number): number;
  yToPrice(y: number): number;
  timestampToX(timestamp: number): number;
  xToTimestamp(x: number): number;
  requestRender(): void;
  on(event: string, callback: Function): void;
  off(event: string, callback: Function): void;
}

PluginRenderState

typescript
interface PluginRenderState {
  visibleData: CandleData[];
  startIndex: number;
  priceRange: { min: number; max: number };
  scaleY: number;
  candleWidth: number;
  panX: number;
  panY: number;
  zoomLevel: number;
  width: number;
  height: number;
}

Example Plugin

typescript
const crosshairPlugin: ChartPlugin = {
  name: 'crosshair-overlay',
  onAfterRender(ctx, state) {
    const { width, height } = state;
    ctx.strokeStyle = 'rgba(184, 149, 79, 0.4)';
    ctx.lineWidth = 1;
    ctx.setLineDash([4, 4]);

    // Draw vertical line at mouse X
    ctx.beginPath();
    ctx.moveTo(mouseX, 0);
    ctx.lineTo(mouseX, height);
    ctx.stroke();

    // Draw horizontal line at mouse Y
    ctx.beginPath();
    ctx.moveTo(0, mouseY);
    ctx.lineTo(width, mouseY);
    ctx.stroke();
  },
};

chart.installPlugin(crosshairPlugin);
03 — ADVANCED

React Integration

romaco-charts/react gives you declarative chart components, hooks, grid layouts, AI helpers, and paper trading helpers.

What the React entry exports

  • MinimalChart Lean chart-only wrapper. No auto-registered indicators. Register them explicitly before rendering.
  • Chart / RomacoChartReact Convenience wrapper that auto-registers the default React indicator set.
  • TradingTerminal Full plug-and-play terminal — TopBar, DrawingSidebar, IndicatorModal, SettingsModal, SymbolSearch built-in.
  • useRomacoChart Hook returning chart, isReady, error, setData, resize for full imperative control.
  • ChartGrid Multi-chart synchronized grid layouts.
  • useChartAgent AI agent WebSocket hook with auto-execution.
  • usePaperTrading Paper trading hook with positions, PnL and order helpers.
  • TopBar, DrawingSidebar, IndicatorModal, SettingsModal, SymbolSearchModal, IndicatorSettingsModal, PaperTradingPanelComponent Packaged UI building blocks.
  • IDatafeed, IRealtimeSubscription, HistoryRequest, HistoryResponse, ResolvedSymbol, SymbolSearchResult Datafeed contract types — re-exported from romaco-charts/react for convenience.

MinimalChart

The lean chart-only wrapper. Renders the chart surface without the default React indicator set. Register them explicitly.

tsx
import { MinimalChart, registerDefaultReactIndicators } from 'romaco-charts/react'

registerDefaultReactIndicators()

export function ChartView({ candles }: { candles: Array<any> }) {
  return <MinimalChart data={candles} preset="romaco" style={{ height: 360 }} />
}

Chart component

The default choice for React applications. Auto-registers the default React indicator set.

tsx
import { useState } from 'react'
import { Chart } from 'romaco-charts/react'
import type { RomacoChart } from 'romaco-charts'

export function TradingView({ candles }: { candles: Array<any> }) {
  const [chart, setChart] = useState<RomacoChart | null>(null)

  return (
    <div style={{ height: 520 }}>
      <Chart
        data={candles}
        preset="dark"
        indicators={['EMA', 'RSI']}
        drawings={true}
        onReady={setChart}
      />
      <button onClick={() => chart?.downloadAsPNG('snapshot.png')}>
        Export PNG
      </button>
    </div>
  )
}

Ref access

tsx
import { useRef } from 'react'
import { Chart } from 'romaco-charts/react'
import type { RomacoChartRef } from 'romaco-charts/react'

function ExportableChart({ data }: { data: Array<any> }) {
  const chartRef = useRef<RomacoChartRef>(null)

  return (
    <>
      <button onClick={() => chartRef.current?.api?.resetZoom()}>
        Reset view
      </button>
      <Chart ref={chartRef} data={data} preset="dark" />
    </>
  )
}

The ref exposes api (the RomacoChart instance) and container (the host div).

TradingTerminal

One component mounts the complete trading workspace. Pass a datafeed and everything else is handled internally.

tsx — feed-driven mode (recommended)
import { useMemo, useRef } from 'react';
import { TradingTerminal } from 'romaco-charts/react';
import type { TradingTerminalRef, IDatafeed } from 'romaco-charts/react';
import { createNoopRealtimeSubscription } from 'romaco-charts/datafeed';

export function Desk() {
  const terminalRef = useRef<TradingTerminalRef>(null);
  const datafeed: IDatafeed = useMemo(() => ({
    async searchSymbols(query) {
      const res = await fetch(`/api/symbols?q=${query}`);
      return res.json();
    },
    async resolveSymbol(symbol) {
      return { symbol, supportedResolutions: ['1m','5m','1h','1d'] };
    },
    async getHistory({ symbol, resolution }) {
      const res = await fetch(`/api/history?symbol=${symbol}&tf=${resolution}`);
      return { bars: await res.json() };
    },
    subscribeBars() {
      return createNoopRealtimeSubscription();
    },
  }), []);

  return (
    <div style={{ height: '100vh' }}>
      <TradingTerminal
        ref={terminalRef}
        symbol="AAPL"
        datafeed={datafeed}
        preset="romaco"
        onReady={(chart) => console.log('chart ready', chart)}
        onTimeframeChange={(tf) => console.log('tf', tf)}
        onSymbolChange={(sym) => console.log('sym', sym)}
        onError={(err, ctx) => console.error(ctx, err)}
      />
    </div>
  );
}

TradingTerminal Props

PropTypeDescription
data CandleDataInput[] Manual mode. Optional when datafeed is provided.
datafeed IDatafeed Feed-driven mode. Handles symbol search, history, and realtime.
symbol string Display symbol. Default 'BTC/USDT'. Reactive in feed-driven mode.
preset ThemePresetName Theme preset. Default 'dark'.
developerMode boolean Enables debug overlays and partial-history overlay.
historyBackfillTrigger 'pan' | 'pan-and-zoom' Default 'pan'. Controls what triggers loading more history.
maxBars number Caps total bars in memory across initial load and backfill.
onReady(chart) (chart) => void Fired when active chart instance is ready.
onError(error, context) (Error, 'history'|'realtime'|'search') => void Fired on datafeed failures.
onTimeframeChange(tf) (string) => void Emits canonical lowercase: 1h, 4h, 1d, 1w, 1mo.
onSymbolChange(symbol) (string) => void Fired when user changes symbol from terminal UI.

TradingTerminalRef

ref.apiActive RomacoChart instance (single chart).
ref.getActiveChart()Active chart in grid — follows focused cell, falls back to first ready.
ref.getCharts()Map<string, RomacoChart> of all charts in layout.
ref.getGrid()ChartGridRef handle when using multi-chart layout.
ref.containerHost HTMLDivElement.

useRomacoChart()

Full imperative lifecycle control over the container element.

tsx
import { useEffect, useRef } from 'react'
import { useRomacoChart } from 'romaco-charts/react'

function RealtimeChart({ stream }) {
  const containerRef = useRef<HTMLDivElement>(null)
  const { chart, isReady, error, resize } = useRomacoChart(containerRef, {
    preset: 'dark',
    indicators: ['VOL'],
    autoResize: true,
    registerCommonIndicators: true,
  })

  useEffect(() => {
    if (!chart || !isReady) return
    return stream.onCandle((bar) => chart.appendData([bar]))
  }, [chart, isReady, stream])

  return <div ref={containerRef} style={{ height: 500 }} />
}

Returns: chart, isReady, error, setData(), resize(). Options: autoResize, resizeDebounce, registerCommonIndicators.

ChartGrid

tsx
import { ChartGrid } from 'romaco-charts/react'

const cells = [
  { id: 'btc-1m', symbol: 'BTC/USDT', timeframe: '1m', data: candles1m },
  { id: 'btc-15m', symbol: 'BTC/USDT', timeframe: '15m', data: candles15m },
]

<ChartGrid
  cells={cells}
  layout="2x1"
  preset="dark"
  showHeaders={true}
  onCellTimeframeChange={(cellId, tf) => console.log(cellId, tf)}
  syncOptions={{ symbol: true, timeRange: true }}
/>

Key props: showHeaders (renders timeframe selector), onCellTimeframeChange(cellId, timeframe) (emits canonical lowercase), syncOptions, onCellFocus, onChartReady.

Default React Indicators

Chart, RomacoChartReact, and useRomacoChart auto-register: SMA, EMA, MA, RSI, MACD, BOLL, KDJ, VOL. For other studies (DMI, WR, AVWAP), register manually:

tsx
import { registerIndicators } from 'romaco-charts/react'
import { DMIIndicator, WRIndicator } from 'romaco-charts/indicators'

registerIndicators(DMIIndicator, WRIndicator)

AI and Paper Trading hooks

tsx — useChartAgent
const [chart, setChart] = useState<RomacoChart | null>(null)

const agent = useChartAgent({
  chart,
  websocketUrl: 'wss://example.com/ws/chat',
  sessionId: 'desk-01',
})
tsx — usePaperTrading
const { positions, balance, equity, stats, openLong, openShort, closePosition }
  = usePaperTrading(chart, {
    autoEnable: true,
    config: { initialBalance: 10_000 },
  })

Datafeed

The IDatafeed contract keeps market data fetching in the host app. Import types from romaco-charts/datafeed or from romaco-charts/react.

IDatafeed interface

typescript
import type {
  IDatafeed,
  HistoryRequest,
  HistoryResponse,
  IRealtimeSubscription,
  RealtimeConnectionState,
  ResolvedSymbol,
  SymbolSearchResult,
} from 'romaco-charts/react'; // or 'romaco-charts/datafeed'

interface IDatafeed<TBar = CandleDataInput> {
  searchSymbols(query: string, signal?: AbortSignal): Promise<SymbolSearchResult[]>
  resolveSymbol(symbol: string, signal?: AbortSignal): Promise<ResolvedSymbol>
  getHistory(request: HistoryRequest, signal?: AbortSignal): Promise<HistoryResponse<TBar>>
  subscribeBars(
    symbol: string,
    resolution: string,
    onBar: (bar: TBar) => void,
    onStatusChange?: (state: RealtimeConnectionState) => void
  ): IRealtimeSubscription
}

Related Types

typescript
type RealtimeConnectionState = 'connected' | 'disconnected' | 'stale'

interface SymbolSearchResult {
  symbol: string
  name: string
  exchange?: string
  type?: 'crypto' | 'stock' | 'forex' | 'futures' | 'index'
}

interface ResolvedSymbol {
  symbol: string
  priceScale?: number
  timezone?: string
  session?: string
  sessionMetadata?: {
    timezone?: string
    sessions?: Array<{
      id: string
      label?: string
      start: string
      end: string
      days?: number[]
      color?: string
    }>
  }
  supportedResolutions: string[]
}

interface HistoryRequest {
  symbol: string
  resolution: string
  from: number
  to: number
  cursor?: string
  limit?: number
}

interface HistoryResponse<TBar = CandleDataInput> {
  bars: TBar[]
  nextCursor?: string
  partial?: boolean
}

interface IRealtimeSubscription {
  unsubscribe(): void
  readonly closed?: boolean
}

supportedResolutions can use the host's native tokens. If your backend prefers 60 instead of 1h, advertise 60 there; TradingTerminal normalizes requested timeframe values and picks the closest supported resolution when an exact match is unavailable.

Use nextCursor when your data service supports cursor-based pagination. TradingTerminal consumes it automatically for left-edge history backfill.

Return partial: true when the loaded window is intentionally incomplete. The chart keeps that signal internal by default and only surfaces the partial-history overlay when developerMode is enabled.

Minimal implementation

typescript
import type { IDatafeed } from 'romaco-charts/react';
import { createNoopRealtimeSubscription } from 'romaco-charts/datafeed';

export const datafeed: IDatafeed = {
  async searchSymbols(query) {
    const res = await fetch(`/api/symbols?q=${encodeURIComponent(query)}`);
    return res.json();
  },

  async resolveSymbol(symbol) {
    const res = await fetch(`/api/symbols/${encodeURIComponent(symbol)}`);
    return res.json();
  },

  async getHistory(request) {
    const res = await fetch('/api/history', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(request),
    });
    return res.json();
  },

  subscribeBars(symbol, resolution, onBar, onStatusChange) {
    const ws = new WebSocket(`wss://example.com/stream?symbol=${symbol}&resolution=${resolution}`);

    ws.onopen = () => onStatusChange?.('connected');
    ws.onclose = () => onStatusChange?.('disconnected');
    ws.onerror = () => onStatusChange?.('stale');
    ws.onmessage = (event) => onBar(JSON.parse(event.data));

    return {
      unsubscribe() {
        ws.close();
      },
      get closed() {
        return ws.readyState === WebSocket.CLOSED;
      },
    };
  },
};

Wiring a chart manually

setDatafeed() only injects the contract. It does not fetch automatically. This manual flow applies to the core chart API. TradingTerminal automates symbol resolution, history loading, backfill, and realtime subscriptions when you pass datafeed directly.

typescript
import { createChart } from 'romaco-charts'

const chart = createChart('#chart', { preset: 'dark' })
chart.setDatafeed(datafeed)

const symbol = await datafeed.resolveSymbol('BTCUSDT')
chart.setSymbol(symbol.symbol)

const history = await datafeed.getHistory({
  symbol: symbol.symbol,
  resolution: '1h',
  from: 1717000000,
  to: 1717600000,
})

chart.setData(history.bars)

const sub = datafeed.subscribeBars(symbol.symbol, '1h', (bar) => {
  chart.updateData(bar)
})

window.addEventListener('beforeunload', () => sub.unsubscribe(), { once: true })

No-op subscriptions

typescript
import { createNoopRealtimeSubscription } from 'romaco-charts/datafeed';

subscribeBars() {
  return createNoopRealtimeSubscription();
}

Design guidance

  • Keep symbol search and history fetching in your host app or data service.
  • Let the chart own rendering, view state, and realtime candle updates.
  • Use ResolvedSymbol.sessionMetadata when your UI needs session windows or timezone-aware overlays.

AI Integration

Use romaco-charts/agent when you want an LLM, rules engine, or remote agent service to reason about the live chart state and return structured actions.

ChartAgentController

typescript
import { ChartAgentController } from 'romaco-charts/agent';

const controller = new ChartAgentController(chart);

controller.getChartContext();        // snapshot of runtime state
controller.getChartCapabilities();   // available actions and flags
controller.executeAction({ action: 'addIndicator', indicatorType: 'EMA', params: [20] });
controller.executeActions([{ action: 'setRenderQuality', quality: 'high' }]);
controller.exportSnapshot({ includeSubpanels: true, format: 'png' });
controller.saveChartState();
controller.loadChartState(savedState);

ChartContext — What it contains

This is the object you send to your agent when you want the model to reason about the current chart state.

visibleRange and visibleCandles
existingDrawings and existingIndicators
chartDimensions, currentPrice, zoomLevel, pan
locale, timezone, renderBackend, renderQuality
availableDrawingTypes and availableIndicatorTypes
Subpanel summaries, alerts, paper trading state
Performance metrics and runtime capability flags

Available Actions

CategoryActions
Drawings addDrawing, removeDrawing, clearDrawings
Indicators addIndicator, removeIndicator, setIndicatorVisibility
View setZoom, zoomIn, zoomOut, resetView, scrollTo, setAutoFit
Styling setTheme, setCandleStyle, setRenderQuality, highlightCandles
Paper Trading enablePaperTrading, disablePaperTrading, openPaperLong, openPaperShort, closePaperPosition, closeAllPaperPositions
Persistence loadChartState, addAlert, updateAlert, removeAlert, clearAlerts

Transport Contract

typescript
interface AgentRequest {
  requestId?: string
  protocolVersion?: number
  dryRun?: boolean
  type: 'chat_message' | 'context_update' | 'snapshot'
  sessionId: string
  message?: string
  chartContext?: ChartContext
  snapshot?: string
}

interface AgentResponse {
  requestId?: string
  protocolVersion?: number
  dryRun?: boolean
  type: 'chat_response' | 'action_batch' | 'error'
  sessionId: string
  textResponse?: string
  chartActions?: ChartAction[]
  actionResults?: ActionResult[]
  executedChartActions?: ChartAction[]
  pendingChartActions?: ChartAction[]
  capabilities?: ChartCapabilities
  error?: string
}

useChartAgent()

The React hook handles WebSocket lifecycle, chart context capture, optional auto-execution, and exponential reconnect.

tsx
import { useState } from 'react'
import { Chart, useChartAgent } from 'romaco-charts/react'
import type { RomacoChart } from 'romaco-charts'

function AgentTerminal({ data }: { data: Array<any> }) {
  const [chart, setChart] = useState<RomacoChart | null>(null)

  const {
    sendMessage,
    sendSnapshot,
    executeActions,
    isConnected,
    isLoading,
    lastResponse,
  } = useChartAgent({
    chart,
    websocketUrl: 'wss://example.com/ws/chat',
    sessionId: 'desk-01',
    autoExecuteActions: false,
  })

  return (
    <>
      <Chart data={data} preset="dark" onReady={setChart} />
      <button disabled={!isConnected || isLoading} onClick={() => sendMessage('Add RSI and a trendline')}>
        Ask AI
      </button>
      <button onClick={() => executeActions(lastResponse?.pendingChartActions)}>
        Apply pending actions
      </button>
    </>
  )
}

Paper Trading

Built-in paper trading with virtual positions, PnL tracking, and chart-linked order visualization.

Enable

typescript
import { createChart } from 'romaco-charts'

const chart = createChart('#chart', { preset: 'dark' })

const manager = chart.enablePaperTrading({
  initialBalance: 10_000,
  defaultSymbol: 'BTC/USDT',
  commissionRate: 0.001,
})

Open and Close Positions

typescript
chart.openLong(0.1, 40_000, 45_000);
chart.openShort(0.2, 44_000, 39_500);

chart.openPosition({
  side: 'long',
  quantity: 1,
  stopLoss: 41_000,
  takeProfit: 47_000,
});

chart.closePosition(positionId);

Events and Manager Access

typescript
manager.on('positionOpened', (p) => console.log(p.id));
manager.on('positionClosed', (p) => console.log(p.realizedPnL));
manager.on('stopLossHit',    (p) => console.log(p.symbol));
manager.on('takeProfitHit',  (p) => console.log(p.symbol));

// Recover later
chart.getPaperTradingManager();
chart.isPaperTradingEnabled();

Disable Overlay and Drive Your Own UI

typescript
chart.enablePaperTrading({
  initialBalance: 10_000,
  renderOverlay: false,
});

React hook — usePaperTrading

tsx
import { usePaperTrading } from 'romaco-charts/react'

function TradingPanel({ chart }) {
  const {
    positions,
    balance,
    equity,
    stats,        // winRate, avgWin, avgLoss, profitFactor, drawdown
    openLong,
    openShort,
    closePosition,
  } = usePaperTrading(chart, {
    autoEnable: true,
    config: { initialBalance: 10_000 },
  })

  return (
    <div>
      <p>Balance: ${balance} · Equity: ${equity}</p>
      <p>Win rate: {stats.winRate}%</p>
      <button onClick={() => openLong(0.1)}>Long</button>
      <button onClick={() => openShort(0.1)}>Short</button>
      {positions.map((p) => (
        <button key={p.id} onClick={() => closePosition(p.id)}>
          Close {p.id}
        </button>
      ))}
    </div>
  )
}

Real-World Integration

End-to-end example showing how to wire TradingTerminal to a real production stack: REST API for history, WebSocket for realtime, full lifecycle management, and proper cleanup.

Stack assumed

  • React 18+ frontend
  • REST endpoint that returns { bars, nextCursor?, partial? }
  • WebSocket endpoint that pushes price ticks
  • Auth via JWT (Supabase, Auth0, custom — doesn't matter)

Architecture

architecture
React component
  └─ TradingTerminal (datafeed={datafeed})
        └─ createDatafeed() — IDatafeed adapter
              ├─ REST SDK (/history)
              └─ WebSocket layer (ws://.../prices)

1. The datafeed adapter

typescript — src/lib/chartDatafeed.ts
import type { IDatafeed } from 'romaco-charts/react'

const SUPPORTED_RESOLUTIONS = ['1m', '5m', '15m', '30m', '1h', '4h', '1d', '1w', '1mo']

function isAbortError(error: unknown): boolean {
  return Boolean(error && typeof error === 'object' && 'name' in error && error.name === 'AbortError')
}

function normalizeError(error: unknown, fallback: string): Error {
  if (error instanceof Error) return error
  if (typeof error === 'string' && error.trim()) return new Error(error)
  return new Error(fallback)
}

export function createDatafeed(): IDatafeed {
  return {
    async searchSymbols(query, signal) {
      if (!query || query.length < 2) return []
      try {
        const res = await fetch(`/api/symbols?q=${query}`, { signal })
        const results = await res.json()
        return (results ?? []).map((r: any) => ({
          symbol: r.symbol,
          name: r.name ?? r.symbol,
          type: r.type ?? 'stock',
        }))
      } catch (error) {
        if (isAbortError(error)) throw error
        return []
      }
    },

    async resolveSymbol(symbol) {
      return { symbol, supportedResolutions: SUPPORTED_RESOLUTIONS }
    },

    async getHistory(request, signal) {
      const { symbol, resolution, from, to, cursor, limit } = request

      const toMs = (t: number) => (t > 1e12 ? t : t * 1000)
      const startDate = from ? new Date(toMs(from)).toISOString() : undefined
      const endDate = to ? new Date(toMs(to)).toISOString() : undefined

      try {
        const res = await fetch('/api/history', {
          method: 'POST',
          headers: { 'content-type': 'application/json' },
          body: JSON.stringify({ symbol, resolution, startDate, endDate, cursor, limit }),
          signal,
        })
        const raw = await res.json()

        if (!raw || !Array.isArray(raw.bars)) {
          throw new Error(`Invalid history response for ${symbol} (${resolution})`)
        }

        const bars = raw.bars.map((d: any) => ({
          time: new Date(d.date).getTime(),
          open: Number(d.open),
          high: Number(d.high),
          low: Number(d.low),
          close: Number(d.close),
          volume: Number(d.volume ?? 0),
        }))

        return {
          bars,
          nextCursor: raw.nextCursor,
          partial: Boolean(raw.partial),
        }
      } catch (error) {
        if (isAbortError(error)) throw error
        throw normalizeError(error, `Failed to load history for ${symbol} (${resolution})`)
      }
    },

    subscribeBars(symbol, resolution, onBar, onStatusChange) {
      const normalizedSymbol = symbol.trim().toUpperCase()
      let closed = false

      const ws = new WebSocket(`wss://example.com/stream?symbol=${symbol}&resolution=${resolution}`)

      ws.onopen = () => { if (!closed) onStatusChange?.('connected') }
      ws.onclose = () => { if (!closed) onStatusChange?.('disconnected') }
      ws.onerror = () => { if (!closed) onStatusChange?.('stale') }
      ws.onmessage = (event) => {
        if (closed) return
        onBar(JSON.parse(event.data))
      }

      return {
        get closed() { return closed },
        unsubscribe() {
          if (closed) return
          closed = true
          ws.close()
        },
      }
    },
  }
}

2. The React component

tsx — src/components/ChartWidget.tsx
'use client'

import { useMemo, useRef } from 'react'
import { TradingTerminal } from 'romaco-charts/react'
import type { TradingTerminalRef } from 'romaco-charts/react'
import { createDatafeed } from '../lib/chartDatafeed'

interface ChartWidgetProps {
  symbol: string
  onSymbolChange?: (symbol: string) => void
  onTimeframeChange?: (timeframe: string) => void
  className?: string
}

export function ChartWidget({ symbol, onSymbolChange, onTimeframeChange, className = '' }: ChartWidgetProps) {
  const terminalRef = useRef<TradingTerminalRef>(null)
  const datafeed = useMemo(() => createDatafeed(), [])

  return (
    <div className={`w-full h-full flex flex-col overflow-hidden ${className}`}>
      <TradingTerminal
        ref={terminalRef}
        symbol={symbol}
        datafeed={datafeed}
        preset="romaco"
        onSymbolChange={onSymbolChange}
        onTimeframeChange={onTimeframeChange}
        onError={(error, context) => {
          console.error(`[chart:${context}]`, error.message)
        }}
        historyBackfillTrigger="pan"
        // maxBars={5000}
      />
    </div>
  )
}

3. Mounting in your app

tsx
import { useState } from 'react'
import { ChartWidget } from './components/ChartWidget'

export function Dashboard() {
  const [symbol, setSymbol] = useState('AAPL')

  return (
    <div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
      <header style={{ padding: 12 }}>
        <h1>Active: {symbol}</h1>
      </header>
      <main style={{ flex: 1, minHeight: 0 }}>
        <ChartWidget symbol={symbol} onSymbolChange={setSymbol} />
      </main>
    </div>
  )
}

minHeight: 0 on the flex item is the canonical fix for charts disappearing inside a flex container.

4. Next.js / SSR

tsx — App Router
'use client' // Required — TradingTerminal touches document, window, and DOM canvas
tsx — Pages Router or strict SSR
import dynamic from 'next/dynamic'

const ChartWidget = dynamic(
  () => import('./ChartWidget').then((m) => m.ChartWidget),
  { ssr: false, loading: () => <div style={{ height: '100%' }}>Loading chart…</div> }
)

5. Error handling pattern

typescript
function handleChartError(error: Error, context: 'history' | 'realtime' | 'search') {
  switch (context) {
    case 'history':
      toast.error(`Could not load chart history: ${error.message}`)
      break
    case 'realtime':
      toast.warn('Realtime feed disconnected, retrying…')
      break
    case 'search':
      console.debug('[chart:search]', error)
      break
  }
}

6. Authentication

The datafeed runs in your app context — it has access to whatever auth state you maintain. The chart does not need an auth API.

typescript
async function fetchPriceHistory(symbol, resolution, opts) {
  const token = await getAccessToken()
  const res = await fetch(`/api/history?symbol=${symbol}&resolution=${resolution}`, {
    headers: { Authorization: `Bearer ${token}` },
    signal: opts.signal,
  })
  if (!res.ok) throw new Error(`History request failed: ${res.status}`)
  return res.json()
}

7. History backfill (infinite scroll)

If your backend supports cursor pagination, TradingTerminal automatically loads more history as the user drags the chart left. Return nextCursor from getHistory.

typescript
async getHistory(request, signal) {
  const response = await fetch('/api/history', {
    method: 'POST',
    body: JSON.stringify(request),
    signal,
  })
  const data = await response.json()
  return {
    bars: data.bars,
    nextCursor: data.next_cursor,  // chart passes this back on next backfill request
  }
}

Backfill trigger

ValueBehavior
'pan' (default) Loads more history on left-drag only. Matches TradingView behavior.
'pan-and-zoom' Also triggers when zoom-out pushes the left edge into view.

maxBars tuning

TimeframemaxBars
Daily / weekly omit (no limit)
1h 3,000–5,000
15m / 5m 5,000–10,000
1m 2,000–5,000

8. Common pitfalls

SymptomCauseFix
Chart renders blank, no errors Container has no height Wrap it in a container with explicit height or use flex with minHeight: 0
Real-time bar jumps off chart Timestamp not aligned to resolution Apply intervalFloor in both getHistory and subscribeBars
Subscription fires after unmount Missing closed flag check Set closed = true in unsubscribe() and check it in every async callback
Datafeed recreated every render createDatafeed() called in component body Wrap in useMemo(() => createDatafeed(), [])
ReferenceError: document is not defined SSR rendering Use 'use client' or dynamic(..., { ssr: false })
Two candles at same timestamp Backend returns overlapping bars on pagination Deduplicate by timestamp in getHistory before returning
Resolution mismatch with backend Backend uses 1H/60/1hour format Advertise those values in resolveSymbol().supportedResolutions
05 — API REFERENCE

Chart Options

Romaco exposes two main configuration layers. Use the simplified config for presets and a shorter setup path. Use the full config when you want direct control over the runtime surface.

SimpleChartConfig

typescript
interface SimpleChartConfig {
  preset?: 'dark' | 'light' | 'bloomberg' | 'neon' | 'professional' | 'classic' | 'romaco'
  data?: CandleDataInput[]
  width?: number
  height?: number

  background?: string
  bullishColor?: string
  bearishColor?: string
  wickColor?: string
  gridColor?: string
  textColor?: string

  enableZoom?: boolean
  enablePanning?: boolean
  autoFit?: boolean
  touchpadOptions?: {
    naturalScrolling?: boolean
    pinchZoomSensitivity?: number
    enableHorizontalScroll?: boolean
    scrollSensitivity?: number
  }

  drawings?: boolean | DrawingsConfig
  indicators?: Array<string | IndicatorConfig>

  renderBackend?: 'auto' | 'canvas2d' | 'webgpu'
  quality?: 'low' | 'high' | 'magnifique'
  developerMode?: boolean
  performanceMonitoring?: boolean
  performanceDebug?: boolean | PerformanceDebugOptions
  dataConflation?: boolean
  locale?: string
  host?: ChartHostContracts

  onCandleClick?: (candle: CandleData, index: number) => void
  onZoom?: (zoomLevel: number) => void
  onPan?: (panX: number, panY: number) => void
  onCrosshairMove?: (candle: CandleData | null, x: number, y: number) => void
}

ChartOptions

typescript
interface ChartOptions {
  width?: number
  height?: number
  theme?: ChartTheme
  candleStyle?: CandleStyle

  enableZoom?: boolean
  enablePanning?: boolean
  touchpadOptions?: { /* same as SimpleChartConfig */ }

  candleRenderer?: CandleRendererInterface
  minCandleWidth?: number
  maxCandleWidth?: number
  priceScaleWidth?: number
  timeScaleHeight?: number
  resizablePriceScale?: boolean
  resizableTimeScale?: boolean
  margins?: [number, number, number, number]
  watermark?: Watermark
  showPriceScale?: boolean
  showTimeScale?: boolean

  autoFitEnabled?: boolean
  autoFitPadding?: number

  onCandleClick?: (candle: CandleData, index: number) => void
  onZoom?: (zoomLevel: number) => void
  onPan?: (panX: number, panY: number) => void
  onCrosshairMove?: (candle: CandleData | null, x: number, y: number) => void

  renderQuality?: 'low' | 'high' | 'magnifique'
  developerMode?: boolean
  performanceMonitoring?: boolean
  performanceDebug?: boolean | PerformanceDebugOptions
  dataConflation?: boolean | DataConflationConfig
  renderBackend?: 'auto' | 'canvas2d' | 'webgpu'
}

Confirmed Constructor Defaults

OptionDefault
width 800
height 400
enableZoom true
enablePanning true
minCandleWidth 1
maxCandleWidth 50
margins [10, 10, 10, 10]
autoFitEnabled true
autoFitPadding 0.05
priceScaleWidth 80
timeScaleHeight 40
renderQuality 'magnifique'
renderBackend 'auto'
theme.background '#ffffff'
theme.textColor '#000000'
theme.fontFamily 'Arial'
theme.fontSize 12
candleStyle.bullishColor '#00ff00'
candleStyle.bearishColor '#ff0000'
candleStyle.borderColor '#000000'
candleStyle.borderWidth 1
candleStyle.opacity 1

renderQuality Levels

LevelMeaning
low Favor speed over fidelity.
high Balanced quality and performance.
magnifique Highest visual quality. This is the default.

renderBackend

  • 'auto': lets the chart decide between Canvas2D and WebGPU.
  • 'canvas2d': forces Canvas2D rendering.
  • 'webgpu': requests the WebGPU renderer.

Key Types

typescript
interface CandleData {
  time: number
  timestamp: number
  open: number
  high: number
  low: number
  close: number
  volume?: number
}

type CandleDataInput =
  | { time: number; timestamp?: number; open: number; high: number; low: number; close: number; volume?: number }
  | { timestamp: number; time?: number; open: number; high: number; low: number; close: number; volume?: number }

interface ChartTheme {
  background?: string | string[]
  textColor?: string
  fontFamily?: string
  fontSize?: number
  crosshair?: CrosshairOptions
  border?: { color?: string; width?: number; radius?: number }
  xAxis?: AxisOptions
  yAxis?: AxisOptions
}

interface CandleStyle {
  bullishColor?: string
  bearishColor?: string
  borderColor?: string
  borderWidth?: number
  wickColor?: string
  opacity?: number
}

interface DrawingsConfig {
  enabled?: boolean
  position?: 'left' | 'right'
  templates?: DrawingTemplate[]
}

interface IndicatorConfig {
  name: string
  params?: number[]
  styles?: Record<string, unknown>
}

RomacoChart Class

The low-level class behind createChart() and the React wrapper. Use it when you need direct runtime control.

Pick the right entry point

  • createChart() — shortest vanilla setup.
  • RomacoChart.create() — same simplified config from the class.
  • new RomacoChart() — full ChartOptions surface.

Static API

RomacoChart.create(container, config?)Factory with SimpleChartConfig.
RomacoChart.registerIndicators(...ctors)Register indicator classes for later use.
RomacoChart.registerIndicator(name, ctor)Register a single indicator under a specific name.
RomacoChart.getRegisteredIndicators()Returns currently registered indicator names.
RomacoChart.getAvailableQualityLevels()Returns supported render quality presets.

Constructor

typescript
new RomacoChart(container: HTMLElement, options?: ChartOptions)

Data API

setData(data)Replace the full series.
appendData(candles)Preferred API for small realtime batches.
updateData(candle)Updates last candle when timestamp matches, appends otherwise.
getData()Returns the current CandleData[].
setDatafeed(datafeed | null)Inject the host-provided data contract. Does not fetch automatically.
getDatafeed()Returns the current IDatafeed or null.
setSymbol(symbol)Set the display symbol.

View and Interaction

setZoom(level)Set zoom level directly.
getZoom()Get current zoom level.
zoomIn(factor?)Zoom in by optional factor.
zoomOut(factor?)Zoom out by optional factor.
setPan(panX, panY?)Set pan offset.
getPan()Get current pan {x, y}.
resetZoom()Reset zoom to default.
setAutoFit(enabled)Toggle auto-fit behavior.
isAutoFitEnabled()Check auto-fit state.
resize(width?, height?)Force canvas resize.
destroy()Teardown the chart and release resources.

Indicators and Panels

addIndicator(name, options?)Returns runtime indicator id.
updateIndicator(id, options)Update params or styles.
removeIndicator(id)Remove indicator by id.
getIndicator(id)Get indicator instance or null.
getIndicatorManager()Get the IndicatorManager.
setIndicatorVisible(id, visible)Show/hide indicator.
toggleIndicatorVisible(id)Toggle indicator visibility.
getIndicatorVisibility(id)Check visibility state.
getPanels()Get all subpanels.
setPanelHeight(panelId, ratio)Resize panel by ratio (0-1).

Drawings

getDrawingManager()Get DrawingManager or null.
getDrawingToolbar()Get DrawingToolbar or null.
setDrawingToolbarVisibilityMode(mode)Set toolbar visibility mode.
setDrawingLineColor(color)Set default drawing line color.
setDrawingLineWidth(width)Set default drawing line width.
setDrawingStyles(styles)Set default drawing styles.
updateDrawingStyle(drawingId, styles)Update specific drawing styles.

Theme and Runtime Controls

setTheme(theme)Apply partial theme override.
applyPreset(name)Apply a built-in preset.
setCandleStyle(style)Override candle appearance.
setRenderer(renderer)Switch renderer at runtime.
setRenderQuality(quality)'low' | 'high' | 'magnifique'.
getRenderQuality()Get current render quality.
setRenderBackend(option)Async: 'auto' | 'canvas2d' | 'webgpu'.
getRenderBackendOption()Get requested backend.
getActiveRenderBackendType()'canvas2d' | 'webgpu'.
setDeveloperMode(enabled)Toggle developer mode.
isDeveloperModeEnabled()Check developer mode.

Alerts

getAlertManager()Get AlertManager.
addAlert(price, options?)Create a price alert.
removeAlert(id)Remove alert by id.
getAlert(id)Get alert by id.
getAlerts()Get all alerts.
clearAlerts()Remove all alerts.

State and Paper Trading

saveState()Returns PersistableChartState.
loadState(state, options?)Restore chart state.
enablePaperTrading(config?)Enable paper trading.
disablePaperTrading()Disable paper trading.
getPaperTradingManager()Get PaperTradingManager or null.
isPaperTradingEnabled()Check if paper trading is active.
openLong(qty, sl?, tp?)Open long position.
openShort(qty, sl?, tp?)Open short position.
openPosition(params)Open position with full params.
closePosition(positionId)Close position by id.

Public Types

Main exported contracts outside the RomacoChart class. Use this as the map for subpath imports.

Agent Protocol

Import from romaco-charts/agent.

typescript
interface ChartContext {
  visibleRange: VisibleRange
  visibleCandles: CandleData[]
  existingDrawings: DrawingSummary[]
  existingIndicators: IndicatorSummary[]
  chartDimensions: { width: number; height: number }
  currentPrice?: number
  currentTheme?: string
  pan?: { x: number; y: number }
  locale?: string
  timezone?: string
  renderBackend?: 'canvas2d' | 'webgpu'
  renderQuality?: RenderQuality
  availableDrawingTypes?: string[]
  availableIndicatorTypes?: string[]
  activePlugins?: string[]
  panels?: PanelSummary[]
  capabilities?: ChartCapabilities
  paperTrading?: PaperTradingSummary | null
  alerts?: AlertSummary[]
  performance?: PerformanceSummary
  totalCandles: number
  zoomLevel: number
}

type ChartAction =
  | { action: 'addDrawing'; drawingType: string; points: Point[]; styles?: Record<string, unknown> }
  | { action: 'removeDrawing'; drawingId: string }
  | { action: 'clearDrawings' }
  | { action: 'addIndicator'; indicatorType: string; params?: number[]; styles?: Record<string, unknown> }
  | { action: 'removeIndicator'; indicatorId: string }
  | { action: 'setIndicatorVisibility'; indicatorId: string; visible: boolean }
  | { action: 'setTheme'; theme: string }
  | { action: 'setCandleStyle'; style: Partial<CandleStyle> }
  | { action: 'setZoom'; zoomLevel: number }
  | { action: 'zoomIn'; factor?: number }
  | { action: 'zoomOut'; factor?: number }
  | { action: 'resetView' }
  | { action: 'scrollTo'; timestamp: number }
  | { action: 'setAutoFit'; enabled: boolean }
  | { action: 'setRenderQuality'; quality: 'low' | 'high' | 'magnifique' }
  | { action: 'highlightCandles'; timestamps: number[] }
  | { action: 'enablePaperTrading'; config?: PaperTradingConfig }
  | { action: 'disablePaperTrading' }
  | { action: 'openPaperPosition'; params: OpenPositionParams }
  | { action: 'openPaperLong'; quantity: number; stopLoss?: number; takeProfit?: number }
  | { action: 'openPaperShort'; quantity: number; stopLoss?: number; takeProfit?: number }
  | { action: 'closePaperPosition'; positionId: string }
  | { action: 'closeAllPaperPositions' }
  | { action: 'loadChartState'; state: PersistableChartState }
  | { action: 'addAlert'; price: number; options?: CreateAlertOptions }
  | { action: 'updateAlert'; alertId: string; options?: Partial<CreateAlertOptions> }
  | { action: 'removeAlert'; alertId: string }
  | { action: 'clearAlerts' }

interface AgentRequest {
  requestId?: string
  protocolVersion?: number
  dryRun?: boolean
  type: 'chat_message' | 'context_update' | 'snapshot'
  sessionId: string
  message?: string
  chartContext?: ChartContext
  snapshot?: string
}

interface AgentResponse {
  requestId?: string
  protocolVersion?: number
  dryRun?: boolean
  type: 'chat_response' | 'action_batch' | 'error'
  sessionId: string
  textResponse?: string
  chartActions?: ChartAction[]
  actionResults?: ActionResult[]
  executedChartActions?: ChartAction[]
  pendingChartActions?: ChartAction[]
  capabilities?: ChartCapabilities
  error?: string
}

Datafeed Types

Import from romaco-charts/datafeed or romaco-charts/react.

typescript
type RealtimeConnectionState = 'connected' | 'disconnected' | 'stale'

interface SymbolSearchResult {
  symbol: string
  name: string
  exchange?: string
  type?: 'crypto' | 'stock' | 'forex' | 'futures' | 'index'
}

interface ResolvedSymbol {
  symbol: string
  priceScale?: number
  timezone?: string
  session?: string
  sessionMetadata?: ResolvedSessionMetadata
  supportedResolutions: string[]
}

interface HistoryRequest {
  symbol: string
  resolution: string
  from: number
  to: number
  cursor?: string
  limit?: number
}

interface HistoryResponse<TBar = CandleDataInput> {
  bars: TBar[]
  nextCursor?: string
  partial?: boolean
}

interface IRealtimeSubscription {
  unsubscribe(): void
  readonly closed?: boolean
}

interface IDatafeed<TBar = CandleDataInput> {
  searchSymbols(query: string, signal?: AbortSignal): Promise<SymbolSearchResult[]>
  resolveSymbol(symbol: string, signal?: AbortSignal): Promise<ResolvedSymbol>
  getHistory(request: HistoryRequest, signal?: AbortSignal): Promise<HistoryResponse<TBar>>
  subscribeBars(
    symbol: string,
    resolution: string,
    onBar: (bar: TBar) => void,
    onStatusChange?: (state: RealtimeConnectionState) => void
  ): IRealtimeSubscription
}

Replay Types

Import from romaco-charts/replay.

typescript
type ReplayMode = 'idle' | 'playing' | 'paused' | 'ended'

interface ReplayStatus {
  mode: ReplayMode
  cursor: number
  speed: number
  totalBars: number
}

interface IReplayController<TBar = CandleDataInput> {
  loadBars(bars: TBar[]): void
  play(speed?: number): void
  pause(): void
  reset(): void
  stepForward(count?: number): void
  seek(index: number): void
  getStatus(): ReplayStatus
  onStatusChange(listener: (status: ReplayStatus) => void): () => void
  onTick(listener: (tick: ReplayTick<TBar>, status: ReplayStatus) => void): () => void
  exportState(): ReplaySessionState<TBar> | null
  restoreState(state: ReplaySessionState<TBar> | null): void
  destroy(): void
}

Persistence / Profile Types

Import from romaco-charts/profile.

typescript
type VisualStateKind =
  | 'empty'
  | 'loading'
  | 'ready'
  | 'partial-history'
  | 'disconnected'
  | 'error'
  | 'replay'

interface ChartStateLoadOptions {
  restorePreferences?: boolean
  restoreIndicators?: boolean
  restoreDrawings?: boolean
  restoreTheme?: boolean
  restoreViewport?: boolean
  restoreMetadata?: boolean
  restorePanels?: boolean
  restoreVisualState?: boolean
  restoreReplay?: boolean
}

interface IChartStateApi {
  saveState(): PersistableChartState
  loadState(state: PersistableChartState, options?: ChartStateLoadOptions): void
}

React-Facing Types

Import from romaco-charts/react.

RomacoChartPropsChart component props.
RomacoChartRefChart component ref shape.
MinimalChartPropsMinimalChart component props.
MinimalChartRefMinimalChart component ref shape.
TradingTerminalPropsTradingTerminal component props.
TradingTerminalRefTradingTerminal component ref shape.
UseRomacoChartConfiguseRomacoChart hook options.
UseRomacoChartResultuseRomacoChart hook return.
UseChartAgentOptionsuseChartAgent hook options.
UseChartAgentReturnuseChartAgent hook return.
UsePaperTradingOptionsusePaperTrading hook options.
UsePaperTradingResultusePaperTrading hook return.
ChartGridPropsChartGrid component props.
ChartGridRefChartGrid component ref shape.
GridLayoutGrid layout type.
ChartCellConfigGrid cell configuration.
SyncOptionsGrid synchronization options.

The following datafeed types are also re-exported from romaco-charts/react for convenience:

  • IDatafeed
  • IRealtimeSubscription
  • HistoryRequest
  • HistoryResponse
  • ResolvedSymbol
  • SymbolSearchResult

Examples

Simple Candlestick Chart

typescript
import { RomacoChart } from 'romaco-charts';

const chart = RomacoChart.create('#chart', {
  preset: 'dark',
  data: candleData
});

With Indicators

typescript
import { RomacoChart, EMAIndicator, RSIIndicator } from 'romaco-charts';

RomacoChart.registerIndicators(EMAIndicator, RSIIndicator);

const chart = RomacoChart.create('#chart', {
  preset: 'dark',
  data: candleData,
  indicators: ['EMA', 'RSI']
});

With Drawing Tools

typescript
import { RomacoChart, allTemplates } from 'romaco-charts';

const chart = RomacoChart.create('#chart', {
  preset: 'dark',
  data: candleData,
  drawings: {
    templates: allTemplates
  }
});

React TradingTerminal

tsx
import { TradingTerminal } from 'romaco-charts/react';

export function App() {
  return (
    <div style={{ height: '100vh' }}>
      <TradingTerminal
        data={candles}
        symbol="BTC/USDT"
        preset="romaco"
      />
    </div>
  );
}
06 — REFERENCE

Changelog

v1.0.0-beta.6 2026-05

Breaking

Timeframe callback values now canonicalized to lowercase: 1h, 4h, 1d, 1w, 1mo. onTimeframeChange and terminal state emit lowercase tokens. Update comparisons from 1H to 1h and 1M to 1mo.

Added

TradingTerminal now supports automatic history backfill when IDatafeed.getHistory() returns nextCursor. Older bars load as the user scrolls left; viewport pan is preserved while bars are prepended.
External symbol prop on TradingTerminal is now reactive. Changing it after mount swaps the active symbol, reloads history, and re-subscribes realtime.
TradingTerminal now supports historyBackfillTrigger (pan or pan-and-zoom) and maxBars to control backfill behavior and total in-memory history.
Timeframe handling is now centralized internally so normalization, labels, seconds conversion, and supported-resolution fallback behave consistently across shipped surfaces.
resolveSupportedTimeframe matches requested resolution against ResolvedSymbol.supportedResolutions, picking the closest supported value when exact match is unavailable.
developerMode prop now gates the partial-history visual overlay. Hidden by default; only appears when developerMode={true}.
ChartGrid cells now expose a compact timeframe dropdown in the header when timeframe is provided, and onCellTimeframeChange emits canonical lowercase tokens.
Drawing sidebar and toolbar refreshed with normalized styles and improved edge-hover behavior.

Fixed

Monthly timeframe handling now resolves correctly for 1M and 1mo instead of silently falling back to one hour.
MinimalChart and useRomacoChart no longer bootstrap with a forced empty dataset when data is undefined or null.
Removed redundant console.warn on RomacoChart.setData([]). Empty arrays treated as intentional clears.
TopBar now keeps a display label distinct from the canonical timeframe value so custom timeframes survive round-tripping through callbacks.
Chart bootstrap race condition removed; explicit drawings-only state restore helper added.
Terminal loading flow stabilized to prevent duplicate history requests and subscription churn on rapid symbol changes.
Grid UI aligned with the romaco theme for consistent colors, borders, and typography across multi-chart layouts.
Zoom now stays focused on the pointer instead of drifting away from the cursor during wheel zoom.
Touchpad interactions corrected for smoother panning, pinch zoom, and horizontal scroll.
Canvas sizing stabilized to prevent zero-height or flickering canvases during container resize or flex layout shifts.
WebGPU logical viewport size tracked correctly so candles no longer disappear or misalign on high-DPI displays.
DrawingSidebar container-edge transform and styles normalized to prevent visual clipping and z-index issues.
v1.0.0-beta.5 2026-05

Added

TradingTerminal now accepts onError(error, context) — typed callback fired on datafeed failures during history loading, realtime subscription, or symbol search.
Datafeed types (IDatafeed, IRealtimeSubscription, HistoryRequest, HistoryResponse, ResolvedSymbol, SymbolSearchResult) re-exported from romaco-charts/react for convenience.

Fixed

subscribeBars exceptions caught and forwarded through onError instead of being swallowed silently.
Realtime "stale" state now triggers onError in addition to the visual overlay.
Error normalization across history, realtime, and search paths now uses a shared helper, ensuring onError always receives a proper Error instance.
v1.0.0-beta.4 2026-05

Added

TradingTerminal now exposes onReady and TradingTerminalRef for React code that needs the active chart instance.
TradingTerminal supports manual data mode or feed-driven datafeed mode, with data optional when a feed is provided.

Fixed

README and React integration guide updated with correct feed-driven TradingTerminal usage.
v1.0.0-beta.3 2026-05

Added

TradingTerminal now accepts a preset prop, eliminating the hardcoded dark default when embedding in themed applications.
romaco built-in preset added and exposed via preset="romaco" or ROMACO_PRESET import.
TradingTerminal, Chart, MinimalChart, useRomacoChart, ChartGrid split into dedicated React surface.

Fixed

IndicatorConfig docs corrected: field is name not type; panelId/visible removed; styles added.
ChartTheme docs expanded to include crosshair, border, xAxis, yAxis and gradient background.
Prevented candles disappearing after zooming in WebGPU mode.
v1.0.0-beta.2 2026-05

Added

Split the React surface into MinimalChart, Chart, and TradingTerminal.
Added the romaco built-in preset and exposed it through the public package.
Added the plug-and-play React consumer demo and release verification tooling.

Changed

Centralized horizontal viewport math for zoom and pan stability.
Updated public docs and React examples to match the beta surface.

Fixed

Prevented candles from disappearing after zooming in WebGPU mode.
v1.0.0-beta.1 2026-05

Initial Release

Automatic Data Conflation with LTTB algorithm
Plugin System with full lifecycle hooks
29+ built-in technical indicators
14+ drawing tools including Fibonacci and Elliott Waves
ChartAgentController for AI/agent workflows
Paper trading system with PnL tracking
60 FPS with 100K+ candles
Tree-shakeable exports
7 built-in themes

Licensing

Romaco Charts is source-available under BUSL-1.1. Commercial production use requires a separate license from Romaco.