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:
| Group | Covers |
|---|---|
| Lifecycle | Readiness, state snapshots, events, undo/redo, persistence |
| Data | Symbol, interval, bars, indicators, comparisons, data export |
| Layout | Viewport, price scales, panes, pixel conversion |
| UI | Drawings, object tree, dialogs, image export, customization |
| Trading | Markers, 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.
| Area | Methods |
|---|---|
| Core state | getState, setState, resize, destroy, on |
| Data readiness | dataReady |
| Undo/redo | undo, redo, getUndoRedoState, clearUndoHistory |
| Storage | getLayout, applyLayout, setStorageAdapter, getStorageCapabilities, listLayouts, saveLayout, loadLayout, deleteLayout, saveDrawings, loadDrawings |
| User settings | getUserSettings, setUserSettings, setUserSetting, removeUserSetting, saveUserSettings, loadUserSettings, deleteUserSettings |
| Templates | createIndicatorTemplate, 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.
| Area | Methods |
|---|---|
| Symbol and interval | setSymbol, getSymbol, setInterval, getInterval |
| Chart type | setChartType, getChartType, listChartTypes |
| Bars and cache | getLoadedBars, clearBars, resetCache |
| Time helpers | barTimeToEndOfPeriod, endOfPeriodToBarTime |
| Price formatting | priceFormatter |
| Indicators | listBuiltInIndicators, getBuiltInIndicator, findBuiltInIndicators, listBuiltInIndicatorCategories, addBuiltInIndicator, addIndicator, duplicateIndicator, updateBuiltInIndicatorProperties, updateIndicator, removeIndicator, removeIndicators, getIndicators, getIndicator, getIndicatorHandle |
| Comparisons | addComparison, updateComparison, removeComparison, getComparisons, setComparisons, clearComparisons, getComparisonBars, reloadComparison |
| Data export | exportData, 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.
| Area | Methods |
|---|---|
| Viewport | setVisibleRange, getVisibleRange, getVisibleBarRange, requestSelectBar, cancelSelectBar, isSelectBarRequested, scrollToTime, scrollToBar, scrollToRealTime, zoomAtTime, zoomAtBar, zoomAtCoordinate, getTimeScale |
| Price scale | setPriceScale, getPriceScale, resetPriceScale, setPriceToBarRatio, getPriceToBarRatio, setPriceToBarRatioLocked, isPriceToBarRatioLocked, setPriceScaleSlots, listPriceScaleSlots, updatePriceScaleSlot, removePriceScaleSlot |
| Panes | getPaneLayout, setPaneOrder, setPaneHeight, setPaneHeights, maximizePane, minimizePane, restorePane |
| DOM access | getChartDom |
// 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.
| Area | Methods |
|---|---|
| Drawings | listDrawingTools, selectDrawingTool, getSelectedDrawingTool, setMagnetMode, getMagnetMode, createDrawing, updateDrawing, updateDrawingProperties, updateDrawingRuntimeMetadata, duplicateDrawing, removeDrawing, removeDrawings, listDrawings, getDrawing, getDrawingHandle, setAllDrawingsVisible, setAllDrawingsLocked, setDrawingVisibility, setDrawingLock, setDrawingZOrder, groupDrawings, ungroupDrawings, setDrawings, getDrawings |
| Object tree | listObjects, getObject, showPropertiesDialog, getHoverObject, setObjectProviders, addObjectProvider, removeObjectProvider, updateObject, reorderObject, moveObject, removeObject, selectObjects, getSelection, clearSelection |
| Favorites | getFavoriteIntervals, getFavoriteChartTypes, getFavoriteDrawingTools, getFavoriteIndicators |
| Image export | exportImage, copyImageToClipboard, uploadSnapshot, setDragExportEnabled, isDragExportEnabled |
| Display settings | setDisplaySettings, getDisplaySettings |
| Widget rail | getWidgetRailState, setWidgetRailState, openWidgetRailPanel, closeWidgetRail |
| UI commands | openSymbolSearch, openIntervalDialog, openIndicatorsDialog, openStorageDialog, openGoToDate, showNoticeDialog, showConfirmDialog, closePopups, customization |
Drawing handles
| Operation | API |
|---|---|
| Read a cloned snapshot | chart.getDrawing(id) |
| Repeated live edits | chart.getDrawingHandle(id) |
| Clone and optionally replace geometry | chart.duplicateDrawing(id, overrides?) or handle.duplicate(overrides?) |
| Strict catalog-backed property edit | chart.updateDrawingProperties(id, patch) |
| Broad persistent patch | chart.updateDrawing(id, patch) |
| Transient hover/active metadata | chart.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 id | Built-in editor |
|---|---|
series:main | Chart settings |
comparison.id | Comparison settings |
drawing:* | Drawing properties |
indicator:* | Indicator properties |
pane:* | Pane height and state |
price-scale:main | Main 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.
| Area | Methods |
|---|---|
| Markers and alerts | addMarker, updateMarker, removeMarker, setMarkers, getMarkers, clearMarkers, addAlert, updateAlert, removeAlert, setAlerts, getAlerts, clearAlerts |
| Order flow | setOrderFlowHeatmap, getOrderFlowHeatmap, setFootprint, getFootprint |
| Price-axis actions | emitPriceAxisAction |
// 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.
Related pages
- 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
ChartApiand every related contract. - Chart — the visible surface each method acts on.