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} from "scichart";
14
15export type TFinanceLegendTemplate = (legendAnnotation: FinChartLegendAnnotation) => string;
16
17export interface IFinChartLegendAnnotationOptions extends ISvgAnnotationBaseOptions {
18 template?: TFinanceLegendTemplate;
19 xIndex?: number;
20 paneId?: string;
21 offsetX?: number;
22 offsetY?: number;
23 title?: string;
24}
25
26/**
27 * A Tooltip Annotation which provides an SVG tooltip over the chart. Used by the {@link RolloverModifier}
28 */
29export class FinChartLegendAnnotation extends SvgAnnotationBase {
30 /** @inheritDoc */
31 public readonly type = EAnnotationType.SVG;
32 public readonly sciFinanceChart: SciChartSurface;
33 private templateProperty: TFinanceLegendTemplate = defaultFinanceLegendTemplate;
34 private xIndexProperty: number;
35 private paneIdProperty: string;
36 private offsetXProperty: number;
37 private offsetYProperty: number;
38 private activeSciChartSurfaceProperty: ISciChartSubSurface;
39 private titleProperty: string = "Default Title";
40
41 /**
42 * Creates an instance of the {@link FinChartLegendAnnotation}
43 * @param sciFinanceChart
44 * @param options
45 */
46 constructor(sciFinanceChart: SciChartSurface, options?: IFinChartLegendAnnotationOptions) {
47 super(options);
48 this.sciFinanceChart = sciFinanceChart;
49 this.isHidden = true;
50 this.templateProperty = options?.template ?? this.templateProperty;
51 this.xIndexProperty = options?.xIndex ?? this.xIndexProperty;
52 this.paneIdProperty = options?.paneId ?? this.paneIdProperty;
53 this.offsetXProperty = options?.offsetX ?? this.offsetXProperty;
54 this.offsetYProperty = options?.offsetY ?? this.offsetYProperty;
55 this.titleProperty = options?.title ?? this.titleProperty;
56 }
57
58 public get template() {
59 return this.templateProperty;
60 }
61
62 public set template(value) {
63 this.templateProperty = value;
64 }
65
66 public get xIndex() {
67 return this.xIndexProperty;
68 }
69
70 public set xIndex(value) {
71 this.xIndexProperty = value;
72 }
73
74 public get paneId() {
75 return this.paneIdProperty;
76 }
77
78 public set paneId(value) {
79 this.paneIdProperty = value;
80 }
81
82 public get offsetX() {
83 return this.offsetXProperty;
84 }
85
86 public set offsetX(value) {
87 this.offsetXProperty = value;
88 }
89
90 public get offsetY() {
91 return this.offsetYProperty;
92 }
93
94 public set offsetY(value) {
95 this.offsetYProperty = value;
96 }
97
98 public get activeSciChartSurface() {
99 return this.activeSciChartSurfaceProperty;
100 }
101
102 public set activeSciChartSurface(value) {
103 this.activeSciChartSurfaceProperty = value;
104 }
105
106 public get title() {
107 return this.titleProperty;
108 }
109
110 public set title(value) {
111 this.titleProperty = value;
112 }
113
114 public update(
115 xCalc: CoordinateCalculatorBase,
116 yCalc: CoordinateCalculatorBase,
117 xCoordSvgTrans: number,
118 yCoordSvgTrans: number
119 ): void {
120 if (this.svg) {
121 this.delete();
122 }
123 this.create(xCalc, yCalc, xCoordSvgTrans, yCoordSvgTrans);
124 }
125
126 protected create(
127 xCalc: CoordinateCalculatorBase,
128 yCalc: CoordinateCalculatorBase,
129 xCoordSvgTrans: number,
130 yCoordSvgTrans: number
131 ) {
132 if (this.template) {
133 let index: number = -1;
134 if (this.xAxis.type === EAxisType.CategoryAxis) {
135 index = Math.round(xCalc.getDataValue(this.x1));
136 } else if (this.xAxis.type === EAxisType.DiscontinuousDateAxis) {
137 const indexCoordCalc = xCalc as IndexCoordinateCalculator;
138 const val = indexCoordCalc.getDataValue(this.x1);
139 index = Math.round(indexCoordCalc.indexCalculator.GetIndex(val));
140 }
141
142 this.xIndex = this.isMouseOverSeriesArea ? index : undefined;
143 if (this.xIndex === undefined) {
144 return;
145 }
146 const svgString = this.template(this);
147 const svgNode = document.createRange().createContextualFragment(svgString);
148 this.svgRoot.appendChild(svgNode);
149 this.setSvg(this.svgRoot.lastChild as SVGElement);
150 this.svg.setAttribute("x", (xCoordSvgTrans + this.offsetX).toString());
151 this.svg.setAttribute("y", (yCoordSvgTrans + this.offsetY).toString());
152 }
153 }
154
155 private get isMouseOverSeriesArea() {
156 return this.activeSciChartSurface;
157 }
158}
159
160/** @ignore */
161const defaultFinanceLegendTemplate: TFinanceLegendTemplate = (la: FinChartLegendAnnotation): string => {
162 const outputStrings: string[] = [];
163 const subSurface = la.sciFinanceChart.subCharts.find((study) => study.id === la.paneId);
164 let outputStr = "";
165 if (la.xIndex >= 0) {
166 subSurface.renderableSeries.asArray().forEach(({ dataSeries }) => {
167 switch (dataSeries.type) {
168 case EDataSeriesType.Ohlc: {
169 const openValues = (dataSeries as OhlcDataSeries).getNativeOpenValues();
170 const highValues = (dataSeries as OhlcDataSeries).getNativeHighValues();
171 const lowValues = (dataSeries as OhlcDataSeries).getNativeLowValues();
172 const closeValues = (dataSeries as OhlcDataSeries).getNativeCloseValues();
173
174 const openValue = openValues.get(la.xIndex);
175 const highValue = highValues.get(la.xIndex);
176 const lowValue = lowValues.get(la.xIndex);
177 const closeValue = closeValues.get(la.xIndex);
178
179 outputStr += `${dataSeries.dataSeriesName} O: ${openValue} H: ${highValue} L: ${lowValue} C: ${closeValue}`;
180 break;
181 }
182
183 case EDataSeriesType.Xyy: {
184 const yValues = dataSeries.getNativeYValues();
185 const y1Values = (dataSeries as XyyDataSeries).getNativeY1Values();
186 const yValue = yValues.get(la.xIndex).toFixed(4);
187 const y1Value = y1Values.get(la.xIndex).toFixed(4);
188 outputStr += `${dataSeries.dataSeriesName} Y: ${yValue} Y1: ${y1Value}`;
189
190 break;
191 }
192
193 default: {
194 const yValues = dataSeries.getNativeYValues();
195 const yValue = yValues.get(la.xIndex).toFixed(4);
196 outputStr += `${dataSeries.dataSeriesName}: ${yValue}`;
197 }
198 }
199
200 if (outputStr) {
201 outputStrings.push(outputStr);
202 outputStr = "";
203 }
204 });
205 }
206
207 let outputSvgString = "";
208 outputStrings.forEach((outputStr, index) => {
209 const y = 30 + index * 20;
210 outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="lightblue">${outputStr}</text>`;
211 });
212 return `<svg width="800" height="200">
213 <rect width="100%" height="100%" fill="#00000000" stroke="#00000000" stroke-width="2" />
214 <svg width="100%">
215 ${outputSvgString}
216 </svg>
217 </svg>`;
218};
219This example demonstrates how to implement a high-performance multi-pane stock chart using JavaScript. It leverages the SciChart.js Sub-Charts API to render several synchronized panes—including a candlestick series, a MACD indicator with divergence and band series, and an RSI chart—on a single shared WebGL chart surface. This approach optimizes rendering performance while keeping interactions such as zooming, panning, and crosshair updates in sync across all panes.
The chart is constructed using the Builder API with JSON configuration via the call to chartBuilder.build2DChart(). This method initializes the primary SciChartSurface along with multiple sub-chart surfaces positioned dynamically using Rect definitions. For enhanced interactivity, custom chart modifiers and SVG annotations, such as the FinChartLegendModifier and FinChartLegendAnnotation, are implemented to provide real-time tooltips and interactive legends. These modifiers update dynamically based on mouse movements and are closely integrated with the axis synchronization logic, which is achieved by subscribing to the axis.visibleRangeChanged event on the x-axes of each sub-chart. For further technical details on creating custom annotations, refer to the Tutorial 06 - Adding Annotations.
The example showcases a wide array of advanced features including:
This implementation is built entirely in JavaScript and follows best practices for high-performance charting with SciChart.js. The use of a shared WebGL context, efficient JSON-based Builder API configuration, and dynamically synchronized axes demonstrates a robust approach to trading chart applications. In addition, the example includes custom event handling for drag-resize logic, which allows users to adjust the sub-chart pane sizes on the fly. To learn more about optimizing performance, developers can review the Performance Tips & Tricks guide.

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

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

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

Create a Javascript 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 JavaScript Multi-Pane Candlestick / Stock Chart with indicator panels, synchronized zooming, panning and cursors. Get your free trial of SciChart.js now.

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

Demonstrates how to place Buy/Sell arrow markers on a JavaScript 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.