Angular 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} 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};
219

Angular Multi-Pane Stock Charts using Subcharts

Overview

This example demonstrates an advanced multi-pane stock chart implementation in Angular using SciChart.js. The chart leverages the new Sub-Charts API to render multiple synchronized panels—including a candlestick view with OHLC data, MACD and RSI indicators, and a volume histogram—on a single WebGL surface. This design not only optimizes performance by sharing the WebGL context but also ensures that interactions such as zooming, panning, and crosshair movements are synchronized across all sub-charts.

Technical Implementation

The chart is constructed using the Builder API with a JSON configuration that defines the main chart and its sub-charts. Each pane is configured with its own axes, series, and custom modifiers to handle interactions and real-time updates. The implementation uses custom SVG annotations and chart modifiers to update financial legends and manage crosshair behavior. For more details on using the Sub-Charts API in Angular, refer to the SubCharts API documentation and explore the capabilities outlined in the Advanced JavaScript Chart and Graph Library.

Features and Capabilities

The example exhibits several advanced features including real-time legend updates, dynamic custom annotations for displaying OHLC and indicator values, and conditional series coloring using custom palette providers. It also includes custom event handling for drag-resize functionality, which enables dynamic layout adjustments when the user resizes chart panes. These capabilities ensure that the chart remains highly interactive and performance-optimized, as detailed in the Getting Started with SciChart JS guide.

Integration and Best Practices

This Angular implementation follows best practices by encapsulating chart initialization and event handling within Angular components. Custom chart modifiers are integrated to synchronize zooming, panning, and crosshair functionality across the sub-charts. Moreover, the example employs dynamic layout and resizing logic to adjust pane sizes on the fly, ensuring a responsive and efficient charting experience. Developers can further enhance performance by reviewing the Performance Tips & Tricks and techniques for synchronizing chart interactions discussed in How to Link JavaScript Charts and Synchronise zooming, panning, and crosshairs.

angular Chart Examples & Demos

See Also: Financial Charts (9 Demos)

Angular Candlestick Chart | Online JavaScript Chart Examples

Angular Candlestick Chart

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

Angular OHLC Chart | Angular Charts | SciChart.js Demo

Angular OHLC Chart

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

Angular Realtime Ticking Stock Chart | SciChart.js Demo

Angular Realtime Ticking Stock Charts

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

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

Angular Orderbook Heatmap

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

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

Angular Multi-Pane Stock Chart | View JavaScript Charts

Angular Multi-Pane Stock Charts using Sync Multi-Chart

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

Angular Market Depth Chart | Angular Charts | SciChart.js

Angular Market Depth Chart

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

Angular Chart Hoverable Buy Sell Marker Annotations

Angular Chart Hoverable Buy Sell Marker Annotations

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

Angular User Annotated Stock Chart | SciChart.js Demo

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