Using the default multi-chart sync APIs, create a multi-pane stock chart example with indicator panels. Zooming, panning, cursors are synchronised between the charts. This is a simpler way to create charts than subcharts, but will have a performance hit on some browsers.
drawExample.ts
angular.ts
ExampleDataProvider.ts
theme.ts
1import {
2 SciChartVerticalGroup,
3 DiscontinuousDateAxis,
4 EAxisAlignment,
5 SciChartSurface,
6 EAutoRange,
7 NumberRange,
8 NumericAxis,
9 OhlcDataSeries,
10 FastCandlestickRenderableSeries,
11 XyDataSeries,
12 calcAverageForArray,
13 FastLineRenderableSeries,
14 ZoomPanModifier,
15 ZoomExtentsModifier,
16 MouseWheelZoomModifier,
17 RolloverModifier,
18 FastBandRenderableSeries,
19 XyyDataSeries,
20 FastColumnRenderableSeries,
21 EXyDirection,
22 EFillPaletteMode,
23 EStrokePaletteMode,
24 IFillPaletteProvider,
25 IStrokePaletteProvider,
26 IRenderableSeries,
27 parseColorToUIntArgb,
28 ENumericFormat,
29 SmartDateLabelProvider,
30 XyMovingAverageFilter,
31 EDataSeriesField,
32 ELabelAlignment,
33 SeriesInfo,
34 EDataSeriesType,
35 OhlcSeriesInfo,
36 RolloverLegendSvgAnnotation,
37 SciChartOverview,
38 ESeriesType,
39 FastMountainRenderableSeries,
40 GradientParams,
41 Point,
42 TextAnnotation,
43 ECoordinateMode,
44 EHorizontalAnchorPoint,
45 EVerticalAnchorPoint,
46 EAnnotationLayer,
47} from "scichart";
48import { fetchMultiPaneData } from "../../../ExampleData/ExampleDataProvider";
49import { appTheme } from "../../../theme";
50
51const getTradingData = async (startPoints?: number, maxPoints?: number) => {
52 const { dateValues, openValues, highValues, lowValues, closeValues, volumeValues } = await fetchMultiPaneData();
53
54 if (maxPoints !== undefined) {
55 return {
56 dateValues: dateValues.slice(startPoints, startPoints + maxPoints),
57 openValues: openValues.slice(startPoints, startPoints + maxPoints),
58 highValues: highValues.slice(startPoints, startPoints + maxPoints),
59 lowValues: lowValues.slice(startPoints, startPoints + maxPoints),
60 closeValues: closeValues.slice(startPoints, startPoints + maxPoints),
61 volumeValues: volumeValues.slice(startPoints, startPoints + maxPoints),
62 };
63 }
64
65 return { dateValues, openValues, highValues, lowValues, closeValues, volumeValues };
66};
67
68// Override the standard legend displayed by RolloverModifier
69const getTooltipLegendTemplate = (seriesInfos: SeriesInfo[], svgAnnotation: RolloverLegendSvgAnnotation) => {
70 let outputSvgString = "";
71
72 // Foreach series there will be a seriesInfo supplied by SciChart. This contains info about the series under the house
73 seriesInfos.forEach((seriesInfo, index) => {
74 const y = 20 + index * 20;
75 const textColor = seriesInfo.stroke;
76 let legendText = seriesInfo.formattedYValue;
77 if (seriesInfo.dataSeriesType === EDataSeriesType.Ohlc) {
78 const o = seriesInfo as OhlcSeriesInfo;
79 legendText = `Open=${o.formattedOpenValue} High=${o.formattedHighValue} Low=${o.formattedLowValue} Close=${o.formattedCloseValue}`;
80 }
81 outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="${textColor}">
82 ${seriesInfo.seriesName}: ${legendText}
83 </text>`;
84 });
85
86 return `<svg width="100%" height="100%">
87 ${outputSvgString}
88 </svg>`;
89};
90
91// Override the Renderableseries to display on the scichart overview
92const getOverviewSeries = (defaultSeries: IRenderableSeries) => {
93 if (defaultSeries.type === ESeriesType.CandlestickSeries) {
94 // Swap the default candlestick series on the overview chart for a mountain series. Same data
95 return new FastMountainRenderableSeries(defaultSeries.parentSurface.webAssemblyContext2D, {
96 dataSeries: defaultSeries.dataSeries,
97 fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
98 { color: appTheme.VividSkyBlue + "77", offset: 0 },
99 { color: "Transparent", offset: 1 },
100 ]),
101 stroke: appTheme.VividSkyBlue,
102 });
103 }
104 // hide all other series
105 return undefined;
106};
107
108export const getChartsInitializationAPI = () => {
109 // We can group together charts using VerticalChartGroup type
110 const verticalGroup = new SciChartVerticalGroup();
111
112 const dataPromise = getTradingData();
113
114 let chart1XAxis: DiscontinuousDateAxis;
115 let chart2XAxis: DiscontinuousDateAxis;
116 let chart3XAxis: DiscontinuousDateAxis;
117 let priceChartSurface: SciChartSurface;
118 let macdChartSurface: SciChartSurface;
119 let rsiChartSurface: SciChartSurface;
120 const axisAlignment = EAxisAlignment.Right;
121
122 const upCol = appTheme.VividGreen;
123 const downCol = appTheme.MutedRed;
124 const opacity = "AA";
125
126 // CHART 1
127 const drawPriceChart = async (rootElement: string | HTMLDivElement) => {
128 const [chart, data] = await Promise.all([
129 SciChartSurface.create(rootElement, {
130 // prevent default size settings
131 disableAspect: true,
132 theme: appTheme.SciChartJsTheme,
133 }),
134 dataPromise,
135 ]);
136 const { wasmContext, sciChartSurface } = chart;
137 const { dateValues, openValues, highValues, lowValues, closeValues, volumeValues } = data;
138
139 chart1XAxis = new DiscontinuousDateAxis(wasmContext, {
140 drawLabels: false,
141 drawMajorTickLines: false,
142 drawMinorTickLines: false,
143 });
144 sciChartSurface.xAxes.add(chart1XAxis);
145
146 const yAxis = new NumericAxis(wasmContext, {
147 maxAutoTicks: 5,
148 autoRange: EAutoRange.Always,
149 growBy: new NumberRange(0.3, 0.11),
150 labelFormat: ENumericFormat.Decimal,
151 labelPrecision: 4,
152 cursorLabelFormat: ENumericFormat.Decimal,
153 cursorLabelPrecision: 4,
154 labelPrefix: "$",
155 axisAlignment,
156 });
157 sciChartSurface.yAxes.add(yAxis);
158
159 // OHLC DATA SERIES
160 const usdDataSeries = new OhlcDataSeries(wasmContext, {
161 dataSeriesName: "EUR/USD",
162 xValues: dateValues,
163 openValues,
164 highValues,
165 lowValues,
166 closeValues,
167 });
168 const fcRendSeries = new FastCandlestickRenderableSeries(wasmContext, {
169 dataSeries: usdDataSeries,
170 stroke: appTheme.ForegroundColor, // Used for legend template
171 brushUp: upCol + "77",
172 brushDown: downCol + "77",
173 strokeUp: upCol,
174 strokeDown: downCol,
175 });
176 sciChartSurface.renderableSeries.add(fcRendSeries);
177
178 // MA1 SERIES
179 const maLowDataSeries = new XyMovingAverageFilter(usdDataSeries, {
180 dataSeriesName: "MA 50 Low",
181 length: 50,
182 field: EDataSeriesField.Low,
183 });
184 const maLowRenderableSeries = new FastLineRenderableSeries(wasmContext, {
185 dataSeries: maLowDataSeries,
186 });
187 sciChartSurface.renderableSeries.add(maLowRenderableSeries);
188 maLowRenderableSeries.rolloverModifierProps.tooltipColor = "red";
189 maLowRenderableSeries.rolloverModifierProps.markerColor = "red";
190 maLowRenderableSeries.stroke = appTheme.VividPink;
191 maLowRenderableSeries.strokeThickness = 2;
192
193 // MA2 SERIES
194 const maHighDataSeries = new XyMovingAverageFilter(usdDataSeries, {
195 dataSeriesName: "MA 200 High",
196 length: 200,
197 field: EDataSeriesField.High,
198 });
199 const maHighRenderableSeries = new FastLineRenderableSeries(wasmContext, {
200 dataSeries: maHighDataSeries,
201 });
202 sciChartSurface.renderableSeries.add(maHighRenderableSeries);
203 maHighRenderableSeries.stroke = appTheme.VividSkyBlue;
204 maHighRenderableSeries.strokeThickness = 2;
205
206 // VOLUME SERIES
207 const yAxis2 = new NumericAxis(wasmContext, {
208 id: "yAxis2",
209 isVisible: false,
210 autoRange: EAutoRange.Always,
211 growBy: new NumberRange(0, 3),
212 });
213 sciChartSurface.yAxes.add(yAxis2);
214
215 const volumeRenderableSeries = new FastColumnRenderableSeries(wasmContext, {
216 yAxisId: "yAxis2",
217 dataSeries: new XyDataSeries(wasmContext, {
218 dataSeriesName: "Volume",
219 xValues: dateValues,
220 yValues: volumeValues,
221 }),
222 dataPointWidth: 0.5,
223 strokeThickness: 1,
224 paletteProvider: new VolumePaletteProvider(usdDataSeries, upCol + opacity, downCol + opacity),
225 });
226 sciChartSurface.renderableSeries.add(volumeRenderableSeries);
227
228 // Add a watermark annotation
229 const watermarkAnnotation = new TextAnnotation({
230 x1: 0.5,
231 y1: 0.5,
232 xCoordinateMode: ECoordinateMode.Relative,
233 yCoordinateMode: ECoordinateMode.Relative,
234 horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
235 verticalAnchorPoint: EVerticalAnchorPoint.Center,
236 opacity: 0.17,
237 textColor: appTheme.ForegroundColor,
238 fontSize: 48,
239 fontWeight: "Bold",
240 text: "Euro / U.S. Dollar - Daily",
241 });
242 sciChartSurface.annotations.add(watermarkAnnotation);
243
244 // MODIFIERS
245 sciChartSurface.chartModifiers.add(new ZoomPanModifier({ enableZoom: true }));
246 sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
247 sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier());
248 sciChartSurface.chartModifiers.add(
249 new RolloverModifier({
250 modifierGroup: "cursorGroup",
251 showTooltip: false,
252 snapToDataPoint: true,
253 tooltipLegendTemplate: getTooltipLegendTemplate,
254 })
255 );
256
257 verticalGroup.addSurfaceToGroup(sciChartSurface);
258 priceChartSurface = sciChartSurface;
259 sciChartSurface.zoomExtentsX();
260
261 return { wasmContext, sciChartSurface };
262 };
263
264 // CHART 2 - MACD
265 const drawMacdChart = async (rootElement: string | HTMLDivElement) => {
266 const [{ wasmContext, sciChartSurface }, { dateValues, closeValues }] = await Promise.all([
267 SciChartSurface.create(rootElement, {
268 // prevent default size settings
269 disableAspect: true,
270 theme: appTheme.SciChartJsTheme,
271 }),
272 dataPromise,
273 ]);
274
275 chart2XAxis = new DiscontinuousDateAxis(wasmContext, {
276 drawLabels: false,
277 drawMajorTickLines: false,
278 drawMinorTickLines: false,
279 });
280 sciChartSurface.xAxes.add(chart2XAxis);
281
282 const yAxis = new NumericAxis(wasmContext, {
283 autoRange: EAutoRange.Always,
284 growBy: new NumberRange(0.1, 0.1),
285 axisAlignment,
286 labelPrecision: 2,
287 cursorLabelPrecision: 2,
288 labelStyle: { alignment: ELabelAlignment.Right },
289 });
290 yAxis.labelProvider.numericFormat = ENumericFormat.Decimal;
291 sciChartSurface.yAxes.add(yAxis);
292
293 const macdArray: number[] = [];
294 const signalArray: number[] = [];
295 const divergenceArray: number[] = [];
296 for (let i = 0; i < dateValues.length; i++) {
297 const maSlow = calcAverageForArray(closeValues, 12, i);
298 const maFast = calcAverageForArray(closeValues, 25, i);
299 const macd = maSlow - maFast;
300 macdArray.push(macd);
301 const signal = calcAverageForArray(macdArray, 9, i);
302 signalArray.push(signal);
303 const divergence = macd - signal;
304 divergenceArray.push(divergence);
305 }
306
307 const bandSeries = new FastBandRenderableSeries(wasmContext, {
308 dataSeries: new XyyDataSeries(wasmContext, {
309 dataSeriesName: "MACD",
310 xValues: dateValues,
311 yValues: signalArray,
312 y1Values: macdArray,
313 }),
314 stroke: downCol,
315 strokeY1: upCol,
316 fill: upCol + "77",
317 fillY1: downCol + "77",
318 });
319 sciChartSurface.renderableSeries.add(bandSeries);
320
321 const columnSeries = new FastColumnRenderableSeries(wasmContext, {
322 dataSeries: new XyDataSeries(wasmContext, {
323 dataSeriesName: "Divergence",
324 xValues: dateValues,
325 yValues: divergenceArray,
326 }),
327 paletteProvider: new MacdHistogramPaletteProvider(upCol + "AA", downCol + "AA"),
328 dataPointWidth: 0.5,
329 });
330 sciChartSurface.renderableSeries.add(columnSeries);
331
332 sciChartSurface.chartModifiers.add(
333 new ZoomPanModifier({ enableZoom: true, xyDirection: EXyDirection.XDirection })
334 );
335 sciChartSurface.chartModifiers.add(new ZoomExtentsModifier({ xyDirection: EXyDirection.XDirection }));
336 sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }));
337 sciChartSurface.chartModifiers.add(
338 new RolloverModifier({
339 modifierGroup: "cursorGroup",
340 showTooltip: false,
341 snapToDataPoint: true,
342 tooltipLegendTemplate: getTooltipLegendTemplate,
343 })
344 );
345
346 verticalGroup.addSurfaceToGroup(sciChartSurface);
347 macdChartSurface = sciChartSurface;
348
349 return { wasmContext, sciChartSurface };
350 };
351
352 // CHART 3 - RSI
353 const drawRsiChart = async (rootElement: string | HTMLDivElement) => {
354 const [{ wasmContext, sciChartSurface }, { dateValues, closeValues }] = await Promise.all([
355 SciChartSurface.create(rootElement, {
356 // prevent default size settings
357 disableAspect: true,
358 theme: appTheme.SciChartJsTheme,
359 }),
360 dataPromise,
361 ]);
362
363 chart3XAxis = new DiscontinuousDateAxis(wasmContext, {
364 autoRange: EAutoRange.Once,
365 labelProvider: new SmartDateLabelProvider(),
366 });
367 sciChartSurface.xAxes.add(chart3XAxis);
368
369 const yAxis = new NumericAxis(wasmContext, {
370 autoRange: EAutoRange.Always,
371 growBy: new NumberRange(0.1, 0.1),
372 labelPrecision: 0,
373 cursorLabelPrecision: 0,
374 axisAlignment,
375 labelStyle: { alignment: ELabelAlignment.Right },
376 });
377 yAxis.labelProvider.numericFormat = ENumericFormat.Decimal;
378 sciChartSurface.yAxes.add(yAxis);
379
380 const RSI_PERIOD = 14;
381 const rsiArray: number[] = [];
382 const gainArray: number[] = [];
383 const lossArray: number[] = [];
384
385 if (dateValues.length) {
386 rsiArray.push(NaN);
387 gainArray.push(NaN);
388 lossArray.push(NaN);
389 }
390
391 for (let i = 1; i < dateValues.length; i++) {
392 const previousClose = closeValues[i - 1];
393 const currentClose = closeValues[i];
394 const gain = currentClose > previousClose ? currentClose - previousClose : 0;
395 gainArray.push(gain);
396 const loss = previousClose > currentClose ? previousClose - currentClose : 0;
397 lossArray.push(loss);
398 const relativeStrength =
399 calcAverageForArray(gainArray, RSI_PERIOD) / calcAverageForArray(lossArray, RSI_PERIOD);
400 const rsi = 100 - 100 / (1 + relativeStrength);
401 rsiArray.push(rsi);
402 }
403 const rsiRenderableSeries = new FastLineRenderableSeries(wasmContext, {
404 dataSeries: new XyDataSeries(wasmContext, {
405 dataSeriesName: "RSI",
406 xValues: dateValues,
407 yValues: rsiArray,
408 }),
409 stroke: appTheme.MutedBlue,
410 strokeThickness: 2,
411 });
412 sciChartSurface.renderableSeries.add(rsiRenderableSeries);
413
414 sciChartSurface.chartModifiers.add(
415 new ZoomPanModifier({ enableZoom: true, xyDirection: EXyDirection.XDirection })
416 );
417 sciChartSurface.chartModifiers.add(new ZoomExtentsModifier({ xyDirection: EXyDirection.XDirection }));
418 sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }));
419 sciChartSurface.chartModifiers.add(
420 new RolloverModifier({
421 modifierGroup: "cursorGroup",
422 showTooltip: false,
423 snapToDataPoint: true,
424 tooltipLegendTemplate: getTooltipLegendTemplate,
425 })
426 );
427
428 verticalGroup.addSurfaceToGroup(sciChartSurface);
429 rsiChartSurface = sciChartSurface;
430
431 return { wasmContext, sciChartSurface };
432 };
433
434 // DRAW OVERVIEW
435 // Must be done after main chart creation
436 const drawOverview = (mainSurface: SciChartSurface) => async (rootElement: string | HTMLDivElement) => {
437 const overview = await SciChartOverview.create(mainSurface, rootElement, {
438 // prevent default size settings
439 disableAspect: true,
440 theme: appTheme.SciChartJsTheme,
441 transformRenderableSeries: getOverviewSeries,
442 });
443
444 return { sciChartSurface: overview.overviewSciChartSurface };
445 };
446
447 const configureAfterInit = () => {
448 const synchronizeAxes = () => {
449 // TODO refactor using AxisSynchroniser
450
451 // SYNCHRONIZE VISIBLE RANGES
452 chart1XAxis.visibleRangeChanged.subscribe((data1) => {
453 chart2XAxis.visibleRange = data1.visibleRange;
454 chart3XAxis.visibleRange = data1.visibleRange;
455 });
456 chart2XAxis.visibleRangeChanged.subscribe((data1) => {
457 chart1XAxis.visibleRange = data1.visibleRange;
458 chart3XAxis.visibleRange = data1.visibleRange;
459 });
460 chart3XAxis.visibleRangeChanged.subscribe((data1) => {
461 chart1XAxis.visibleRange = data1.visibleRange;
462 chart2XAxis.visibleRange = data1.visibleRange;
463 });
464 };
465
466 synchronizeAxes();
467
468 // Force showing the latest 200 bars
469 const twoHundredDaysOfSeconds = 60 * 60 * 24 * 200;
470 chart1XAxis.visibleRange = new NumberRange(
471 chart1XAxis.visibleRange.max - twoHundredDaysOfSeconds,
472 chart1XAxis.visibleRange.max
473 );
474 };
475
476 return { drawPriceChart, drawMacdChart, drawRsiChart, drawOverview, configureAfterInit };
477};
478
479/**
480 * An example PaletteProvider applied to the volume column series. It will return green / red
481 * fills and strokes when the main price data bar is up or down
482 */
483class VolumePaletteProvider implements IStrokePaletteProvider, IFillPaletteProvider {
484 public readonly strokePaletteMode: EStrokePaletteMode = EStrokePaletteMode.SOLID;
485 public readonly fillPaletteMode: EFillPaletteMode = EFillPaletteMode.SOLID;
486 private priceData: OhlcDataSeries;
487 private volumeUpArgb: number;
488 private volumnDownArgb: number;
489
490 constructor(priceData: OhlcDataSeries, volumeUpColor: string, volumeDownColor: string) {
491 this.priceData = priceData;
492 this.volumeUpArgb = parseColorToUIntArgb(volumeUpColor);
493 this.volumnDownArgb = parseColorToUIntArgb(volumeDownColor);
494 }
495
496 onAttached(parentSeries: IRenderableSeries): void {}
497
498 onDetached(): void {}
499
500 overrideFillArgb(xValue: number, yValue: number, index: number): number {
501 const open = this.priceData.getNativeOpenValues().get(index);
502 const close = this.priceData.getNativeCloseValues().get(index);
503
504 return close >= open ? this.volumeUpArgb : this.volumnDownArgb;
505 }
506
507 overrideStrokeArgb(xValue: number, yValue: number, index: number): number {
508 return this.overrideFillArgb(xValue, yValue, index);
509 }
510}
511
512// tslint:disable-next-line:max-classes-per-file
513class MacdHistogramPaletteProvider implements IStrokePaletteProvider, IFillPaletteProvider {
514 public readonly strokePaletteMode: EStrokePaletteMode = EStrokePaletteMode.SOLID;
515 public readonly fillPaletteMode: EFillPaletteMode = EFillPaletteMode.SOLID;
516 private aboveZeroArgb: number;
517 private belowZeroArgb: number;
518
519 constructor(aboveZeroColor: string, belowZeroColor: string) {
520 this.aboveZeroArgb = parseColorToUIntArgb(aboveZeroColor);
521 this.belowZeroArgb = parseColorToUIntArgb(belowZeroColor);
522 }
523
524 onAttached(parentSeries: IRenderableSeries): void {}
525
526 onDetached(): void {}
527
528 overrideFillArgb(xValue: number, yValue: number, index: number): number {
529 return yValue >= 0 ? this.aboveZeroArgb : this.belowZeroArgb;
530 }
531
532 overrideStrokeArgb(xValue: number, yValue: number, index: number): number {
533 return this.overrideFillArgb(xValue, yValue, index);
534 }
535}
536This Angular example demonstrates the creation of a synchronized multi-pane stock chart application using SciChart.js. The application renders several chart panels including a primary price chart, MACD and RSI indicator panels, and an overview chart that appears once the main charts are initialized. It leverages Angular standalone components for modular and dynamic chart rendering.
The core of the example is contained within the Angular component where multiple instances of the SciChart chart component are instantiated. These instances are initialized with functions from the initialization API, such as drawPriceChart, drawMacdChart, drawRsiChart, and drawOverview. Each initialization function configures its respective chart using the SciChartSurface API, allowing for dynamic chart creation and synchronization. Axis synchronization is implemented by subscribing to the visibleRangeChanged events on the x-axes across the different chart panels. For more details on subscribing to these events, refer to the Axis Ranging - How to Listen to VisibleRange Changes documentation. Additionally, asynchronous initialization of charts is handled in a way that once the primary charts are ready, the overview chart is dynamically rendered using Angular mechanisms. This approach is aligned with Angular’s best practices for managing asynchronous operations, as discussed in Component Lifecycle - Angular.
The example not only showcases real-time charting capabilities with synchronized zooming, panning, and cursor interactions but also includes advanced customizations like custom palette providers for volume and MACD histogram series. The flexible use of CSS flexbox layout ensures that the UI is responsive and that charts adjust to various screen sizes. The implementation highlights the importance of performance considerations when dealing with dynamic, high-performance charting applications in Angular. For more on performance optimization in multi-chart scenarios, see How to Link JavaScript Charts and Synchronise zooming, panning ....
This example is built entirely on Angular, using standalone components to integrate SciChart.js seamlessly into an Angular application. The use of scichart-angular makes it easy to pass initialization functions as component properties, thereby streamlining the component interaction process as described in Component interaction - Angular. The synchronization of visual ranges across multiple charts ensures a coherent user experience, and the dynamic rendering approach improves both performance and flexibility. For additional insights into dynamic component rendering and lazy loading in Angular, consider reviewing resources such as Implementing Angular Lazy Loading with Dynamic Chart ....
Overall, this Angular multi-pane stock chart example demonstrates effective usage of SciChart.js in an Angular environment, emphasizing modular design, performance optimization, and advanced visual sync capabilities for stock charting applications.

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

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

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.

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

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

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

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

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

Build high-performance financial apps with Angular trading drawing tools. Try SciChart’s demo to see the advanced drawing tools for trading apps in action.

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

Deploy the ultimate Angular freehand drawing tool in your app. Render complex freehand drawing tools with a smooth UI experience. Try the SciChart demo today.