Skip to main content

JSON Data: Load OHLCV Data from JSON or a JavaScript Object

Candles often come as JSON: from your own REST API, a file, or a JavaScript array. This page shows how to chart them in a SciTrader chart.

note

The SDK doesn't yet have an exchange that takes JavaScript objects directly. Until it does, turn the candles into CSV text for CSVExchange, as below: a few lines of code, and you keep all of CSVExchange's features, such as every coarser interval and dates moved to now. For live data, write an exchange of your own instead; see Building a Custom Exchange.

Loading JSON Candles in Three Steps​

1. Get the candles, from a file, your API or anywhere else. Any shape works, as long as each candle has a time, prices and volume:

// One candle as your JSON has it. Here: time in milliseconds, prices and volume as numbers
type TJsonCandle = { time: number; open: number; high: number; low: number; close: number; volume: number };

const candles: TJsonCandle[] = await (await fetch("https://example.com/api/candles?symbol=BTCUSDT&interval=1d")).json();

2. Turn them into an exchange. Write them as CSV rows, and give CSVExchange the symbol, the interval and how time is written:

// CSVExchange reads CSV text, so write the candles as CSV rows: a header, then one row per candle.
// The header names are the ones CSVExchange looks for; use the `columns` option for other names
function candlesToCsv(candles: TJsonCandle[]): string {
const rows = candles.map((c) => `${c.time},${c.open},${c.high},${c.low},${c.close},${c.volume}`);
return ["time,open,high,low,close,volume", ...rows].join("\n");
}
const exchange = new CSVExchange(
{ csv: candlesToCsv(candles), symbol: "BTCUSDT", interval: CandleInterval.ONE_DAY },
{ name: "MyData", timeUnit: "ms" } // "s" for seconds, "iso" for date strings
);

3. Chart it, with createCSVServices for one chart, or registerCSVExchange(defaultExchangeProvider, exchange) for every chart:

const { services } = createCSVServices(exchange);
await SciFinanceChartPanel.create(container, {
exchangeName: exchange.name,
symbol: "BTCUSDT",
interval: CandleInterval.ONE_DAY,
period: "1Y",
services,
studies: [new OHLCStudy(), new VolumeStudy()],
});

JSON Shapes and Times​

Your JSON hasDo this
Times in millisecondstimeUnit: "ms"
Times in seconds (Unix time)timeUnit: "s"
ISO date strings, e.g. "2026-07-01T12:00:00Z"timeUnit: "iso". Dates without a timezone are read as UTC
Prices as stringsWrite them as they are: the CSV is text
Other field names, e.g. { t, o, h, l, c, v }Read them in candlesToCsv, e.g. ${c.t},${c.o},…
Several symbolsOne { csv, symbol } source per symbol, in an array: new CSVExchange([source1, source2], options)
Several intervals of one symbolOne source per interval: each interval is then served from the best match
Arrays instead of objects, e.g. [time, open, high, low, close, volume]rows.map((r) => r.join(","))

The data is moved to end now, like any CSV data; pass alignToNow: false if it is already current. See CSV Data.

TypeScript Example: A Candlestick Chart from JSON Data​

Four years of BTCUSDT daily candles, fetched as JSON, turned into an exchange and charted with Volume and MACD. The buttons change the interval with changeInterval: from daily candles, the exchange also makes 3-day and weekly ones.

The code for this example is found below.

import "scichart-scitrader/scitrader.css";
import {
CandleInterval,
createCSVServices,
CSVExchange,
MACDStudy,
OHLCStudy,
SciFinanceChartPanel,
VolumeStudy,
} from "scichart-scitrader";

// This example expects this HTML:
// <div style="display: flex; flex-direction: column; height: 100%">
// <div id="toolbar"></div>
// <div id="chart" style="flex: 1; min-height: 0"></div>
// </div>

// The sample price history: real Binance candles for BTCUSDT, ETHUSDT and SOLUSDT, served by jsDelivr
const SAMPLE_DATA = "https://cdn.jsdelivr.net/gh/ABTSoftware/SciChart.JS.Docs@0c19782c/docusaurus/static/financial-charts/data";

// One candle as your JSON has it. Here: time in milliseconds, prices and volume as numbers
type TJsonCandle = { time: number; open: number; high: number; low: number; close: number; volume: number };

// CSVExchange reads CSV text, so write the candles as CSV rows: a header, then one row per candle.
// The header names are the ones CSVExchange looks for; use the `columns` option for other names
function candlesToCsv(candles: TJsonCandle[]): string {
const rows = candles.map((c) => `${c.time},${c.open},${c.high},${c.low},${c.close},${c.volume}`);
return ["time,open,high,low,close,volume", ...rows].join("\n");
}

async function createChart() {
const toolbar = document.getElementById("toolbar");
Object.assign(toolbar.style, toolbarStyle);

// 1. Get the candles: here from a JSON file, but any array of candles works, e.g. from your own API
const candles: TJsonCandle[] = await (await fetch(`${SAMPLE_DATA}/btcusdt-1d.json`)).json();

// 2. Turn them into an exchange: one symbol, one interval. timeUnit "ms" says how `time` is
// written ("s" for seconds, "iso" for date strings). From daily candles, CSVExchange also
// serves 3d and 1w
const exchange = new CSVExchange(
{ csv: candlesToCsv(candles), symbol: "BTCUSDT", interval: CandleInterval.ONE_DAY },
{ name: "MyData", timeUnit: "ms" }
);

// 3. Chart it, with chart services for this exchange alone
const { services } = createCSVServices(exchange);
const financeChartPanel = await SciFinanceChartPanel.create(document.getElementById("chart"), {
exchangeName: exchange.name,
symbol: "BTCUSDT",
interval: CandleInterval.ONE_DAY,
period: "1Y",
services,
studies: [new OHLCStudy(), new VolumeStudy(), new MACDStudy()],
});
const chart = financeChartPanel.financeApi;

// 4. The intervals the candles can make: 1d, 3d and 1w. getSupportedIntervals() also lists 1M,
// which charts can't show yet, so leave it out. changeInterval picks a period that suits the interval
const intervals = exchange.getSupportedIntervals().filter((i) => i !== CandleInterval.ONE_MONTH);
toolbar.append(
buttonGroup(intervals, (i) => i, CandleInterval.ONE_DAY, (interval) =>
chart.changeInterval(interval)
)
);

// When the page goes away: financeChartPanel.cleanup();
return financeChartPanel;
}

// A row of toggle buttons: one per value, the selected one highlighted
function buttonGroup<T>(values: readonly T[], label: (v: T) => string, selected: T, onSelect: (v: T) => void) {
const group = document.createElement("div");
Object.assign(group.style, { display: "flex", gap: "4px" });
const render = (current: T) =>
group.replaceChildren(
...values.map((value) => {
const b = document.createElement("button");
b.textContent = label(value);
Object.assign(b.style, buttonStyle(value === current));
b.onclick = () => {
render(value);
onSelect(value);
};
return b;
})
);
render(selected);
return group;
}

// The toolbar uses SciTrader's UI colours: its --st-ui-* CSS variables, with the stylesheet's defaults
const toolbarStyle: Partial<CSSStyleDeclaration> = {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: "8px 16px",
padding: "8px",
background: "var(--st-ui-background, #2b2839)",
borderBottom: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
color: "var(--st-ui-text-muted, #9da2bc)",
fontSize: "13px",
};
const buttonStyle = (active = false): Partial<CSSStyleDeclaration> => ({
padding: "4px 10px",
border: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
borderRadius: "6px",
background: active ? "var(--st-ui-selection, #6c63ff)" : "var(--st-ui-surface, #373447)",
color: "var(--st-ui-text, #ffffff)",
font: "inherit",
fontSize: "13px",
cursor: "pointer",
});

createChart();

React Example: A Candlestick Chart from JSON Data​

The same chart in React: the candles are fetched in an effect, and the chart renders once they are in.

The code for this example is found below.

import "scichart-scitrader/scitrader.css";
import * as React from "react";
import {
CandleInterval,
createCSVServices,
CSVExchange,
ICSVServicesHandle,
ISciFinanceChartPanelApi,
ISciFinanceChartPanelConfig,
MACDStudy,
OHLCStudy,
VolumeStudy,
} from "scichart-scitrader";
import { SciFinanceChartView } from "scichart-scitrader-react";

// The sample price history: real Binance candles for BTCUSDT, ETHUSDT and SOLUSDT, served by jsDelivr
const SAMPLE_DATA = "https://cdn.jsdelivr.net/gh/ABTSoftware/SciChart.JS.Docs@0c19782c/docusaurus/static/financial-charts/data";

// One candle as your JSON has it. Here: time in milliseconds, prices and volume as numbers
type TJsonCandle = { time: number; open: number; high: number; low: number; close: number; volume: number };

// CSVExchange reads CSV text, so write the candles as CSV rows: a header, then one row per candle.
// The header names are the ones CSVExchange looks for; use the `columns` option for other names
function candlesToCsv(candles: TJsonCandle[]): string {
const rows = candles.map((c) => `${c.time},${c.open},${c.high},${c.low},${c.close},${c.volume}`);
return ["time,open,high,low,close,volume", ...rows].join("\n");
}

// Turn candles into chart services: an exchange with one symbol at one interval. timeUnit "ms" says
// how `time` is written ("s" for seconds, "iso" for date strings)
function servicesFor(candles: TJsonCandle[]): ICSVServicesHandle {
const exchange = new CSVExchange(
{ csv: candlesToCsv(candles), symbol: "BTCUSDT", interval: CandleInterval.ONE_DAY },
{ name: "MyData", timeUnit: "ms" }
);
return createCSVServices(exchange);
}

export function JsonDataChart() {
// Get the candles: here from a JSON file, but any array of candles works, e.g. from your own API
const [handle, setHandle] = React.useState<ICSVServicesHandle>();
React.useEffect(() => {
fetch(`${SAMPLE_DATA}/btcusdt-1d.json`)
.then((response) => response.json())
.then((candles: TJsonCandle[]) => setHandle(servicesFor(candles)));
}, []);

// Fills its parent, so give the parent a height
return handle ? <JsonChart handle={handle} /> : <div style={toolbarStyle}>Loading the candles…</div>;
}

function JsonChart({ handle }: { handle: ICSVServicesHandle }) {
const chartRef = React.useRef<ISciFinanceChartPanelApi>(null);
const [interval, setInterval] = React.useState(CandleInterval.ONE_DAY);

// Create the config once per mounted chart: studies are live objects bound to one chart
const config = React.useMemo<ISciFinanceChartPanelConfig>(
() => ({
exchangeName: handle.exchange.name,
symbol: "BTCUSDT",
interval: CandleInterval.ONE_DAY,
period: "1Y",
services: handle.services,
studies: [new OHLCStudy(), new VolumeStudy(), new MACDStudy()],
}),
[handle]
);

// financeApi throws until the chart has initialised
const chart = () => (chartRef.current?.isInitialized ? chartRef.current.financeApi : undefined);

return (
<div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
<div style={toolbarStyle}>
{/* The intervals the candles can make: 1d, 3d and 1w. getSupportedIntervals() also lists 1M,
which charts can't show yet. changeInterval picks a period that suits the interval */}
<ButtonGroup
values={handle.exchange.getSupportedIntervals().filter((i) => i !== CandleInterval.ONE_MONTH)}
selected={interval}
onSelect={(i) => (setInterval(i), chart()?.changeInterval(i))}
/>
</div>
<div style={{ flex: 1, minHeight: 0 }}>
<SciFinanceChartView ref={chartRef} config={config} />
</div>
</div>
);
}

// A row of toggle buttons: one per value, the selected one highlighted
function ButtonGroup<T extends string>(props: { values: readonly T[]; selected: T; onSelect: (v: T) => void }) {
return (
<div style={{ display: "flex", gap: 4 }}>
{props.values.map((value) => (
<button key={value} style={buttonStyle(value === props.selected)} onClick={() => props.onSelect(value)}>
{value}
</button>
))}
</div>
);
}

// The toolbar uses SciTrader's UI colours: its --st-ui-* CSS variables, with the stylesheet's defaults
const toolbarStyle: React.CSSProperties = {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: "8px 16px",
padding: 8,
background: "var(--st-ui-background, #2b2839)",
borderBottom: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
color: "var(--st-ui-text-muted, #9da2bc)",
fontSize: 13,
};
const buttonStyle = (active = false): React.CSSProperties => ({
padding: "4px 10px",
border: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
borderRadius: 6,
background: active ? "var(--st-ui-selection, #6c63ff)" : "var(--st-ui-surface, #373447)",
color: "var(--st-ui-text, #ffffff)",
font: "inherit",
fontSize: 13,
cursor: "pointer",
});