Saving and Restoring
A trading app should open the chart the way the user left it: the same symbol and interval, their indicators with their settings, their trend lines and Fibonacci retracements, and their pane layout. SciTrader saves all of that as plain JSON in one call, and builds the chart again from it in another.
// Save
const state = financeChartPanel.toJSON(); // ISciFinanceChartPanelState: plain JSON
localStorage.setItem("my-chart", JSON.stringify(state));
// Restore
const saved = JSON.parse(localStorage.getItem("my-chart"));
const restored = await SciFinanceChartPanel.fromJSON(element, saved, { useSubCharts: saved.useSubCharts });
What Is Saved
| Saved and restored | Where in the JSON |
|---|---|
| The exchange, symbol, interval and period | finance., symbol, interval, period |
| Every study, with its pane and settings, including the user's edits | finance. |
| The drawings: trend lines, Fibonacci tools, channels, patterns | finance. |
| The live-tip mode | finance. |
| The pane layout: order, sizes, hidden and maximised panes, axis sync | layout |
| Sub-chart mode | use |
The Saved JSON Format shows a real saved chart, and every field.
What Isn't Saved
- The zoom and pan position. It is saved, as
finance.visibleRanges, but not applied: a restored chart opens on its period, or on the latest data. - The theme. Pass it to
fromJSON({ theme }), from the user's saved preference; see Remembering the User's Theme. - Settings that aren't data, such as
services,axisDefaults,paneSizingandsurfaceConfig. Pass them tofromJSONagain. - Anything outside the chart, such as a watchlist, the selected drawing tool or other app preferences. Save them yourself, beside the chart's state.
- The candles. Only the symbol and interval are saved; a restored chart loads fresh data.
TypeScript Example: Save, Restore and See the JSON
A BTC-USD chart with MACD. Add a study, draw with the tools on the left, or drag a pane divider, then Save: the JSON appears on the right. Change the chart again, then Restore to bring back the saved one. The save is kept in localStorage, so it survives a page reload.
The code for this example is found below.
- TS
import "scichart-scitrader/scitrader.css";
import {
CandleInterval,
CoinbaseExchange,
defaultExchangeProvider,
DrawingToolbar,
DrawingToolsController,
EStudyType,
idbPriceStore,
ISciFinanceChartPanelApi,
ISciFinanceChartPanelState,
MACDStudy,
OHLCStudy,
registerDrawingTools,
SciFinanceChartPanel,
studyProvider,
VolumeStudy,
} from "scichart-scitrader";
// This example expects this HTML:
// <div style="display: flex; flex-direction: column; height: 100%">
// <div id="toolbar"></div>
// <div style="display: flex; flex: 1; min-height: 0">
// <div id="tools"></div>
// <div id="chart" style="flex: 1; min-width: 0"></div>
// <pre id="json"></pre>
// </div>
// </div>
// Register a data source, once, at app startup
defaultExchangeProvider.registerExchange(CoinbaseExchange, idbPriceStore);
// Register the drawing tools' types before restoring, so saved drawings can be rebuilt. Without it,
// fromJSON on a fresh page skips them
registerDrawingTools();
const STORAGE_KEY = "my-app:chart";
const container = document.getElementById("chart");
let financeChartPanel: ISciFinanceChartPanelApi;
let drawing: DrawingToolsController;
let tools: DrawingToolbar;
// Drawing tools for whichever chart is current
function attachDrawingTools() {
tools?.destroy();
drawing?.detach();
drawing = DrawingToolsController.attach(financeChartPanel.financeApi);
tools = DrawingToolbar.create(document.getElementById("tools"), drawing, { orientation: "vertical" });
}
async function createChart() {
financeChartPanel = await SciFinanceChartPanel.create(container, {
exchangeName: CoinbaseExchange.name, // name="Coinbase" matches the built-in CoinbaseExchange
symbol: "BTC-USD",
interval: CandleInterval.SIX_HOURS,
period: "3M",
studies: [new OHLCStudy(), new VolumeStudy(), new MACDStudy()],
});
attachDrawingTools();
}
// 1. Save: toJSON() returns the chart as plain JSON: the exchange, symbol, interval and period,
// every study and its settings, the drawings, and the pane layout. Store it anywhere
function save() {
const state = financeChartPanel.toJSON();
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
showJson(state);
}
// 2. Restore: remove the chart, and build it again from the saved JSON. fromJSON recreates the
// studies, panes, drawings and data feed, then loads fresh data
async function restore() {
const saved = localStorage.getItem(STORAGE_KEY);
if (!saved) return;
const state: ISciFinanceChartPanelState = JSON.parse(saved);
tools.destroy();
drawing.detach();
financeChartPanel.cleanup();
financeChartPanel = await SciFinanceChartPanel.fromJSON(container, state, { useSubCharts: state.useSubCharts });
attachDrawingTools();
}
// Change the chart, so there is something to save: add a study, or draw with the tools on the left
function addStudy(type: EStudyType) {
financeChartPanel.financeApi.attachStudy(studyProvider.getStudy(type));
}
function showJson(state: ISciFinanceChartPanelState) {
const pre = document.getElementById("json");
Object.assign(pre.style, jsonStyle);
pre.textContent = `${JSON.stringify(state).length.toLocaleString()} characters\n\n${JSON.stringify(state, null, 2)}`;
}
function renderToolbar() {
const toolbar = document.getElementById("toolbar");
Object.assign(toolbar.style, toolbarStyle);
const button = (text: string, onClick: () => void) => {
const b = document.createElement("button");
b.textContent = text;
Object.assign(b.style, buttonStyle);
b.onclick = onClick;
return b;
};
toolbar.replaceChildren(
button("Add RSI", () => addStudy(EStudyType.RSI)),
button("Add SMA", () => addStudy(EStudyType.SMA)),
button("Save", save),
button("Restore", restore),
button("Clear saved", () => localStorage.removeItem(STORAGE_KEY))
);
}
// The toolbar and JSON panel use SciTrader's UI colours: its --st-ui-* CSS variables, with the stylesheet's defaults
const toolbarStyle: Partial<CSSStyleDeclaration> = {
display: "flex",
flexWrap: "wrap",
gap: "4px",
padding: "8px 12px",
background: "var(--st-ui-toolbar-background, #2b2839)",
borderBottom: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
};
const buttonStyle: Partial<CSSStyleDeclaration> = {
padding: "4px 12px",
border: "1px solid var(--st-ui-divider, rgba(255,255,255,0.15))",
borderRadius: "4px",
background: "var(--st-ui-button-background, #363345)",
color: "var(--st-ui-text, #e8e8ee)",
font: "13px sans-serif",
cursor: "pointer",
};
const jsonStyle: Partial<CSSStyleDeclaration> = {
width: "300px",
margin: "0",
padding: "8px",
overflow: "auto",
background: "var(--st-ui-background, #2b2839)",
color: "var(--st-ui-text-muted, #9da2bc)",
font: "11px monospace",
borderLeft: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
};
renderToolbar();
createChart();
React Example: Save, Restore and See the JSON
The same in React: Restore passes the saved state as restoreState, and changes the view’s key.
The code for this example is found below.
- React
import "scichart-scitrader/scitrader.css";
import * as React from "react";
import {
CandleInterval,
CoinbaseExchange,
defaultExchangeProvider,
EStudyType,
idbPriceStore,
ISciFinanceChartPanelApi,
ISciFinanceChartPanelConfig,
ISciFinanceChartPanelState,
MACDStudy,
OHLCStudy,
registerDrawingTools,
studyProvider,
VolumeStudy,
} from "scichart-scitrader";
import { DrawingToolbar, SciFinanceChartView, useDrawingTools } from "scichart-scitrader-react";
// Register a data source, once, at app startup
defaultExchangeProvider.registerExchange(CoinbaseExchange, idbPriceStore);
// Register the drawing tools' types before restoring, so saved drawings can be rebuilt. Without it,
// a chart restored on a fresh page skips them
registerDrawingTools();
const STORAGE_KEY = "my-app:chart";
export function SaveRestoreChart() {
const [api, setApi] = React.useState<ISciFinanceChartPanelApi>();
const { controller } = useDrawingTools(api);
// The state to restore, and a key that changes to create the chart again from it
const [restoreState, setRestoreState] = React.useState<ISciFinanceChartPanelState | null>(null);
const [mount, setMount] = React.useState(0);
const [json, setJson] = React.useState("");
// Create the config once per mounted chart: studies are live objects bound to one chart
const config = React.useMemo<ISciFinanceChartPanelConfig>(
() => ({
exchangeName: CoinbaseExchange.name, // name="Coinbase" matches the built-in CoinbaseExchange
symbol: "BTC-USD",
interval: CandleInterval.SIX_HOURS,
period: "3M",
studies: [new OHLCStudy(), new VolumeStudy(), new MACDStudy()],
}),
[mount]
);
// 1. Save: toJSON() returns the chart as plain JSON: the exchange, symbol, interval and period,
// every study and its settings, the drawings, and the pane layout. Store it anywhere
const save = () => {
if (!api) return;
const state = api.toJSON();
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
setJson(`${JSON.stringify(state).length.toLocaleString()} characters\n\n${JSON.stringify(state, null, 2)}`);
};
// 2. Restore: pass the saved state as restoreState, and change the key so the view creates the
// chart again from it. restoreState is read when the chart is created, like config
const restore = () => {
const saved = localStorage.getItem(STORAGE_KEY);
if (!saved) return;
// Deselect any drawing first: the drawing tools detach after the old chart is gone
controller?.clearSelection();
setApi(undefined);
setRestoreState(JSON.parse(saved));
setMount((m) => m + 1);
};
// Change the chart, so there is something to save: add a study, or draw with the tools on the left
const addStudy = (type: EStudyType) => api?.financeApi.attachStudy(studyProvider.getStudy(type));
return (
<div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
<div style={toolbarStyle}>
<button style={buttonStyle} onClick={() => addStudy(EStudyType.RSI)}>Add RSI</button>
<button style={buttonStyle} onClick={() => addStudy(EStudyType.SMA)}>Add SMA</button>
<button style={buttonStyle} onClick={save}>Save</button>
<button style={buttonStyle} onClick={restore}>Restore</button>
<button style={buttonStyle} onClick={() => localStorage.removeItem(STORAGE_KEY)}>Clear saved</button>
</div>
<div style={{ display: "flex", flex: 1, minHeight: 0 }}>
<DrawingToolbar controller={controller} orientation="vertical" />
<div style={{ flex: 1, minWidth: 0 }}>
<SciFinanceChartView
key={mount}
config={config}
restoreState={restoreState}
useSubCharts={restoreState?.useSubCharts}
onInit={setApi}
/>
</div>
<pre style={jsonStyle}>{json}</pre>
</div>
</div>
);
}
// The toolbar and JSON panel use SciTrader's UI colours: its --st-ui-* CSS variables, with the stylesheet's defaults
const toolbarStyle: React.CSSProperties = {
display: "flex",
flexWrap: "wrap",
gap: 4,
padding: "8px 12px",
background: "var(--st-ui-toolbar-background, #2b2839)",
borderBottom: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
};
const buttonStyle: React.CSSProperties = {
padding: "4px 12px",
border: "1px solid var(--st-ui-divider, rgba(255,255,255,0.15))",
borderRadius: 4,
background: "var(--st-ui-button-background, #363345)",
color: "var(--st-ui-text, #e8e8ee)",
font: "13px sans-serif",
cursor: "pointer",
};
const jsonStyle: React.CSSProperties = {
width: 300,
margin: 0,
padding: 8,
overflow: "auto",
background: "var(--st-ui-background, #2b2839)",
color: "var(--st-ui-text-muted, #9da2bc)",
font: "11px monospace",
borderLeft: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
};
Restoring a Chart
SciFinanceChartPanel.fromJSON(element, state, options) builds a new chart from a saved state. It creates each study through studyProvider, opens the panes, applies the saved layout and the drawings, then loads data for the saved symbol and period.
import { registerDrawingTools, SciFinanceChartPanel } from "scichart-scitrader";
registerDrawingTools(); // once, at startup: before any chart is restored
financeChartPanel.cleanup(); // the old chart, if there is one
financeChartPanel = await SciFinanceChartPanel.fromJSON(element, state, {
useSubCharts: state.useSubCharts,
theme, // not saved: pass the user's theme
});
Drawings are restored only if their types are registered. DrawingToolsController.attach registers the built-in drawing tools, but a chart restored when the page loads is created before any controller is attached, so its drawings are silently skipped. Call registerDrawingTools() once at startup. For your own annotation types, call SciFinanceChart.registerDrawingType(name).
- Studies must be registered with
studyProvider. The built-in studies are. A state with a study type that isn't throws, so load saved state safely. - Remove the old chart first, with
cleanup(), and detach its drawing tools before that. - Use the saved sub-chart mode,
state.useSubCharts, so the layout matches.
Restoring in React
Pass the saved state as restoreState, and give <SciFinanceChartView> a new key to create the chart again from it. Like config, restoreState is read when the chart is created:
<SciFinanceChartView
key={mount} // change it to restore
config={config}
restoreState={savedState}
useSubCharts={savedState?.useSubCharts}
onInit={setApi}
/>
With useDrawingTools, deselect any drawing (controller.clearSelection()) before changing the key: the drawing tools detach after the old chart has gone.
Pages in This Section
| Page | What it covers |
|---|---|
| The Saved JSON Format | A real saved chart, every field and its type, editing a saved state, and versions |
| Saving to Your Own Storage or Server | local, IndexedDB or your server; auto-saving; loading safely |
SciFinanceChartPanel.fromJSON, with its TSDoc
class SciFinanceChartPanel {
/**
* Restore a panel from serialized state.
*
* @param container — DOM element to mount into.
* @param state — Previously serialized state from {@link ISciFinanceChartPanelApi.toJSON}.
* @param options — Non-serializable settings (sub-chart mode, services, surface setup).
*/
static fromJSON(container: HTMLElement, state: ISciFinanceChartPanelState, options?: {
useSubCharts?: boolean;
services?: ISciFinanceChartServices;
surfaceConfig?: ISciFinanceChartPanelConfig["surfaceConfig"];
axisDefaults?: IChartAxisDefaults;
paneSizing?: Record<string, IPaneSizeConfig>;
maxPanes?: number;
theme?: ISciTraderTheme;
paneControls?: IPaneControlOptions;
loading?: IPaneLoadingOptions;
}): Promise<ISciFinanceChartPanelApi<HTMLElement>>;
}