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";
15
16export type TFinanceLegendTemplate = (legendAnnotation: FinChartLegendAnnotation) => string;
17
18export interface IFinChartLegendAnnotationOptions extends ISvgAnnotationBaseOptions {
19 template?: TFinanceLegendTemplate;
20 xIndex?: number;
21 paneId?: string;
22 offsetX?: number;
23 offsetY?: number;
24 title?: string;
25}
26
27/**
28 * A Tooltip Annotation which provides an SVG tooltip over the chart. Used by the {@link RolloverModifier}
29 */
30export class FinChartLegendAnnotation extends SvgAnnotationBase {
31 /** @inheritDoc */
32 public readonly type = EAnnotationType.SVG;
33 public readonly sciFinanceChart: SciChartSurface;
34 private templateProperty: TFinanceLegendTemplate = defaultFinanceLegendTemplate;
35 private xIndexProperty: number;
36 private paneIdProperty: string;
37 private offsetXProperty: number;
38 private offsetYProperty: number;
39 private activeSciChartSurfaceProperty: ISciChartSubSurface;
40 private titleProperty: string = "Default Title";
41
42 /**
43 * Creates an instance of the {@link FinChartLegendAnnotation}
44 * @param sciFinanceChart
45 * @param options
46 */
47 constructor(sciFinanceChart: SciChartSurface, options?: IFinChartLegendAnnotationOptions) {
48 super(options);
49 this.sciFinanceChart = sciFinanceChart;
50 this.isHidden = true;
51 this.templateProperty = options?.template ?? this.templateProperty;
52 this.xIndexProperty = options?.xIndex ?? this.xIndexProperty;
53 this.paneIdProperty = options?.paneId ?? this.paneIdProperty;
54 this.offsetXProperty = options?.offsetX ?? this.offsetXProperty;
55 this.offsetYProperty = options?.offsetY ?? this.offsetYProperty;
56 this.titleProperty = options?.title ?? this.titleProperty;
57 }
58
59 public get template() {
60 return this.templateProperty;
61 }
62
63 public set template(value) {
64 this.templateProperty = value;
65 }
66
67 public get xIndex() {
68 return this.xIndexProperty;
69 }
70
71 public set xIndex(value) {
72 this.xIndexProperty = value;
73 }
74
75 public get paneId() {
76 return this.paneIdProperty;
77 }
78
79 public set paneId(value) {
80 this.paneIdProperty = value;
81 }
82
83 public get offsetX() {
84 return this.offsetXProperty;
85 }
86
87 public set offsetX(value) {
88 this.offsetXProperty = value;
89 }
90
91 public get offsetY() {
92 return this.offsetYProperty;
93 }
94
95 public set offsetY(value) {
96 this.offsetYProperty = value;
97 }
98
99 public get activeSciChartSurface() {
100 return this.activeSciChartSurfaceProperty;
101 }
102
103 public set activeSciChartSurface(value) {
104 this.activeSciChartSurfaceProperty = value;
105 }
106
107 public get title() {
108 return this.titleProperty;
109 }
110
111 public set title(value) {
112 this.titleProperty = value;
113 }
114
115 public update(
116 xCalc: CoordinateCalculatorBase,
117 yCalc: CoordinateCalculatorBase,
118 xCoordSvgTrans: number,
119 yCoordSvgTrans: number
120 ): void {
121 if (this.svg) {
122 this.delete();
123 }
124 this.create(xCalc, yCalc, xCoordSvgTrans, yCoordSvgTrans);
125 }
126
127 protected create(
128 xCalc: CoordinateCalculatorBase,
129 yCalc: CoordinateCalculatorBase,
130 xCoordSvgTrans: number,
131 yCoordSvgTrans: number
132 ) {
133 if (this.template) {
134 let index: number = -1;
135 if (this.xAxis.type === EAxisType.CategoryAxis) {
136 index = Math.round(xCalc.getDataValue(this.x1 * DpiHelper.PIXEL_RATIO));
137 } else if (
138 this.xAxis.type === EAxisType.BaseValueAxis ||
139 this.xAxis.type === EAxisType.DiscontinuousDateAxis
140 ) {
141 const indexCoordCalc = xCalc as IndexCoordinateCalculator;
142 const val = indexCoordCalc.getDataValue(this.x1 * DpiHelper.PIXEL_RATIO);
143 index = Math.round(indexCoordCalc.indexCalculator.GetIndex(val));
144 }
145
146 this.xIndex = this.isMouseOverSeriesArea ? index : undefined;
147 if (this.xIndex === undefined) {
148 return;
149 }
150 const svgString = this.template(this);
151 const svgNode = document.createRange().createContextualFragment(svgString);
152 this.svgRoot.appendChild(svgNode);
153 this.setSvg(this.svgRoot.lastChild as SVGElement);
154 this.svg.setAttribute("x", (xCoordSvgTrans + this.offsetX).toString());
155 this.svg.setAttribute("y", (yCoordSvgTrans + this.offsetY).toString());
156 }
157 }
158
159 private get isMouseOverSeriesArea() {
160 return this.activeSciChartSurface;
161 }
162}
163
164/** @ignore */
165const defaultFinanceLegendTemplate: TFinanceLegendTemplate = (la: FinChartLegendAnnotation): string => {
166 const outputStrings: string[] = [];
167 const subSurface = la.sciFinanceChart.subCharts.find((study) => study.id === la.paneId);
168 let outputStr = "";
169 if (la.xIndex >= 0) {
170 subSurface.renderableSeries.asArray().forEach(({ dataSeries }) => {
171 switch (dataSeries.type) {
172 case EDataSeriesType.Ohlc: {
173 const openValues = (dataSeries as OhlcDataSeries).getNativeOpenValues();
174 const highValues = (dataSeries as OhlcDataSeries).getNativeHighValues();
175 const lowValues = (dataSeries as OhlcDataSeries).getNativeLowValues();
176 const closeValues = (dataSeries as OhlcDataSeries).getNativeCloseValues();
177
178 const openValue = openValues.get(la.xIndex);
179 const highValue = highValues.get(la.xIndex);
180 const lowValue = lowValues.get(la.xIndex);
181 const closeValue = closeValues.get(la.xIndex);
182
183 outputStr += `${dataSeries.dataSeriesName} O: ${openValue} H: ${highValue} L: ${lowValue} C: ${closeValue}`;
184 break;
185 }
186
187 case EDataSeriesType.Xyy: {
188 const yValues = dataSeries.getNativeYValues();
189 const y1Values = (dataSeries as XyyDataSeries).getNativeY1Values();
190 const yValue = yValues.get(la.xIndex).toFixed(4);
191 const y1Value = y1Values.get(la.xIndex).toFixed(4);
192 outputStr += `${dataSeries.dataSeriesName} Y: ${yValue} Y1: ${y1Value}`;
193
194 break;
195 }
196
197 default: {
198 const yValues = dataSeries.getNativeYValues();
199 const yValue = yValues.get(la.xIndex).toFixed(4);
200 outputStr += `${dataSeries.dataSeriesName}: ${yValue}`;
201 }
202 }
203
204 if (outputStr) {
205 outputStrings.push(outputStr);
206 outputStr = "";
207 }
208 });
209 }
210
211 let outputSvgString = "";
212 outputStrings.forEach((outputStr, index) => {
213 const y = 30 + index * 20;
214 outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="lightblue">${outputStr}</text>`;
215 });
216 return `<svg width="800" height="200">
217 <rect width="100%" height="100%" fill="#00000000" stroke="#00000000" stroke-width="2" />
218 <svg width="100%">
219 ${outputSvgString}
220 </svg>
221 </svg>`;
222};
223This 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.

Create a React heatmap chart showing historical orderbook levels, using the high performance SciChart.js chart library. Get free demo 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.