drawExample.ts
index.tsx
tradingAnnotationExampleUtils.ts
1import { buildAnnotations, ERenderLayer, Thickness, IAnnotation } from "scichart";
2import {
3 EAnnotationVisibilityMode,
4 EFibonacciLabelColorMode,
5 EFibonacciLabelPlacement,
6 ETradingAnnotationType,
7 ChannelAnnotation,
8 FibonacciRetracementAnnotation,
9 ExtendedLineAnnotation,
10 MultiPointAnnotationPlacementModifier,
11 TFibonacciLevelLabelFormatParams,
12 FreehandDrawingAnnotation,
13 FreehandDrawingModifier,
14 IMultiPointAnnotationBaseOptions,
15 EMultiPointLabelAnchorMode,
16 EAxisLabelDrawMode,
17} from "scichart-financial-tools";
18import {
19 addDefaultFinancialModifiers,
20 createFinancialChart,
21 createTradingAnnotationOptions,
22 defaultSnapToCandleOptions,
23 FIB_REGION_COLORS,
24 TRADING_ANNOTATION_COLORS,
25} from "../_shared/tradingAnnotationExampleUtils";
26
27type TStartToolOptions = {
28 snapToCandle?: boolean;
29 extendStart?: boolean;
30 extendEnd?: boolean;
31 verticalOnly?: boolean;
32 lockedAspect?: boolean;
33};
34
35const CHANNEL_LABEL_PAIRS = [
36 [0, 1],
37 [2, 3],
38] as const;
39
40const PITCH_LABEL_PAIRS = [[1, 2]] as const;
41
42export const drawExample = async (rootElement: string | HTMLDivElement) => {
43 const ctx = await createFinancialChart(rootElement, {
44 volatility: 0.0028,
45 title: "BTC / USDT - Drawing Tools",
46 startDate: new Date("2024-01-01T00:00:00Z"),
47 dataSeed: 133337,
48 });
49 const { sciChartSurface, candlestickSeries, xAt, yAt } = ctx;
50
51 const placementModifier = new MultiPointAnnotationPlacementModifier({
52 isPlacing: false,
53 keepPlacingAfterComplete: false,
54 });
55 const freehandDrawingModifier = new FreehandDrawingModifier({
56 isDrawing: false,
57 keepDrawingAfterComplete: true,
58 pointSamplingDistancePx: 0.5,
59 simplifyTolerancePx: 1,
60 maxPoints: 5000,
61 });
62
63 addDefaultFinancialModifiers(sciChartSurface);
64 sciChartSurface.chartModifiers.add(freehandDrawingModifier, placementModifier);
65
66 const preparePlacementOptions = <T extends IMultiPointAnnotationBaseOptions>(options: T): T => options;
67
68 const stopActiveTools = () => {
69 console.log('stop active tools');
70 placementModifier.stopPlacement(true);
71 freehandDrawingModifier.stopDrawing(true);
72 };
73
74 const startFreehand = (lockedAspect = false) => {
75 placementModifier.stopPlacement(true);
76 freehandDrawingModifier.startDrawing({
77 isEditable: true,
78 stroke: lockedAspect ? TRADING_ANNOTATION_COLORS.lockedFreehand : TRADING_ANNOTATION_COLORS.freehand,
79 strokeThickness: 2,
80 showBoxOutline: true,
81 showBoxOutlineOnlyWhenSelected: true,
82 boxOutlineStrokeDashArray: [6, 4],
83 keepAspectRatioOnResize: lockedAspect,
84 forcedAspectRatio: lockedAspect ? 1 : undefined,
85 allowMove: true,
86 annotationsGripsRadius: 4,
87 annotationsGripsStroke: lockedAspect
88 ? TRADING_ANNOTATION_COLORS.lockedFreehand
89 : TRADING_ANNOTATION_COLORS.freehand,
90 gripSvgTemplate: (annotation: any, x: number, y: number) => {
91 const ann = annotation as FreehandDrawingAnnotation;
92 return `<circle cx="${x}" cy="${y}" r="${ann.annotationsGripsRadius}" fill="${ann.parentSurface.background}" stroke="${ann.annotationsGripsStroke}" stroke-width="${ann.strokeThickness}" />`;
93 },
94 });
95 };
96
97 const startTool = (tool: ETradingAnnotationType, options: TStartToolOptions = {}) => {
98 stopActiveTools();
99
100 switch (tool) {
101 case ETradingAnnotationType.PolyLineAnnotation: {
102 const color = options.snapToCandle
103 ? TRADING_ANNOTATION_COLORS.snappedPolyline
104 : TRADING_ANNOTATION_COLORS.freePolyline;
105
106 placementModifier.startPlacement({
107 type: ETradingAnnotationType.PolyLineAnnotation,
108 options: {
109 ...createTradingAnnotationOptions(options.snapToCandle ? "SNP" : "PLY", 5),
110 ...(options.snapToCandle ? defaultSnapToCandleOptions(candlestickSeries.id) : {}),
111 isEditable: true,
112 stroke: color,
113 strokeThickness: 2,
114 fill: `${color}33`,
115 placementPointCount: 5,
116 } as any,
117 });
118 return;
119 }
120 case ETradingAnnotationType.ExtendedLineAnnotation: {
121 const extendStart = options.extendStart ?? true;
122 const extendEnd = options.extendEnd ?? true;
123
124 placementModifier.startPlacement({
125 type: ETradingAnnotationType.ExtendedLineAnnotation,
126 options: preparePlacementOptions({
127 ...createTradingAnnotationOptions("RAY", 2),
128 isEditable: true,
129 stroke:
130 extendStart && extendEnd
131 ? TRADING_ANNOTATION_COLORS.extendedLine
132 : TRADING_ANNOTATION_COLORS.ray,
133 strokeThickness: 2,
134 extendStart,
135 extendEnd,
136 }),
137 });
138 return;
139 }
140 case ETradingAnnotationType.ChannelAnnotation:
141 placementModifier.startPlacement({
142 type: ETradingAnnotationType.ChannelAnnotation,
143 options: preparePlacementOptions({
144 ...createTradingAnnotationOptions("CHN", 4, undefined, { includeSegmentLabels: false }),
145 isEditable: true,
146 stroke: TRADING_ANNOTATION_COLORS.channel,
147 fill: `${TRADING_ANNOTATION_COLORS.channel}33`,
148 strokeThickness: 2,
149 midLineStrokeDashArray: [4, 3],
150 showMidLine: true,
151 showMidPointGrips: true,
152
153 } as any),
154 });
155 console.log('placing channel');
156 return;
157 case ETradingAnnotationType.FlatBottomChannelAnnotation:
158 placementModifier.startPlacement({
159 type: ETradingAnnotationType.FlatBottomChannelAnnotation,
160 options: preparePlacementOptions({
161 ...createTradingAnnotationOptions("FLT", 4, undefined, { segmentPairs: CHANNEL_LABEL_PAIRS }),
162 isEditable: true,
163 stroke: TRADING_ANNOTATION_COLORS.flatChannel,
164 fill: `${TRADING_ANNOTATION_COLORS.flatChannel}33`,
165 strokeThickness: 2,
166 midLineStrokeDashArray: [4, 3],
167 showMidLine: true,
168 showMidPointGrips: false,
169 } as any),
170 });
171 return;
172 case ETradingAnnotationType.DisjointChannelAnnotation:
173 placementModifier.startPlacement({
174 type: ETradingAnnotationType.DisjointChannelAnnotation,
175 options: preparePlacementOptions({
176 ...createTradingAnnotationOptions("DSJ", 4, undefined, { segmentPairs: CHANNEL_LABEL_PAIRS }),
177 isEditable: true,
178 stroke: TRADING_ANNOTATION_COLORS.disjointChannel,
179 fill: `${TRADING_ANNOTATION_COLORS.disjointChannel}33`,
180 strokeThickness: 2,
181 midLineStrokeDashArray: [4, 3],
182 } as any),
183 });
184 return;
185 case ETradingAnnotationType.PitchforkAnnotation:
186 placementModifier.startPlacement({
187 type: ETradingAnnotationType.PitchforkAnnotation,
188 options: preparePlacementOptions({
189 ...createTradingAnnotationOptions("PFK", 3, undefined, { segmentPairs: PITCH_LABEL_PAIRS }),
190 isEditable: true,
191 stroke: TRADING_ANNOTATION_COLORS.pitchfork,
192 strokeThickness: 2,
193 showFullWidthZone: true,
194 fullWidthZoneFill: `${TRADING_ANNOTATION_COLORS.pitchZone}66`,
195 fullWidthZoneStroke: TRADING_ANNOTATION_COLORS.pitchZone,
196 showHalfWidthZone: true,
197 halfWidthZoneFill: "#33ff3366",
198 halfWidthZoneStroke: "#33ff33",
199 renderLayer: ERenderLayer.First,
200 } as any),
201 });
202 return;
203 case ETradingAnnotationType.PitchfanAnnotation:
204 placementModifier.startPlacement({
205 type: ETradingAnnotationType.PitchfanAnnotation,
206 options: preparePlacementOptions({
207 ...createTradingAnnotationOptions("FAN", 3, undefined, {
208 segmentPairs: [...PITCH_LABEL_PAIRS, [0, 1]],
209 }),
210 isEditable: true,
211 stroke: TRADING_ANNOTATION_COLORS.pitchfan,
212 strokeThickness: 2,
213 showShoulderLine: true,
214 showFullWidthZone: true,
215 fullWidthZoneFill: `${TRADING_ANNOTATION_COLORS.pitchZone}66`,
216 fullWidthZoneStroke: TRADING_ANNOTATION_COLORS.pitchZone,
217 showHalfWidthZone: true,
218 halfWidthZoneFill: `${TRADING_ANNOTATION_COLORS.halfPitchZone}66`,
219 halfWidthZoneStroke: TRADING_ANNOTATION_COLORS.halfPitchZone,
220 } as any),
221 });
222 return;
223 case ETradingAnnotationType.FibonacciRetracementAnnotation:
224 placementModifier.startPlacement({
225 type: ETradingAnnotationType.FibonacciRetracementAnnotation,
226 options: preparePlacementOptions({
227 ...createTradingAnnotationOptions("FIB", 3, undefined, {
228 includeSegmentLabels: false,
229 extraLabels: options.verticalOnly // note that "extraLabels" is not a library prop, these are just additional utils
230 ? [
231 // extra axis labels to show extended Fibonacci using "segmentRatio"
232 // for labels at thresholds "-0.618" and "2.618"
233 {
234 id: `FIB-pt-extended-1`,
235 anchorMode: EMultiPointLabelAnchorMode.Axis,
236 axisLabelDrawMode: EAxisLabelDrawMode.Y,
237 segmentStartIndex: 1,
238 segmentEndIndex: 2,
239 segmentRatio: 2.618,
240 },
241 {
242 id: `FIB-pt-extended-2`,
243 anchorMode: EMultiPointLabelAnchorMode.Axis,
244 axisLabelDrawMode: EAxisLabelDrawMode.Y,
245 segmentStartIndex: 1,
246 segmentEndIndex: 2,
247 segmentRatio: -0.618,
248 },
249 ]
250 : [
251 {
252 id: `FIB-pt-extended-1`,
253 anchorMode: EMultiPointLabelAnchorMode.Axis,
254 axisLabelDrawMode: EAxisLabelDrawMode.Y,
255 segmentStartIndex: 1,
256 segmentEndIndex: 2,
257 segmentRatio: 4.236,
258 },
259 ],
260 }),
261 isEditable: true,
262 strokeThickness: 2,
263 regionColors: options.verticalOnly
264 ? ["#F85161", "#FB8B62", "#D2E26F", "#70CEA5", "#7FAECE"]
265 : FIB_REGION_COLORS,
266 fillOpacity: 0.25,
267 opacity: 1,
268 showConnectorLine: true,
269 connectorLineStrokeDashArray: options.verticalOnly ? [16, 4] : [6, 4],
270 //thresholds: options.verticalOnly ? [-0.618, -0.236, 0, 0.618, 1, 2.618] : undefined, // use defaults
271 verticalOnly: options.verticalOnly,
272 // fibonacciLabelPlacement: EFibonacciLabelPlacement.Top,
273 // fibonacciLabelColorMode: EFibonacciLabelColorMode.MultiColor,
274 // formatFibonacciLabel: (params: TFibonacciLevelLabelFormatParams) => {
275 // const percentage = `${(params.threshold * 100).toFixed(1)}%`;
276 // return `${percentage}\n${params.valueLabel}`;
277 // },
278 } as any),
279 });
280 return;
281 case ETradingAnnotationType.FibonacciExtensionAnnotation:
282 placementModifier.startPlacement({
283 type: ETradingAnnotationType.FibonacciExtensionAnnotation,
284 options: preparePlacementOptions({
285 ...createTradingAnnotationOptions("FBE", 3, undefined, { includeSegmentLabels: false }),
286 isEditable: true,
287 strokeThickness: 2,
288 regionColors: FIB_REGION_COLORS,
289 fillOpacity: 0.25,
290 opacity: 1,
291 showConnectorLine: true,
292 connectorLineStrokeDashArray: [6, 4],
293 } as any),
294 });
295 return;
296 case ETradingAnnotationType.FibonacciCirclesAnnotation:
297 placementModifier.startPlacement({
298 type: ETradingAnnotationType.FibonacciCirclesAnnotation,
299 options: preparePlacementOptions({
300 ...createTradingAnnotationOptions("FBC", 2, undefined, { includeSegmentLabels: false }),
301 isEditable: true,
302 strokeThickness: 2,
303 regionColors: FIB_REGION_COLORS,
304 fillOpacity: 0.2,
305 opacity: 1,
306 showConnectorLine: true,
307 connectorLineStrokeDashArray: [6, 4],
308 } as any),
309 });
310 return;
311 case ETradingAnnotationType.FibonacciSpeedResistanceArcsAnnotation:
312 placementModifier.startPlacement({
313 type: ETradingAnnotationType.FibonacciSpeedResistanceArcsAnnotation,
314 options: preparePlacementOptions({
315 ...createTradingAnnotationOptions("FSR", 2, undefined, { includeSegmentLabels: false }),
316 isEditable: true,
317 strokeThickness: 2,
318 regionColors: FIB_REGION_COLORS,
319 fillOpacity: 0.2,
320 opacity: 1,
321 showConnectorLine: true,
322 connectorLineStrokeDashArray: [6, 4],
323 } as any),
324 });
325 return;
326 case ETradingAnnotationType.FibonacciWedgeAnnotation:
327 placementModifier.startPlacement({
328 type: ETradingAnnotationType.FibonacciWedgeAnnotation,
329 options: preparePlacementOptions({
330 ...createTradingAnnotationOptions("FBW", 3, undefined, { includeSegmentLabels: false }),
331 isEditable: true,
332 strokeThickness: 2,
333 regionColors: FIB_REGION_COLORS,
334 fillOpacity: 0.2,
335 opacity: 1,
336 showConnectorLine: true,
337 connectorLineStrokeDashArray: [6, 4],
338 } as any),
339 });
340 return;
341 case ETradingAnnotationType.MeasureAnnotation:
342 placementModifier.startPlacement({
343 type: ETradingAnnotationType.MeasureAnnotation,
344 options: preparePlacementOptions({
345 ...createTradingAnnotationOptions("MSR", 2, undefined, { includeSegmentLabels: false }),
346 ...defaultSnapToCandleOptions(candlestickSeries.id),
347 isEditable: true,
348 strokeThickness: 2,
349 growingColor: TRADING_ANNOTATION_COLORS.measureUp,
350 decliningColor: TRADING_ANNOTATION_COLORS.measureDown,
351 fillOpacity: 0.15,
352 labelCornerRadius: 6,
353 labelOffset: 6,
354 labelPadding: new Thickness(4, 8, 4, 8),
355 yValueScaleFactor: 100,
356 }),
357 });
358 return;
359 case ETradingAnnotationType.StopLossTakeProfitAnnotation:
360 placementModifier.startPlacement({
361 type: ETradingAnnotationType.StopLossTakeProfitAnnotation,
362 options: preparePlacementOptions({
363 ...createTradingAnnotationOptions("RISK", 2),
364 isEditable: true,
365 strokeThickness: 2,
366 strokeDashArray: [6, 3],
367 takeProfitColor: "#16A34A",
368 stopLossColor: TRADING_ANNOTATION_COLORS.measureDown,
369 fillOpacity: 0.18,
370 axisSpanFillOpacity: 0.2,
371 axisLabelVisibility: EAnnotationVisibilityMode.Always,
372 axisLabelStroke: "#FFFFFF",
373 annotationsGripsRadius: 4,
374 annotationsGripsStroke: TRADING_ANNOTATION_COLORS.foreground,
375 } as any),
376 });
377 return;
378 case ETradingAnnotationType.FreehandDrawingAnnotation:
379 startFreehand(options.lockedAspect);
380 return;
381 }
382 };
383
384 const addSeedAnnotations = () => {
385 sciChartSurface.annotations.clear(true);
386 sciChartSurface.annotations.add(
387 new ChannelAnnotation({
388 ...createTradingAnnotationOptions("", 4, undefined, { includeSegmentLabels: false }),
389 isEditable: true,
390 stroke: TRADING_ANNOTATION_COLORS.channel,
391 fill: `${TRADING_ANNOTATION_COLORS.channel}26`,
392 strokeThickness: 2,
393 midLineStrokeDashArray: [4, 3],
394 showMidLine: true,
395 showMidPointGrips: true,
396 points: [
397 { x: 1705049596, y: 62895 },
398 { x: 1705259865, y: 64864 },
399 { x: 1705049596, y: 61731 },
400 { x: 1705259865, y: 63699 },
401 ],
402 } as any),
403 new FibonacciRetracementAnnotation({
404 ...createTradingAnnotationOptions("Fib", 3, undefined, {
405 includeSegmentLabels: false,
406 extraLabels: [
407 {
408 id: "Fib-extended-1",
409 anchorMode: EMultiPointLabelAnchorMode.Axis,
410 axisLabelDrawMode: EAxisLabelDrawMode.Y,
411 segmentStartIndex: 1,
412 segmentEndIndex: 2,
413 segmentRatio: 2.618,
414 },
415 {
416 id: "Fib-extended-2",
417 anchorMode: EMultiPointLabelAnchorMode.Axis,
418 axisLabelDrawMode: EAxisLabelDrawMode.Y,
419 segmentStartIndex: 1,
420 segmentEndIndex: 2,
421 segmentRatio: -0.618,
422 },
423 ],
424 }),
425 isEditable: true,
426 strokeThickness: 2,
427 regionColors: ["#F85161", "#FB8B62", "#D2E26F", "#70CEA5", "#7FAECE"],
428 fillOpacity: 0.25,
429 opacity: 1,
430 fibonacciLabelPlacement: EFibonacciLabelPlacement.Top,
431 fibonacciLabelColorMode: EFibonacciLabelColorMode.MultiColor,
432 showConnectorLine: true,
433 connectorLineStrokeDashArray: [16, 4],
434 // thresholds: [-0.618, -0.236, 0, 0.618, 1, 2.618],
435 verticalOnly: true,
436 formatFibonacciLabel: (params: TFibonacciLevelLabelFormatParams) => {
437 const percentage = `${(params.threshold * 100).toFixed(1)}%`;
438 return `${percentage}\n${params.valueLabel}`;
439 },
440 points: [
441 {x: 1705587439, y: 63110},
442 {x: 1705822618, y: 65283}
443 ],
444 }),
445 new ExtendedLineAnnotation({
446 ...createTradingAnnotationOptions("", 2, undefined),
447 isEditable: true,
448 strokeThickness: 2,
449 stroke: TRADING_ANNOTATION_COLORS.ray,
450 points: [
451 {x: 1705291231, y: 62131},
452 {x: 1705459018, y: 63495}
453 ],
454 })
455 );
456 };
457
458 const removeSelectedAnnotations = () => {
459 sciChartSurface.annotations
460 .asArray()
461 .filter((annotation: IAnnotation) => annotation.isSelected)
462 .forEach((annotation: IAnnotation) => sciChartSurface.annotations.remove(annotation, true));
463 };
464
465 const duplicateSelectedAnnotation = () => {
466 const selectedAnnotation = sciChartSurface.annotations
467 .asArray()
468 .find((annotation: IAnnotation) => annotation.isSelected);
469 if (!selectedAnnotation) return;
470
471 const json = JSON.parse(JSON.stringify(selectedAnnotation.toJSON()));
472 json.options.isSelected = true;
473 delete json.options.id;
474 if (Array.isArray(json.options.points)) {
475 json.options.points = json.options.points.map((point: { x: number; y: number }) => ({
476 x: point.x + 8 * 60 * 60,
477 y: point.y + 350,
478 }));
479 }
480 const [duplicate] = buildAnnotations(json);
481 if (duplicate) {
482 selectedAnnotation.isSelected = false;
483 sciChartSurface.annotations.add(duplicate);
484 }
485 };
486 addSeedAnnotations();
487
488 const disposeKeyboard = addKeyboardShortcuts(removeSelectedAnnotations, duplicateSelectedAnnotation);
489
490 const deleteAllAnnotations = () => {
491 sciChartSurface.annotations.clear(true);
492 };
493
494 return {
495 sciChartSurface,
496 startTool,
497 stopActiveTools,
498 resetAnnotations: addSeedAnnotations,
499 deleteAllAnnotations,
500 removeSelectedAnnotations,
501 duplicateSelectedAnnotation,
502 setKeepPlacingAfterComplete: (enabled: boolean) => {
503 (placementModifier as any).keepPlacingAfterCompleteProperty = enabled;
504 (freehandDrawingModifier as any).keepDrawingAfterCompleteProperty = enabled;
505 },
506 dispose: disposeKeyboard,
507 };
508};
509
510const addKeyboardShortcuts = (removeSelected: () => void, duplicateSelected: () => void) => {
511 const isTypingTarget = (target: EventTarget | null) => {
512 if (!(target instanceof HTMLElement)) return false;
513 return target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName);
514 };
515
516 const onKeyDown = (event: KeyboardEvent) => {
517 if (isTypingTarget(event.target)) return;
518 if (event.key === "Backspace" || event.key === "Delete") {
519 removeSelected();
520 event.preventDefault();
521 }
522 if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "d") {
523 duplicateSelected();
524 event.preventDefault();
525 }
526 };
527
528 document.addEventListener("keydown", onKeyDown);
529 return () => document.removeEventListener("keydown", onKeyDown);
530};
531
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.

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

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.
