React 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";
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};
223

Sub Chart Stock Charts Example with React

Overview

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

Technical Implementation

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.

Features and Capabilities

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.

Integration and Best Practices

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.

react Chart Examples & Demos

See Also: Financial Charts (9 Demos)

React Candlestick Chart | Online JavaScript Chart Examples

React Candlestick Chart

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

React OHLC Chart | React Charts | SciChart.js Demo

React OHLC Chart

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

React Realtime Ticking Stock Chart | SciChart.js Demo

React Realtime Ticking Stock Charts

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.

NEW!
React Orderbook Heatmap | React Charts | SciChart.js Demo

Order Book Heatmap

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

Tenor Curves Demo | React 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.

React Multi-Pane Stock Chart | View JavaScript Charts

React Multi-Pane Stock Charts using Sync Multi-Chart

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

React Market Depth Chart | React Charts | SciChart.js Demo

React Market Depth Chart

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

React Chart Hoverable Buy Sell Marker Annotations | SciChart

React Chart Hoverable Buy Sell Marker Annotations

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

React User Annotated Stock Chart | React Charts | SciChart.js

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

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