Skip to main content

Chart Controller

The public chart controller is ChartApi. The core mount(...) lifecycle owns it, and mounted.api.chart(chartId?) hands out the controller for one chart. Await mounted.ready() before calling controller methods — before readiness the runtime, datafeed, and restored state are still loading. Existing plain JavaScript integrations can also obtain a direct ChartApi from createChart(...).

import { createTradeScriptSdk } from '@tradescript/pro/sdk/core';

const sdk = await createTradeScriptSdk({ lease: deploymentLease });
const mounted = sdk.chart.mount({
mount: '#chart',
symbol: 'AAPL',
interval: '1D',
datafeed,
});

await mounted.ready();
const chart = mounted.api.chart();
await chart.setSymbol('MSFT');
await chart.setInterval('1H');

The controller stays valid until the mount is destroyed. Do not cache controllers past mounted.destroy(); in multi-chart workspaces, request per-chart controllers with mounted.api.chart(chartId).

Methods are grouped by task below:

GroupCovers
LifecycleReadiness, state snapshots, events, undo/redo, persistence
DataSymbol, interval, bars, indicators, comparisons, data export
LayoutViewport, price scales, panes, pixel conversion
UIDrawings, object tree, dialogs, image export, customization
TradingMarkers, alerts, order flow, price-axis actions

Core controller

ChartController is the minimal lifecycle and state API that ChartApi extends.

Full ChartController interface
export interface ChartController {
setSymbol: (symbol: string | SymbolInfo) => Promise<void> | void;
getSymbol: () => SymbolInfo;
setInterval: (interval: ChartInterval) => Promise<void> | void;
getInterval: () => ChartInterval;
barTimeToEndOfPeriod: (time: number, interval?: ChartInterval) => number;
endOfPeriodToBarTime: (time: number, interval?: ChartInterval) => number;
resize: () => void;
destroy: () => void;
getState: () => ChartState;
setState: (state: ChartState) => Promise<void> | void;
on: <TType extends ChartEventType>(
event: TType,
callback: ChartEventHandler<TType>
) => Unsubscribe;
}

Lifecycle

State, readiness, event subscription, history, and persistence.

AreaMethods
Core stategetState, setState, resize, destroy, on
Data readinessdataReady
Undo/redoundo, redo, getUndoRedoState, clearUndoHistory
StoragegetLayout, applyLayout, setStorageAdapter, getStorageCapabilities, listLayouts, saveLayout, loadLayout, deleteLayout, saveDrawings, loadDrawings
User settingsgetUserSettings, setUserSettings, setUserSetting, removeUserSetting, saveUserSettings, loadUserSettings, deleteUserSettings
TemplatescreateIndicatorTemplate, saveTemplate, loadTemplate, listTemplates, deleteTemplate, applyTemplate, exportTemplate, importTemplate

Data readiness

Use dataReady() when host code needs to wait for the current chart data load. The no-argument form returns Promise<boolean>. The callback overload returns true when data was already ready, or false when the callback was registered for the next load.

await chart.dataReady();

const alreadyReady = chart.dataReady(() => {
console.log('Chart data is loaded');
});

Storage

loadLayout applies the returned layout by default. Pass apply: false when you need to inspect or rename saved layout JSON without changing the active chart.

const savedLayout = await chart.loadLayout({
layoutId: 'opening-drive',
apply: false,
});

For drawing templates, pass templateTool on listTemplates, saveTemplate, loadTemplate, and deleteTemplate. The REST adapter maps it to tool=toolName.

For indicator templates, use createIndicatorTemplate(...) when the template should snapshot the current indicators, comparisons, and display settings. Set saveSymbol or saveInterval only when applying the template should also switch the active chart.

const template = chart.createIndicatorTemplate({
id: 'momentum-stack',
name: 'Momentum stack',
saveSymbol: true,
saveInterval: true,
});

await chart.saveTemplate({ template });
await chart.applyTemplate(template);
await chart.saveTemplate({
templateTool: 'trendLine',
template: {
id: 'trend-defaults',
kind: 'drawing',
version: 1,
name: 'Trend defaults',
drawings: chart.getDrawings().filter((drawing) => drawing.type === 'trendLine'),
metadata: { tool: 'trendLine' },
},
});

Data

Symbol, interval, chart type, loaded bars, indicators, and tabular export.

AreaMethods
Symbol and intervalsetSymbol, getSymbol, setInterval, getInterval
Chart typesetChartType, getChartType, listChartTypes
Bars and cachegetLoadedBars, clearBars, resetCache
Time helpersbarTimeToEndOfPeriod, endOfPeriodToBarTime
Price formattingpriceFormatter
IndicatorslistBuiltInIndicators, getBuiltInIndicator, findBuiltInIndicators, listBuiltInIndicatorCategories, addBuiltInIndicator, addIndicator, duplicateIndicator, updateBuiltInIndicatorProperties, updateIndicator, removeIndicator, removeIndicators, getIndicators, getIndicator, getIndicatorHandle
ComparisonsaddComparison, updateComparison, removeComparison, getComparisons, setComparisons, clearComparisons, getComparisonBars, reloadComparison
Data exportexportData, exportCsv

Data export

exportData() returns a typed columnar payload: field descriptors in schema, numeric Float64Array columns in data, display strings in displayedData, and NaN for sparse comparison or indicator cells. By default it includes main-series OHLCV columns, comparison OHLCV columns, and indicator plot columns. Pass includeComparisons: false, includeIndicators: false, includeVolume: false, or timeRange when exporting a narrower table.

exportCsv() serializes that same schema without creating a second data pipeline. Use columnIds to export only selected fields, timeRange for the visible range, and includeBom when the target spreadsheet expects a UTF-8 byte-order mark. exportedDataToCsv() is also exported for hosts that already hold an ExportedData result.

const csv = await chart.exportCsv({
columnIds: ['time', 'main.open', 'main.high', 'main.low', 'main.close', 'main.volume'],
timeRange: chart.getVisibleRange(),
includeBom: true,
});

Indicators

updateBuiltInIndicatorProperties(id, patch) applies a strict catalog-validated properties patch to an existing built-in indicator. It validates native inputs and styles against the built-in indicator catalog, deep-merges nested inputs, styles, and metadata, rejects unsupported fields before mutation, and leaves broad custom indicator records to updateIndicator(...).

updateIndicator also handles indicator scale reassignment. Changing paneId or priceScale applies new-left, new-right, as-series, or no-scale as a live placement change.

Use getIndicator(id) for a cloned snapshot of one indicator. Use duplicateIndicator(id, overrides?) to clone an existing indicator into a new native indicator instance while deep-merging inputs, symbolInputs, styles, and metadata overrides. Use getIndicatorHandle(id) when host code needs a live native handle for repeated reads, catalog-backed built-in property edits, broad indicator patches, input edits, style edits, metadata edits, duplication, visibility, pane placement, pane height, price-scale placement, and removal. The handle exposes cloned getInputs(), getStyles(), and getMetadata() reads; updateProperties(...) for strict built-in property edits; full-record setters; granular setInput(...); array-path setStyle(...) / setMetadataValue(...) methods for nested native state; and duplicate(...) for copy-and-tweak workflows. The handle delegates to the same indicator APIs as duplicateIndicator(...), updateBuiltInIndicatorProperties(...), updateIndicator(...), and removeIndicator(...).

Price formatter

Use priceFormatter() when host UI needs to format prices the same way as the main series. It returns a small formatter object with a format(value) method. The formatter uses customization().setTooltipFormatting({ formatPrice }) when configured, otherwise it falls back to the symbol's priceFormat or pricePrecision.

const formatter = chart.priceFormatter();
formatter.format(133.65625);

Data cache reset

Use resetCache() after upstream history corrections, vendor outage recovery, or a cache-invalidation event. It calls MarketDataFeed.resetCache(request) when the feed implements it, clears cached active and comparison bars, then reloads the active series.

await widget.resetCache();
// or
await chart.resetCache();

await chart.resetCache({ scope: 'visible-range', reason: 'vendor-correction' });

scope: 'all' is the default. scope: 'visible-range' forwards the current visibleRange with the active symbol and interval so feed-owned caches can refresh only the visible window.

Layout

Viewport, scales, panes, and coordinate mapping.

AreaMethods
ViewportsetVisibleRange, getVisibleRange, getVisibleBarRange, requestSelectBar, cancelSelectBar, isSelectBarRequested, scrollToTime, scrollToBar, scrollToRealTime, zoomAtTime, zoomAtBar, zoomAtCoordinate, getTimeScale
Price scalesetPriceScale, getPriceScale, resetPriceScale, setPriceToBarRatio, getPriceToBarRatio, setPriceToBarRatioLocked, isPriceToBarRatioLocked, setPriceScaleSlots, listPriceScaleSlots, updatePriceScaleSlot, removePriceScaleSlot
PanesgetPaneLayout, setPaneOrder, setPaneHeight, setPaneHeights, maximizePane, minimizePane, restorePane
DOM accessgetChartDom
// Fix the first pane's height, then snap back to realtime
const [mainPane] = chart.getPaneLayout();
chart.setPaneHeight(mainPane.paneId, 320);
await chart.scrollToRealTime();

Viewport

getTimeScale() returns a stable time-scale control object with barSpacing() / setBarSpacing() and rightOffset() / setRightOffset() methods. Bar spacing is in CSS pixels and right offset is in bars. inactivityGaps() / setInactivityGaps() and inactivityGapsChanged() control whether generated empty bars remain visible; visible gaps still require emptyBars or another explicit time-domain extension from the feed/cache layer. defaultRightOffset() reports the current default offset in bars.

requestSelectBar() switches the chart into bar-selection mode and resolves with the next selected bar timestamp. It rejects if a selection request is already active or if cancelSelectBar() is called first. isSelectBarRequested() reports whether that mode is currently active.

Price scale

setPriceToBarRatio() / getPriceToBarRatio() and setPriceToBarRatioLocked() / isPriceToBarRatioLocked() provide persistent ratio state. The value is validated as a positive finite number, null clears it, and { disableUndo } controls history recording. The ratio does not change y-axis geometry in this version; use visiblePriceRange for a fixed displayed range.

priceScaleSlots define named scale choices for the built-in indicator scale picker. They persist in ChartState and saved layouts and resolve to stable pane-backed scale targets. A slot can provide paneId; otherwise as-series uses candle_pane and non-main placements use price-scale-slot:<id>. Indicators carrying metadata.priceScaleSlotId use the slot's priceScale and paneId. The SDK lists each slot as a price-scale object row and exposes properties, update, and remove operations.

Panes

getPaneLayout() returns the current pane geometry and state. Use setPaneOrder(paneIds) for ordering, setPaneHeight(paneId, height) for one pane, setPaneHeights(...) for an atomic height batch, and maximizePane(...), minimizePane(...), or restorePane(...) for pane state changes. Successful changes emit pane-order-change or pane-layout-change and schedule autosave. Batch height changes validate every height before mutation and emit one pane-layout-change payload. The default maximize shortcut is Alt+Enter.

Multi-chart workspaces expose chart-level maximize state through workspace.maximizeChart(chartId?), restoreMaximizedChart(), toggleMaximizedChart(chartId?), getMaximizedChartId(), and isChartMaximized(chartId?). The state is stored as multiChart.maximizedChartId, emits workspace-maximize-change, and preserves the underlying split layout for lossless restore.

UI

Drawings, object tree, dialogs, chrome, and image export.

AreaMethods
DrawingslistDrawingTools, selectDrawingTool, getSelectedDrawingTool, setMagnetMode, getMagnetMode, createDrawing, updateDrawing, updateDrawingProperties, updateDrawingRuntimeMetadata, duplicateDrawing, removeDrawing, removeDrawings, listDrawings, getDrawing, getDrawingHandle, setAllDrawingsVisible, setAllDrawingsLocked, setDrawingVisibility, setDrawingLock, setDrawingZOrder, groupDrawings, ungroupDrawings, setDrawings, getDrawings
Object treelistObjects, getObject, showPropertiesDialog, getHoverObject, setObjectProviders, addObjectProvider, removeObjectProvider, updateObject, reorderObject, moveObject, removeObject, selectObjects, getSelection, clearSelection
FavoritesgetFavoriteIntervals, getFavoriteChartTypes, getFavoriteDrawingTools, getFavoriteIndicators
Image exportexportImage, copyImageToClipboard, uploadSnapshot, setDragExportEnabled, isDragExportEnabled
Display settingssetDisplaySettings, getDisplaySettings
Widget railgetWidgetRailState, setWidgetRailState, openWidgetRailPanel, closeWidgetRail
UI commandsopenSymbolSearch, openIntervalDialog, openIndicatorsDialog, openStorageDialog, openGoToDate, showNoticeDialog, showConfirmDialog, closePopups, customization

Drawing handles

OperationAPI
Read a cloned snapshotchart.getDrawing(id)
Repeated live editschart.getDrawingHandle(id)
Clone and optionally replace geometrychart.duplicateDrawing(id, overrides?) or handle.duplicate(overrides?)
Strict catalog-backed property editchart.updateDrawingProperties(id, patch)
Broad persistent patchchart.updateDrawing(id, patch)
Transient hover/active metadatachart.updateDrawingRuntimeMetadata(id, patch)

A drawing handle supports point edits, style and metadata updates, visibility, locking, z-order, grouping, duplication, and removal. Runtime metadata does not enter undo history or saved drawing state.

Properties editors

showPropertiesDialog(entityId, options?) returns true when an editor accepts the entity and false when no editor owns it.

Entity idBuilt-in editor
series:mainChart settings
comparison.idComparison settings
drawing:*Drawing properties
indicator:*Indicator properties
pane:*Pane height and state
price-scale:mainMain price-scale settings
price-scale:slot:<slotId>Slot label, placement, pane, order, and metadata

Provider-owned orders, positions, executions, annotations, and custom objects can implement ChartObjectProvider.showPropertiesDialog(context). Rows advertise support through actions.properties; accepted providers can publish open/close state with emitState(open, metadata).

Image export and drag export

copyImageToClipboard() uses the same native image export path as exportImage() and writes a bitmap blob through the browser Clipboard API. It defaults to PNG for clipboard compatibility and rejects with snapshot.clipboard-unsupported when the environment cannot write image clipboard items. The built-in copy-chart-image action calls this method with drawings included.

chart.exportImage() owns the chart-engine viewport. Use widget.exportImage() for the complete TradeScript widget root, including TradeScript toolbar and workspace-tab chrome. Its optional compose callback lets the host add application-owned pixels before the bitmap is encoded.

setDragExportEnabled(true) makes the chart surface browser-draggable and emits chart-drag-export on drag start. The payload includes pixel coordinates, mapped time/price when available, modifier keys, hoveredSourceId, setData, clearData, setDragImage, preventDefault, and an exportData() helper.

Widget rail and watchlist

openWidgetRailPanel(panelId), setWidgetRailState(state), and closeWidgetRail() expose typed state for host-composed side panels such as watchlists, market depth, news, details, account manager, or order ticket. The SDK emits widget-rail-change with previous/current state; rendering the rail is still owned by the host shell or exported React widgets.

addSymbolToWatchlist(request?) adds the current or supplied symbol to the active/default or supplied watchlist through MarketDataFeed.getWatchlist and updateWatchlist. The request supports item labels, sections/groups, metadata, top/bottom insertion, active-symbol selection, and optional watchlist rail opening. It emits watchlist-change and fails with datafeed.unsupported when the datafeed does not implement watchlist writes. The default add-symbol-to-watchlist shortcut action binds Alt+W and calls the method with openPanel: true.

Dialogs

Hosts can open the built-in storage dialog from their own chrome:

chart.openStorageDialog('load');
chart.openStorageDialog('save-as');

Hosts can also open built-in picker chrome directly. openIntervalDialog(...) focuses the native interval selector's custom interval field and can seed the first typed value:

chart.openSymbolSearch();
chart.openIntervalDialog('6');

Use the widget-hosted prompt APIs when host chrome should match the chart's modal styling:

const confirmed = await chart.showConfirmDialog({
title: 'Replace layout',
body: 'Overwrite the existing layout?',
});

await chart.showNoticeDialog({
title: 'Saved',
body: 'Layout saved.',
});

Customization

The old setTheme, setStyles, setLocale, and setTimezone shape is not the current SDK controller. Runtime customization lives under chart.customization().

const ui = chart.customization();

ui.setTheme('dark');
ui.setThemeRegistry({
desk: {
base: 'dark',
name: 'desk',
colors: { background: '#020617', text: '#e5e7eb' },
},
});
ui.applyTheme('desk');
ui.setTimezone('America/New_York');
ui.setLegend({ visible: true, showIndicatorRows: true });
ui.applyOverrides({ grid: { color: '#20242c' } });
ui.setCandleColors({
up: '#22c55e',
down: '#ef4444',
});

Trading

Markers, alerts, order-flow overlays, and price-axis actions. Broker-connected order and position operations live on widget.trading(chartId?) — see Trading.

AreaMethods
Markers and alertsaddMarker, updateMarker, removeMarker, setMarkers, getMarkers, clearMarkers, addAlert, updateAlert, removeAlert, setAlerts, getAlerts, clearAlerts
Order flowsetOrderFlowHeatmap, getOrderFlowHeatmap, setFootprint, getFootprint
Price-axis actionsemitPriceAxisAction
// Mark a fill and arm a price alert above it
const markerId = chart.addMarker({
points: [{ time: 1719499200000, value: 248.5 }],
shape: 'circle',
text: 'Fill',
group: 'trading',
});

const alertId = chart.addAlert({
condition: { kind: 'price-cross-up', price: 250 },
label: 'Breakout',
});

Alert lifecycle, evaluation, and providers are documented under Alerts. Order-flow overlays require a datafeed with depth and tape support — see Order Flow.

  • Widget Options — the construction options that produce the mount this controller belongs to.
  • Chart Events — the typed events emitted by the operations on this page.
  • Widget API — the searchable declaration for ChartApi and every related contract.
  • Chart — the visible surface each method acts on.