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
index.tsx
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 example demonstrates a multi-pane stock chart built exclusively with JavaScript and SciChart.js. It features a primary price chart along with indicator panels for MACD and RSI, plus an additional overview chart that synchronizes with the main charts.
The charts are initialized asynchronously using functions such as drawPriceChart, drawMacdChart, drawRsiChart, and drawOverview. These functions leverage the SciChartSurface.create method to establish high-performance WebGL charts. Custom palette providers are implemented for the volume and MACD histogram series by extending the IStrokePaletteProvider and IFillPaletteProvider interfaces, as detailed in The PaletteProvider API. Additionally, the charts are grouped using the SciChartVerticalGroup and synchronized by subscribing to the x-axis visibleRangeChanged events, which is explained in the Tutorial 09 - Linking Multiple Charts.
The example supports real-time chart updates, advanced technical analysis through moving average filters (see XyMovingAverageFilter API Documentation), and interactive tooltips configured via the RolloverModifier. The integration of chart modifiers such as ZoomPanModifier, ZoomExtentsModifier, and MouseWheelZoomModifier ensures seamless navigation and interaction.
Built with JavaScript, this implementation follows asynchronous initialization best practices outlined in Getting Started with SciChart JS, and employs performance optimization techniques recommended in Performance Tips & Tricks. The synchronized overview chart, created using the transformRenderableSeries feature, further enhances usability by providing a compact visual summary, as described in How to Link JavaScript Charts and Synchronise zooming, panning ....
Overall, the example offers a comprehensive demonstration of chart synchronization, advanced customization, and efficient performance optimizations, making it a valuable reference for developers building high-performance financial charting applications using SciChart.js and JavaScript.

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.

Create a JavaScript 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 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.

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.

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

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