JavaScript Multi-Pane Stock Charts using Subcharts

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.

Fullscreen

Edit

 Edit

Docs

index.tsx

ExampleDataProvider.ts

theme.ts

FinChartLegendAnnotation.ts

FinChartLegendModifier.ts

Copy to clipboard
Minimise
Fullscreen
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};
227

JavaScript Multi-Pane Stock Charts using Subcharts

Overview

This 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.

Technical Implementation

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.

Features and Capabilities

The example showcases a wide array of advanced features including:

  • Sub-Charts API usage: All sub-charts share a single WebGL context, significantly boosting performance and ensuring synchronized interactions. More details on this approach can be found in the Creating Resizable Multi Pane Charts with SubCharts documentation.
  • Axis Synchronization: Axis ranges across different panes are linked using event subscriptions, ensuring a unified zoom and pan experience as described in the Synchronizing Multiple Charts documentation.
  • Custom Chart Modifiers and SVG Annotations: Interactive SVG legends are generated via custom modifiers that update in real-time based on pointer events.
  • Dynamic Technical Indicator Calculation: The MACD and RSI values are computed on-the-fly using JavaScript, allowing for immediate feedback as the data updates.
  • Custom Palette Providers: Conditional coloring is applied through custom palette providers to visually distinguish between rising and falling values. Developers may consult the The PaletteProvider API for further customization options.

Integration and Best Practices

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.

javascript Chart Examples & Demos

See Also: Financial Charts (13 Demos)

JavaScript Candlestick Chart | Online JavaScript Chart Examples

JavaScript Candlestick Chart

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

JavaScript OHLC Chart | Javascript Charts | SciChart.js Demo

JavaScript OHLC Chart

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

JavaScript Realtime Ticking Stock Chart | SciChart.js

JavaScript Realtime Ticking Stock Charts

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.

JavaScript Orderbook Heatmap | Javascript Charts | SciChart.js

JavaScript Orderbook Heatmap

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

Tenor Curves Demo | Javascript Charts | SciChart.js Demo

Tenor Curves Demo

Demonstrating the capability of SciChart.js to create a composite 2D &amp; 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.

JavaScript Multi-Pane Stock Chart | View JavaScript Charts

JavaScript Multi-Pane Stock Charts using Sync Multi-Chart

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

Financial Data Filters | Javascript Charts | SciChart.js Demo

Financial Data Filters

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

JavaScript Market Depth Chart | Javascript Charts | SciChart.js

JavaScript Market Depth Chart

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

JavaScript Chart Hoverable Buy Sell Marker Annotations

JavaScript Chart Hoverable Buy Sell Marker Annotations

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

JavaScript User Annotated Stock Chart | SciChart.js Demo

JavaScript User Annotated Stock Chart

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.

JavaScript Trading Drawing Tools | Javascript Charts | SciChart.js

JavaScript Trading Drawing Tools

Create an interactive JavaScript trading charts for technical analysis. Trading Drawing Tools Demo, which shows how to use Polylines, Extended Lines, Rays, Channels, Pitchforks, Pitchfans, Fibonnaci Retracements, Measure, Stop Loss and Take Profit chart drawing tools for Technical Analysis.

JavaScript Custom Paired Dashed Polyline Annotation

JavaScript Custom Paired Dashed Polyline Annotation

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

JavaScript Freehand Drawing Tools | SciChart.js Demo

JavaScript Freehand Drawing Tools

An example of using JavaScript FreehandDrawingModifier for arbitrary drawing on trading and financial charts. Can be used for drawing trends, arrow, markers, text, etc.

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.