Using the new Sub-Charts API, create a multi-pane stock chart example with indicator panels on a single WebGL chart surface. This allows for higher-performance since the WebGL context is shared. Zooming, panning, cursors are synchronised between the charts.
index.tsx
ExampleDataProvider.ts
theme.ts
FinChartLegendAnnotation.ts
FinChartLegendModifier.ts
1import {
2 SciChartSurface,
3 EDataSeriesType,
4 OhlcDataSeries,
5 XyyDataSeries,
6 CoordinateCalculatorBase,
7 EAnnotationType,
8 ISvgAnnotationBaseOptions,
9 SvgAnnotationBase,
10 ISciChartSubSurface,
11 EAxisType,
12 IndexCoordinateCalculator,
13 DpiHelper,
14} from "scichart";
15import { appTheme } from "../../../theme";
16
17export type TFinanceLegendTemplate = (legendAnnotation: FinChartLegendAnnotation) => string;
18
19export interface IFinChartLegendAnnotationOptions extends ISvgAnnotationBaseOptions {
20 template?: TFinanceLegendTemplate;
21 xIndex?: number;
22 paneId?: string;
23 offsetX?: number;
24 offsetY?: number;
25 title?: string;
26}
27
28/**
29 * A Tooltip Annotation which provides an SVG tooltip over the chart. Used by the {@link RolloverModifier}
30 */
31export class FinChartLegendAnnotation extends SvgAnnotationBase {
32 /** @inheritDoc */
33 public readonly type = EAnnotationType.SVG;
34 public readonly sciFinanceChart: SciChartSurface;
35 private templateProperty: TFinanceLegendTemplate = defaultFinanceLegendTemplate;
36 private xIndexProperty: number;
37 private paneIdProperty: string;
38 private offsetXProperty: number;
39 private offsetYProperty: number;
40 private activeSciChartSurfaceProperty: ISciChartSubSurface;
41 private titleProperty: string = "Default Title";
42
43 /**
44 * Creates an instance of the {@link FinChartLegendAnnotation}
45 * @param sciFinanceChart
46 * @param options
47 */
48 constructor(sciFinanceChart: SciChartSurface, options?: IFinChartLegendAnnotationOptions) {
49 super(options);
50 this.sciFinanceChart = sciFinanceChart;
51 this.isHidden = true;
52 this.templateProperty = options?.template ?? this.templateProperty;
53 this.xIndexProperty = options?.xIndex ?? this.xIndexProperty;
54 this.paneIdProperty = options?.paneId ?? this.paneIdProperty;
55 this.offsetXProperty = options?.offsetX ?? this.offsetXProperty;
56 this.offsetYProperty = options?.offsetY ?? this.offsetYProperty;
57 this.titleProperty = options?.title ?? this.titleProperty;
58 }
59
60 public get template() {
61 return this.templateProperty;
62 }
63
64 public set template(value) {
65 this.templateProperty = value;
66 }
67
68 public get xIndex() {
69 return this.xIndexProperty;
70 }
71
72 public set xIndex(value) {
73 this.xIndexProperty = value;
74 }
75
76 public get paneId() {
77 return this.paneIdProperty;
78 }
79
80 public set paneId(value) {
81 this.paneIdProperty = value;
82 }
83
84 public get offsetX() {
85 return this.offsetXProperty;
86 }
87
88 public set offsetX(value) {
89 this.offsetXProperty = value;
90 }
91
92 public get offsetY() {
93 return this.offsetYProperty;
94 }
95
96 public set offsetY(value) {
97 this.offsetYProperty = value;
98 }
99
100 public get activeSciChartSurface() {
101 return this.activeSciChartSurfaceProperty;
102 }
103
104 public set activeSciChartSurface(value) {
105 this.activeSciChartSurfaceProperty = value;
106 }
107
108 public get title() {
109 return this.titleProperty;
110 }
111
112 public set title(value) {
113 this.titleProperty = value;
114 }
115
116 public update(
117 xCalc: CoordinateCalculatorBase,
118 yCalc: CoordinateCalculatorBase,
119 xCoordSvgTrans: number,
120 yCoordSvgTrans: number
121 ): void {
122 if (this.svg) {
123 this.delete();
124 }
125 this.create(xCalc, yCalc, xCoordSvgTrans, yCoordSvgTrans);
126 }
127
128 protected create(
129 xCalc: CoordinateCalculatorBase,
130 yCalc: CoordinateCalculatorBase,
131 xCoordSvgTrans: number,
132 yCoordSvgTrans: number
133 ) {
134 if (this.template) {
135 let index: number = -1;
136 if (this.xAxis.type === EAxisType.CategoryAxis) {
137 index = Math.round(xCalc.getDataValue(this.x1 * DpiHelper.PIXEL_RATIO));
138 } else if (
139 this.xAxis.type === EAxisType.BaseValueAxis ||
140 this.xAxis.type === EAxisType.DiscontinuousDateAxis
141 ) {
142 const indexCoordCalc = xCalc as IndexCoordinateCalculator;
143 const val = indexCoordCalc.getDataValue(this.x1 * DpiHelper.PIXEL_RATIO);
144 index = Math.round(indexCoordCalc.indexCalculator.GetIndex(val));
145 }
146
147 this.xIndex = this.isMouseOverSeriesArea ? index : undefined;
148 if (this.xIndex === undefined) {
149 return;
150 }
151 const svgString = this.template(this);
152 const svgNode = document.createRange().createContextualFragment(svgString);
153 this.svgRoot.appendChild(svgNode);
154 this.setSvg(this.svgRoot.lastChild as SVGElement);
155 this.svg.setAttribute("x", (xCoordSvgTrans + this.offsetX).toString());
156 this.svg.setAttribute("y", (yCoordSvgTrans + this.offsetY).toString());
157 }
158 }
159
160 private get isMouseOverSeriesArea() {
161 return this.activeSciChartSurface;
162 }
163}
164
165/** @ignore */
166const defaultFinanceLegendTemplate: TFinanceLegendTemplate = (la: FinChartLegendAnnotation): string => {
167 const outputStrings: string[] = [];
168 const subSurface = la.sciFinanceChart.subCharts.find((study) => study.id === la.paneId);
169 let outputStr = "";
170 if (la.xIndex >= 0) {
171 subSurface.renderableSeries.asArray().forEach(({ dataSeries }) => {
172 switch (dataSeries.type) {
173 case EDataSeriesType.Ohlc: {
174 const openValues = (dataSeries as OhlcDataSeries).getNativeOpenValues();
175 const highValues = (dataSeries as OhlcDataSeries).getNativeHighValues();
176 const lowValues = (dataSeries as OhlcDataSeries).getNativeLowValues();
177 const closeValues = (dataSeries as OhlcDataSeries).getNativeCloseValues();
178
179 const openValue = openValues.get(la.xIndex);
180 const highValue = highValues.get(la.xIndex);
181 const lowValue = lowValues.get(la.xIndex);
182 const closeValue = closeValues.get(la.xIndex);
183
184 outputStr += `${dataSeries.dataSeriesName} O: ${openValue} H: ${highValue} L: ${lowValue} C: ${closeValue}`;
185 break;
186 }
187
188 case EDataSeriesType.Xyy: {
189 const yValues = dataSeries.getNativeYValues();
190 const y1Values = (dataSeries as XyyDataSeries).getNativeY1Values();
191 const yValue = yValues.get(la.xIndex).toFixed(4);
192 const y1Value = y1Values.get(la.xIndex).toFixed(4);
193 outputStr += `${dataSeries.dataSeriesName} Y: ${yValue} Y1: ${y1Value}`;
194
195 break;
196 }
197
198 default: {
199 const yValues = dataSeries.getNativeYValues();
200 const yValue = yValues.get(la.xIndex).toFixed(4);
201 outputStr += `${dataSeries.dataSeriesName}: ${yValue}`;
202 }
203 }
204
205 if (outputStr) {
206 outputStrings.push(outputStr);
207 outputStr = "";
208 }
209 });
210 }
211
212 let outputSvgString = "";
213 outputStrings.forEach((outputStr, index) => {
214 const y = 30 + index * 20;
215 // lightblue reads well over the dark panes, but washes out on the light theme's background,
216 // so that one drops to a deep blue instead
217 const textColor = appTheme.isDark ? "lightblue" : appTheme.VividBlue;
218 outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="${textColor}">${outputStr}</text>`;
219 });
220 return `<svg width="800" height="200">
221 <rect width="100%" height="100%" fill="#00000000" stroke="#00000000" stroke-width="2" />
222 <svg width="100%">
223 ${outputSvgString}
224 </svg>
225 </svg>`;
226};
227This React Charts example demonstrates how to build a high-performance multi-pane stock chart in a React application using SciChart.js. It leverages the new Sub-Charts API to render multiple sub-charts on a single WebGL chart surface.
The implementation makes use of the <SciChartReact/> component for easy integration within a React application. The chart is constructed using the Builder API with a JSON configuration through the call to chartBuilder.build2DChart(). Three distinct sub-charts are configured: a candlestick chart displaying OHLC data, a MACD chart with divergence and band series, and an RSI chart for technical analysis. For guidance on using the Sub-Charts API, see Creating Resizable Multi Pane Charts with SubCharts. Advanced customizations are implemented via custom chart modifiers such as FinChartLegendModifier and FinChartLegendAnnotation, which provide interactive SVG legends and tooltips. Developer-defined palette providers further enhance visual clarity by conditionally coloring volume and MACD histogram series.
The example showcases several advanced features including synchronized axis ranges across sub-charts, real-time legend updates driven by mouse movements, and interactive annotations powered by SVG. Custom drag-resizing logic has been integrated to allow users to adjust pane sizes dynamically. The shared WebGL context not only boosts performance but also ensures that all sub-charts respond uniformly to user interactions. For more details on implementing such techniques in a React environment, consult the React Charts with SciChart.js guide and explore performance tips in Optimizing SciChartJS Performance.
This example follows best practices for integrating SciChart.js in React by encapsulating chart initialization within the <SciChartReact/> component and using the Builder API for clear, maintainable configuration. Efficient handling of events and state synchronization across multiple sub-chart surfaces has been implemented to deliver a smooth interactive experience. Developers are encouraged to review related resources such as Creating a SciChart React Component from the Ground Up and guides on custom chart modifiers to further optimize their React chart applications.

Discover how to create a React Candlestick Chart or Stock Chart using SciChart.js. For high Performance JavaScript Charts, get your free demo now.

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.

Render a real-time React BTC order book heatmap with ease. Optimize your crypto exchange with a fast heatmap order book. Give the SciChart demo a try.

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.

Shows one seeded OHLC data set toggled between no filter, Heikin-Ashi, Renko, and Point & Figure views.

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.

Build high-performance financial apps with React trading drawing tools. Try SciChart’s demo to see the advanced drawing tools for trading apps in action.

Create a custom React stock chart annotation by extending SciChart.js PolyLineAnnotation with paired dashed connectors.

Deploy the ultimate React freehand drawing tool in your app. Render complex freehand drawing tools with a smooth UI experience. Try the SciChart demo today.