Demonstrates how to create a React Candlestick Chart using SciChart.js, High Performance JavaScript Charts
drawExample.ts
index.tsx
binanceRestClient.ts
ExampleDataProvider.ts
theme.ts
1// SCICHART EXAMPLE
2import {
3 CursorModifier,
4 CursorTooltipSvgAnnotation,
5 DateTimeNumericAxis,
6 DiscontinuousDateAxis,
7 EAutoRange,
8 EDataSeriesType,
9 EFillPaletteMode,
10 ENumericFormat,
11 ESeriesType,
12 FastCandlestickRenderableSeries,
13 FastColumnRenderableSeries,
14 FastLineRenderableSeries,
15 FastMountainRenderableSeries,
16 FastOhlcRenderableSeries,
17 GradientParams,
18 IFillPaletteProvider,
19 IPointMetadata,
20 IRenderableSeries,
21 MouseWheelZoomModifier,
22 NumberRange,
23 NumericAxis,
24 OhlcDataSeries,
25 OhlcSeriesInfo,
26 parseColorToUIntArgb,
27 Point,
28 SciChartOverview,
29 SciChartSurface,
30 SeriesInfo,
31 XyDataSeries,
32 XyMovingAverageFilter,
33 ZoomExtentsModifier,
34 ZoomPanModifier,
35} from "scichart";
36import { appTheme } from "../../../theme";
37import { simpleBinanceRestClient } from "../../../ExampleData/binanceRestClient";
38import { ExampleDataProvider, TPriceBar } from "../../../ExampleData/ExampleDataProvider";
39import {
40 DEFAULT_LABEL_THRESHOLDS,
41 ETradeChartLabelFormat,
42} from "scichart/Charting/Visuals/Axis/LabelProvider/SmartDateLabelProvider";
43
44const Y_AXIS_VOLUME_ID = "Y_AXIS_VOLUME_ID";
45
46export const drawExample = (dataSource: string) => async (rootElement: string | HTMLDivElement) => {
47 // Create a SciChartSurface
48 const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
49 theme: appTheme.SciChartJsTheme,
50 });
51
52 // We have a hybrid DiscontinuousDateAxis which 'magically' solves problems of different # of points in stock market datasetd with gaps
53 const xAxis = new DiscontinuousDateAxis(wasmContext, {
54 // autoRange.never as we're setting visibleRange explicitly below. If you dont do this, leave this flag default
55 autoRange: EAutoRange.Never,
56 cursorLabelFormat: ENumericFormat.Date_HHMM,
57 // Increase this threshold as the default value is right on our default range
58 labelThresholds: { [ETradeChartLabelFormat.Minutes]: 60 * 60 * 24 * 10 },
59 });
60 sciChartSurface.xAxes.add(xAxis);
61
62 // Create a NumericAxis on the YAxis with 2 Decimal Places
63 sciChartSurface.yAxes.add(
64 new NumericAxis(wasmContext, {
65 growBy: new NumberRange(0.1, 0.1),
66 labelFormat: ENumericFormat.Decimal,
67 labelPrecision: 2,
68 labelPrefix: "$",
69 autoRange: EAutoRange.Always,
70 })
71 );
72
73 // Create a secondary YAxis to host volume data on its own scale
74 sciChartSurface.yAxes.add(
75 new NumericAxis(wasmContext, {
76 id: Y_AXIS_VOLUME_ID,
77 growBy: new NumberRange(0, 4),
78 isVisible: false,
79 autoRange: EAutoRange.Always,
80 })
81 );
82
83 const xValues: number[] = [];
84 const openValues: number[] = [];
85 const highValues: number[] = [];
86 const lowValues: number[] = [];
87 const closeValues: number[] = [];
88 const volumeValues: number[] = [];
89
90 // Fetch data from now to 300 1hr candles ago
91 const endDate = new Date(Date.now());
92 const startDate = new Date();
93 startDate.setHours(endDate.getHours() - 300);
94 let priceBars: TPriceBar[];
95 if (dataSource !== "Random") {
96 priceBars = await simpleBinanceRestClient.getCandles("BTCUSDT", "1h", startDate, endDate, 500, dataSource);
97 } else {
98 priceBars = ExampleDataProvider.getRandomCandles(300, 60000, startDate, 60 * 60, true);
99 }
100
101 // Maps PriceBar { date, open, high, low, close, volume } to structure-of-arrays expected by scichart
102 priceBars.forEach((priceBar: any) => {
103 xValues.push(priceBar.date);
104 openValues.push(priceBar.open);
105 highValues.push(priceBar.high);
106 lowValues.push(priceBar.low);
107 closeValues.push(priceBar.close);
108 volumeValues.push(priceBar.volume);
109 });
110 // Zoom to the latest 100 candles
111 const lastBar = priceBars[priceBars.length - 1];
112 const firstVisiblebar = priceBars[priceBars.length - 100];
113 xAxis.visibleRange = new NumberRange(firstVisiblebar.date, lastBar.date);
114
115 // Create and add the Candlestick series
116 // The Candlestick Series requires a special dataseries type called OhlcDataSeries with o,h,l,c and date values
117 const candleDataSeries = new OhlcDataSeries(wasmContext, {
118 xValues,
119 openValues,
120 highValues,
121 lowValues,
122 closeValues,
123 dataSeriesName: dataSource === "Random" ? "Random" : "BTC/USDT",
124 });
125 const candlestickSeries = new FastCandlestickRenderableSeries(wasmContext, {
126 dataSeries: candleDataSeries,
127 stroke: appTheme.ForegroundColor, // used by cursorModifier below
128 strokeThickness: 1,
129 brushUp: appTheme.VividGreen + "77",
130 brushDown: appTheme.MutedRed + "77",
131 strokeUp: appTheme.VividGreen,
132 strokeDown: appTheme.MutedRed,
133 });
134 sciChartSurface.renderableSeries.add(candlestickSeries);
135
136 // Add an Ohlcseries. this will be invisible to begin with
137 const ohlcSeries = new FastOhlcRenderableSeries(wasmContext, {
138 dataSeries: candleDataSeries,
139 stroke: appTheme.ForegroundColor, // used by cursorModifier below
140 strokeThickness: 1,
141 dataPointWidth: 0.9,
142 strokeUp: appTheme.VividGreen,
143 strokeDown: appTheme.MutedRed,
144 isVisible: false,
145 });
146 sciChartSurface.renderableSeries.add(ohlcSeries);
147
148 //Add some moving averages using SciChart's filters/transforms API
149 // when candleDataSeries updates, XyMovingAverageFilter automatically recomputes
150 sciChartSurface.renderableSeries.add(
151 new FastLineRenderableSeries(wasmContext, {
152 dataSeries: new XyMovingAverageFilter(candleDataSeries, {
153 dataSeriesName: "Moving Average (20)",
154 length: 20,
155 }),
156 stroke: appTheme.VividSkyBlue,
157 })
158 );
159
160 sciChartSurface.renderableSeries.add(
161 new FastLineRenderableSeries(wasmContext, {
162 dataSeries: new XyMovingAverageFilter(candleDataSeries, {
163 dataSeriesName: "Moving Average (50)",
164 length: 50,
165 }),
166 stroke: appTheme.VividPink,
167 })
168 );
169
170 // Add volume data onto the chart
171 sciChartSurface.renderableSeries.add(
172 new FastColumnRenderableSeries(wasmContext, {
173 dataSeries: new XyDataSeries(wasmContext, { xValues, yValues: volumeValues, dataSeriesName: "Volume" }),
174 strokeThickness: 0,
175 // This is how we get volume to scale - on a hidden YAxis
176 yAxisId: Y_AXIS_VOLUME_ID,
177 // This is how we colour volume bars red or green
178 paletteProvider: new VolumePaletteProvider(
179 candleDataSeries,
180 appTheme.VividGreen + "77",
181 appTheme.MutedRed + "77"
182 ),
183 })
184 );
185
186 // Optional: Add some interactivity modifiers
187 sciChartSurface.chartModifiers.add(
188 new ZoomExtentsModifier(),
189 new ZoomPanModifier({ enableZoom: true }),
190 new MouseWheelZoomModifier(),
191 new CursorModifier({
192 crosshairStroke: appTheme.VividOrange,
193 axisLabelFill: appTheme.VividOrange,
194 tooltipLegendTemplate: getTooltipLegendTemplate,
195 })
196 );
197
198 // Add Overview chart. This will automatically bind to the parent surface
199 // displaying its series. Zooming the chart will zoom the overview and vice versa
200
201 //Exporting at the bottom by an object-
202 // const overview = await SciChartOverview.create(sciChartSurface, divOverviewId, {
203 // theme: appTheme.SciChartJsTheme,
204 // transformRenderableSeries: getOverviewSeries,
205 // });
206
207 return { sciChartSurface, candlestickSeries, ohlcSeries };
208};
209
210class VolumePaletteProvider implements IFillPaletteProvider {
211 fillPaletteMode: EFillPaletteMode = EFillPaletteMode.SOLID;
212 private ohlcDataSeries: OhlcDataSeries;
213 private upColorArgb: number;
214 private downColorArgb: number;
215
216 constructor(masterData: OhlcDataSeries, upColor: string, downColor: string) {
217 this.upColorArgb = parseColorToUIntArgb(upColor);
218 this.downColorArgb = parseColorToUIntArgb(downColor);
219 this.ohlcDataSeries = masterData;
220 }
221 onAttached(parentSeries: IRenderableSeries): void {}
222 onDetached(): void {}
223
224 // Return up or down color for the volume bars depending on Ohlc data
225 overrideFillArgb(
226 xValue: number,
227 yValue: number,
228 index: number,
229 opacity?: number,
230 metadata?: IPointMetadata
231 ): number {
232 const isUpCandle =
233 this.ohlcDataSeries.getNativeOpenValues().get(index) >=
234 this.ohlcDataSeries.getNativeCloseValues().get(index);
235 return isUpCandle ? this.upColorArgb : this.downColorArgb;
236 }
237
238 // Override stroke as well, even though strokethickness is 0, because stroke is used if column thickness goes to 0.
239 overrideStrokeArgb(
240 xValue: number,
241 yValue: number,
242 index: number,
243 opacity?: number,
244 metadata?: IPointMetadata
245 ): number {
246 return this.overrideFillArgb(xValue, yValue, index, opacity, metadata);
247 }
248}
249
250// Override the standard tooltip displayed by CursorModifier
251const getTooltipLegendTemplate = (seriesInfos: SeriesInfo[], svgAnnotation: CursorTooltipSvgAnnotation) => {
252 let outputSvgString = "";
253
254 // Foreach series there will be a seriesInfo supplied by SciChart. This contains info about the series under the house
255 seriesInfos.forEach((seriesInfo, index) => {
256 const y = 20 + index * 20;
257 const textColor = seriesInfo.stroke;
258 let legendText = seriesInfo.formattedYValue;
259 if (seriesInfo.dataSeriesType === EDataSeriesType.Ohlc) {
260 const o = seriesInfo as OhlcSeriesInfo;
261 legendText = `Open=${o.formattedOpenValue} High=${o.formattedHighValue} Low=${o.formattedLowValue} Close=${o.formattedCloseValue}`;
262 }
263 outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="${textColor}">
264 ${seriesInfo.seriesName}: ${legendText}
265 </text>`;
266 });
267
268 return `<svg width="100%" height="100%">
269 ${outputSvgString}
270 </svg>`;
271};
272
273// Override the Renderableseries to display on the scichart overview
274const getOverviewSeries = (defaultSeries: IRenderableSeries) => {
275 if (defaultSeries.type === ESeriesType.CandlestickSeries) {
276 // Swap the default candlestick series on the overview chart for a mountain series. Same data
277 return new FastMountainRenderableSeries(defaultSeries.parentSurface.webAssemblyContext2D, {
278 dataSeries: defaultSeries.dataSeries,
279 fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
280 { color: appTheme.VividSkyBlue + "77", offset: 0 },
281 { color: "Transparent", offset: 1 },
282 ]),
283 stroke: appTheme.VividSkyBlue,
284 });
285 }
286 // hide all other series
287 return undefined;
288};
289
290export const overviewOptions = {
291 theme: appTheme.SciChartJsTheme,
292 transformRenderableSeries: getOverviewSeries,
293};
294This example demonstrates how to implement a high performance candlestick chart using SciChart.js within a React framework. The chart is designed to render financial data, visualize candlestick and OHLC series, and integrate advanced features such as moving average filters and a custom volume palette provider. Additionally, it leverages a chart overview component to provide users with a synchronized miniature view of the main chart.
The chart is initialized by asynchronously creating a SciChartSurface and its associated WebAssembly context using the SciChartSurface.create method. DateTime and Numeric axes are set up with precise configuration of visible ranges and auto-ranging options. Data is mapped into an OhlcDataSeries and rendered with a FastCandlestickRenderableSeries. Moving average filters are applied with the XyMovingAverageFilter to dynamically update trend lines as new data arrives. A custom palette provider is implemented to automatically color volume bars based on candle data. Interactivity is enhanced through modifiers such as ZoomPanModifier and CursorModifier, providing users with responsive zooming, panning, and tooltip capabilities.
SciChartNestedOverview from scichart-react provides a synchronized, miniature view of the main chart, making navigation through large datasets more intuitive.The example is built with React using components like <SciChartReact/> for chart rendering and Material-UI’s ToggleButtonGroup for user interaction. React hooks manage dynamic state changes, such as toggling between candlestick and OHLC series and switching data sources. For instance, the Material-UI ToggleButtonGroup is employed to change series visibility, enhancing the user interface. Refer to the Material UI ToggleButtonGroup documentation for more details on its configuration.
Performance optimizations are achieved through efficient WebAssembly integration and the use of optimized data structures, as highlighted in the Why SciChart High Performance Realtime Big Data Charts article. Overall, this example provides a comprehensive guide for developers looking to build interactive, high-performance financial charts in React using SciChart.js.

Easily create React OHLC Chart or Stock Chart using feature-rich SciChart.js chart library. Supports custom colors. Get your free trial now.

Create a React Realtime Ticking Candlestick / Stock Chart with live ticking and updating, using the high performance SciChart.js chart library. Get free demo now.

Create a React heatmap chart showing historical orderbook levels, using the high performance SciChart.js chart library. Get free demo now.

Create a React Multi-Pane Candlestick / Stock Chart with indicator panels, synchronized zooming, panning and cursors. Get your free trial of SciChart.js now.

Demonstrating the capability of SciChart.js to create a composite 2D & 3D Chart application. An example like this could be used to visualize Tenor curves in a financial setting, or other 2D/3D data combined on a single screen.

Create a React Multi-Pane Candlestick / Stock Chart with indicator panels, synchronized zooming, panning and cursors. Get your free trial of SciChart.js now.

Create a React Depth Chart, using the high performance SciChart.js chart library. Get free demo now.

Demonstrates how to place Buy/Sell arrow markers on a React Stock Chart using SciChart.js - Annotations API

This demo shows you how to create a <strong>{frameworkName} User Annotated Stock Chart</strong> using SciChart.js. Custom modifiers allow you to add lines and markers, then use the built in serialisation functions to save and reload the chart, including the data and all your custom annotations.