@pradeepjadhav/trading-reload-chart 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +87 -0
  2. package/README.md +256 -0
  3. package/dist/canvas/layers/AxisLayerX/AxisLayerX.d.ts +27 -0
  4. package/dist/canvas/layers/AxisLayerX/AxisLayerX.helpers.d.ts +32 -0
  5. package/dist/canvas/layers/AxisLayerX/AxisLayerX.types.d.ts +10 -0
  6. package/dist/canvas/layers/AxisLayerY/AxisLayerY.d.ts +23 -0
  7. package/dist/canvas/layers/AxisLayerY/AxisLayerY.helpers.d.ts +4 -0
  8. package/dist/canvas/layers/AxisLayerY/AxisLayerY.types.d.ts +8 -0
  9. package/dist/canvas/layers/CrosshairLayer.d.ts +23 -0
  10. package/dist/canvas/layers/CrosshairLayer.types.d.ts +10 -0
  11. package/dist/canvas/layers/ExistingCandlesLayer.d.ts +107 -0
  12. package/dist/canvas/layers/ExistingCandlesLayer.types.d.ts +56 -0
  13. package/dist/canvas/layers/PdhPdlLayer.d.ts +24 -0
  14. package/dist/canvas/layers/ShapesLayer/FibRetracementShape.d.ts +28 -0
  15. package/dist/canvas/layers/ShapesLayer/LineShape.d.ts +27 -0
  16. package/dist/canvas/layers/ShapesLayer/PathShape.d.ts +29 -0
  17. package/dist/canvas/layers/ShapesLayer/RectangleShape.d.ts +28 -0
  18. package/dist/canvas/layers/ShapesLayer/ShapesLayer.d.ts +70 -0
  19. package/dist/canvas/layers/ShapesLayer/ShapesLayer.helpers.d.ts +47 -0
  20. package/dist/canvas/layers/ShapesLayer/ShapesLayer.types.d.ts +470 -0
  21. package/dist/canvas/layers/ShapesLayer/ShortLongPosition.d.ts +40 -0
  22. package/dist/canvas/layers/ShapesLayer/ShortLongPosition.types.d.ts +11 -0
  23. package/dist/canvas/layers/TradeLayer/TradeLayer.d.ts +152 -0
  24. package/dist/canvas/layers/TradeLayer/TradeLayer.helpers.d.ts +3 -0
  25. package/dist/canvas/layers/TradeLayer/TradeLayer.types.d.ts +261 -0
  26. package/dist/canvas/layers/TradeLayer/TradeLayerEvents.d.ts +68 -0
  27. package/dist/canvas/layers/TradeLayer/TradeLayerEvents.types.d.ts +12 -0
  28. package/dist/canvas/layers/VolumeLayer/VolumeLayer.d.ts +28 -0
  29. package/dist/canvas/layers/VolumeLayer/VolumeLayer.types.d.ts +25 -0
  30. package/dist/canvas/layers/helpers/LayerHelpers.d.ts +29 -0
  31. package/dist/chart/ChartController.d.ts +8 -0
  32. package/dist/chart/ChartController.types.d.ts +224 -0
  33. package/dist/chart/createChartDom.d.ts +2 -0
  34. package/dist/chart/createChartDom.types.d.ts +13 -0
  35. package/dist/chart/utils/deepPartial.types.d.ts +14 -0
  36. package/dist/chart/utils/getCanvasPoint.d.ts +2 -0
  37. package/dist/chart/utils/getCanvasPoint.types.d.ts +10 -0
  38. package/dist/chart/utils/mergeChartConfig.d.ts +3 -0
  39. package/dist/chart/utils/normalizeCandleTime.d.ts +2 -0
  40. package/dist/chart/utils/updateTradeTPSL.d.ts +8 -0
  41. package/dist/config/ChartShapeConfig/chartShapeConfig.d.ts +2 -0
  42. package/dist/config/chartConfig.d.ts +20 -0
  43. package/dist/config/chartConfig.types.d.ts +642 -0
  44. package/dist/config/colors.d.ts +19 -0
  45. package/dist/config/pairs.d.ts +3 -0
  46. package/dist/core/CoordinateSystem.d.ts +10 -0
  47. package/dist/favicon.svg +1 -0
  48. package/dist/helpers/math.d.ts +1 -0
  49. package/dist/icons.svg +24 -0
  50. package/dist/index.d.ts +14 -0
  51. package/dist/models/Candle.types.d.ts +67 -0
  52. package/dist/models/ChartViewport.types.d.ts +30 -0
  53. package/dist/models/Point.types.d.ts +12 -0
  54. package/dist/models/Trade.types.d.ts +151 -0
  55. package/dist/models/VisibleRange.types.d.ts +15 -0
  56. package/dist/react/TradingReload.d.ts +28 -0
  57. package/dist/react/TradingReload.types.d.ts +74 -0
  58. package/dist/trading-reload-chart.css +2 -0
  59. package/dist/trading-reload-chart.js +3503 -0
  60. package/package.json +74 -0
@@ -0,0 +1,3503 @@
1
+ import { forwardRef as Mt, useEffect as tt, useImperativeHandle as Rt, useRef as z } from "react";
2
+ import { jsx as Wt } from "react/jsx-runtime";
3
+ var h = {
4
+ primary: "#7959F7",
5
+ blue: "#2962FF",
6
+ yellow: "#FBC02D",
7
+ purple: "#9C27B0",
8
+ green: "#26A69A",
9
+ red: "#EF5350",
10
+ white: "#ffffff",
11
+ background: "#0F0F0F",
12
+ handleBackground: "#111827",
13
+ slBackground: "#2a1111",
14
+ tpBackground: "#0d2418",
15
+ axisBorder: "rgb(89, 89, 89)",
16
+ axisText: "rgba(255, 255, 255, 0.60)",
17
+ axisTick: "rgba(255,255,255,0.12)",
18
+ axisYTick: "rgba(255, 255, 255, 0.40)",
19
+ divider: "rgba(255,255,255,0.12)",
20
+ crosshairLabelBackground: "#494949"
21
+ }, f = {
22
+ ...h,
23
+ gray: "#808080",
24
+ orange: "#ff9500",
25
+ purple: "#af52de",
26
+ handleBorder: h.blue,
27
+ handleFill: h.background
28
+ }, et = {
29
+ defaultWidthCandles: 10,
30
+ defaultStopLossPercent: 0.1,
31
+ defaultRiskRewardRatio: 1.5,
32
+ profitFillColor: f.green,
33
+ profitFillOpacity: 0.18,
34
+ lossFillColor: f.red,
35
+ lossFillOpacity: 0.18,
36
+ borderColor: f.gray,
37
+ borderOpacity: 0,
38
+ borderWidth: 1,
39
+ borderStyle: "solid",
40
+ midLineColor: f.gray,
41
+ midLineOpacity: 0.9,
42
+ midLineWidth: 0.5,
43
+ midLineStyle: "solid",
44
+ handleColor: f.handleFill,
45
+ handleBorderColor: f.handleBorder,
46
+ handleBorderThickness: 1.5,
47
+ handleRadius: 5,
48
+ hoverLineWidth: 2,
49
+ selectedLineWidth: 2
50
+ }, Dt = {
51
+ trendline: {
52
+ lineWidth: 1,
53
+ lineColor: f.blue,
54
+ lineOpacity: 1,
55
+ lineStyle: "solid",
56
+ fillColor: f.blue,
57
+ fillOpacity: 0,
58
+ handleColor: f.handleFill,
59
+ handleBorderColor: f.handleBorder,
60
+ handleRadius: 5,
61
+ hoverLineWidth: 1,
62
+ selectedLineWidth: 1.1
63
+ },
64
+ rectangle: {
65
+ lineWidth: 1,
66
+ lineColor: f.purple,
67
+ lineOpacity: 0.6,
68
+ lineStyle: "solid",
69
+ fillColor: f.purple,
70
+ fillOpacity: 0.06,
71
+ handleColor: f.handleFill,
72
+ handleBorderColor: f.handleBorder,
73
+ handleRadius: 5,
74
+ hoverLineWidth: 1,
75
+ selectedLineWidth: 1.1,
76
+ textColor: h.white,
77
+ textOpacity: 1,
78
+ textFontFamily: "Arial",
79
+ textFontSize: 20
80
+ },
81
+ path: {
82
+ lineWidth: 1,
83
+ lineColor: f.yellow,
84
+ lineOpacity: 0.8,
85
+ lineStyle: "solid",
86
+ fillColor: f.yellow,
87
+ fillOpacity: 0,
88
+ handleColor: f.handleFill,
89
+ handleBorderColor: f.handleBorder,
90
+ handleRadius: 5,
91
+ hoverLineWidth: 1,
92
+ selectedLineWidth: 1.1
93
+ },
94
+ fibRetracement: {
95
+ levels: [
96
+ {
97
+ level: 1,
98
+ label: "1",
99
+ color: f.gray,
100
+ opacity: 0.5,
101
+ lineWidth: 1,
102
+ lineStyle: "dashed"
103
+ },
104
+ {
105
+ level: 0.72,
106
+ label: "0.720",
107
+ color: f.red,
108
+ opacity: 0.5,
109
+ lineWidth: 1,
110
+ lineStyle: "dashed"
111
+ },
112
+ {
113
+ level: 0.618,
114
+ label: "0.618",
115
+ color: f.orange,
116
+ opacity: 0.5,
117
+ lineWidth: 1,
118
+ lineStyle: "dashed"
119
+ },
120
+ {
121
+ level: 0.5,
122
+ label: "0.5",
123
+ color: f.purple,
124
+ opacity: 0.5,
125
+ lineWidth: 1,
126
+ lineStyle: "dashed"
127
+ },
128
+ {
129
+ level: 0,
130
+ label: "0",
131
+ color: f.gray,
132
+ opacity: 0.5,
133
+ lineWidth: 1,
134
+ lineStyle: "dashed"
135
+ }
136
+ ],
137
+ fillColor: f.gray,
138
+ fillOpacity: 0,
139
+ handleColor: f.handleFill,
140
+ handleBorderColor: f.handleBorder,
141
+ handleRadius: 5,
142
+ hoverLineWidth: 1,
143
+ selectedLineWidth: 1
144
+ },
145
+ longPosition: { ...et },
146
+ shortPosition: { ...et },
147
+ previousDayHighLow: {
148
+ line: {
149
+ color: h.red,
150
+ width: 1,
151
+ opacity: 0.7,
152
+ dash: [4, 16]
153
+ },
154
+ text: {
155
+ color: h.white,
156
+ font: "11px Arial",
157
+ paddingX: 6
158
+ }
159
+ }
160
+ }, A = { default: "12px Arial" }, it = {
161
+ width: 1,
162
+ opacity: 0.9,
163
+ style: "dashed",
164
+ dash: [4, 4]
165
+ }, N = {
166
+ width: 1,
167
+ opacity: 0.9,
168
+ style: "dotted",
169
+ dash: [2, 4]
170
+ }, G = {
171
+ height: 22,
172
+ widthVolume: 60,
173
+ widthPNL: 80,
174
+ widthClose: 24,
175
+ position: {
176
+ placement: "left",
177
+ margin: 100
178
+ },
179
+ borderWidth: 1,
180
+ borderColor: h.blue,
181
+ borderOpacity: 0.9,
182
+ backgroundColor: h.handleBackground,
183
+ backgroundOpacity: 0.9,
184
+ sectionDividerColor: h.divider,
185
+ paddingX: 10,
186
+ closeButtonColor: h.blue,
187
+ showVolumeSection: !0,
188
+ showPnlSection: !0,
189
+ showCloseSection: !0
190
+ }, rt = {
191
+ width: 32,
192
+ height: 22,
193
+ gapFromStartPriceHandle: 6,
194
+ gapBetweenHandles: 4,
195
+ placement: "right",
196
+ borderWidth: 1,
197
+ borderColor: h.white,
198
+ borderOpacity: 0.5,
199
+ backgroundColor: h.handleBackground,
200
+ backgroundOpacity: 0.3,
201
+ textColor: h.white,
202
+ textOpacity: 0.6,
203
+ font: A.default
204
+ }, st = {
205
+ height: 22,
206
+ widthPNL: 80,
207
+ position: {
208
+ placement: "left",
209
+ margin: 100
210
+ },
211
+ borderWidth: 1,
212
+ borderColor: h.white,
213
+ borderOpacity: 0.4,
214
+ backgroundColor: h.handleBackground,
215
+ backgroundOpacity: 0.9,
216
+ paddingX: 10,
217
+ textColor: h.white,
218
+ textOpacity: 0.85,
219
+ font: A.default
220
+ }, c = {
221
+ zoom: {
222
+ x: {
223
+ speed: 0.05,
224
+ min: 0.03,
225
+ max: 20
226
+ },
227
+ y: {
228
+ speed: 0.1,
229
+ min: 10,
230
+ max: 500
231
+ }
232
+ },
233
+ candles: {
234
+ defaultWidth: 8,
235
+ defaultGap: 2,
236
+ minBodyHeight: 1,
237
+ autoFollowLatestCandle: !0,
238
+ autoFollowThresholdCandles: 10,
239
+ rightOffsetCandles: 15,
240
+ liveAskPriceLine: {
241
+ visible: !0,
242
+ width: 1,
243
+ opacity: 0.7,
244
+ dash: [4, 6],
245
+ bullishColor: h.purple,
246
+ bearishColor: h.purple
247
+ },
248
+ liveBidPriceLine: {
249
+ visible: !0,
250
+ width: 1,
251
+ opacity: 0.7,
252
+ dash: [4, 4],
253
+ bullishColor: h.green,
254
+ bearishColor: h.red
255
+ }
256
+ },
257
+ volume: {
258
+ visible: !0,
259
+ bullishColor: h.green,
260
+ bearishColor: h.red,
261
+ opacity: 0.35,
262
+ height: 200,
263
+ bottomOffset: 0,
264
+ minBarHeight: 1
265
+ },
266
+ crosshair: {
267
+ color: "rgba(255,255,255,0.35)",
268
+ thickness: 1,
269
+ style: "dashed"
270
+ },
271
+ axis: {
272
+ axisX: {
273
+ height: 30,
274
+ backgroundColor: h.background,
275
+ borderColor: h.axisBorder,
276
+ borderWidth: 1,
277
+ textColor: h.axisText,
278
+ font: A.default,
279
+ textAlign: "center",
280
+ tickColor: h.axisTick,
281
+ tickWidth: 1,
282
+ tickLength: 4,
283
+ labelOffsetY: 16,
284
+ minLabelGap: 16,
285
+ crosshairLabel: {
286
+ backgroundColor: h.crosshairLabelBackground,
287
+ borderColor: h.crosshairLabelBackground,
288
+ borderWidth: 1,
289
+ textColor: h.white,
290
+ font: A.default,
291
+ paddingX: 8,
292
+ height: 26
293
+ }
294
+ },
295
+ axisY: {
296
+ width: 80,
297
+ backgroundColor: h.background,
298
+ borderColor: h.axisBorder,
299
+ borderWidth: 1,
300
+ textColor: h.axisText,
301
+ font: A.default,
302
+ textAlign: "left",
303
+ tickCount: 12,
304
+ tickColor: h.axisYTick,
305
+ tickWidth: 1,
306
+ tickLength: 4,
307
+ tickGap: 64,
308
+ labelOffsetX: 8,
309
+ crosshairLabel: {
310
+ backgroundColor: h.crosshairLabelBackground,
311
+ borderColor: h.crosshairLabelBackground,
312
+ borderWidth: 1,
313
+ textColor: h.white,
314
+ font: A.default,
315
+ paddingX: 8,
316
+ height: 20
317
+ }
318
+ }
319
+ },
320
+ tradeHandles: {
321
+ font: A.default,
322
+ textColor: h.white,
323
+ textOpacity: 0.8,
324
+ pnlTextColors: {
325
+ positive: h.green,
326
+ negative: h.red
327
+ },
328
+ slHandle: {
329
+ handle: {
330
+ ...G,
331
+ borderColor: h.red,
332
+ borderOpacity: 0.4,
333
+ backgroundColor: h.slBackground,
334
+ closeButtonColor: h.red,
335
+ showVolumeSection: !1
336
+ },
337
+ handleLine: {
338
+ ...it,
339
+ color: h.red
340
+ }
341
+ },
342
+ tpHandle: {
343
+ handle: {
344
+ ...G,
345
+ borderColor: h.green,
346
+ borderOpacity: 0.4,
347
+ backgroundColor: h.tpBackground,
348
+ closeButtonColor: h.green,
349
+ showVolumeSection: !1
350
+ },
351
+ handleLine: {
352
+ ...it,
353
+ color: h.green
354
+ }
355
+ },
356
+ startPriceHandle: {
357
+ handle: { ...G },
358
+ handleLine: {
359
+ width: 1,
360
+ color: h.blue,
361
+ opacity: 1,
362
+ style: "solid"
363
+ }
364
+ },
365
+ missingProtectionHandles: {
366
+ visible: !0,
367
+ dragActivationThresholdPx: 2,
368
+ slHandle: {
369
+ ...rt,
370
+ borderColor: h.red,
371
+ backgroundColor: h.slBackground,
372
+ textColor: h.red
373
+ },
374
+ tpHandle: {
375
+ ...rt,
376
+ borderColor: h.green,
377
+ backgroundColor: h.tpBackground,
378
+ textColor: h.green
379
+ },
380
+ temporarySlHandle: {
381
+ ...st,
382
+ borderColor: h.red,
383
+ backgroundColor: h.slBackground,
384
+ textColor: h.red
385
+ },
386
+ temporaryTpHandle: {
387
+ ...st,
388
+ borderColor: h.green,
389
+ backgroundColor: h.tpBackground,
390
+ textColor: h.green
391
+ },
392
+ temporarySlLine: {
393
+ ...N,
394
+ color: h.red
395
+ },
396
+ temporaryTpLine: {
397
+ ...N,
398
+ color: h.green
399
+ }
400
+ }
401
+ },
402
+ pastTradeIndicators: {
403
+ visible: !0,
404
+ arrow: {
405
+ visible: !0,
406
+ width: 60,
407
+ height: 30,
408
+ headHeightRatio: 0.45,
409
+ shaftWidthRatio: 6,
410
+ buyColor: h.green,
411
+ buyBorderColor: h.white,
412
+ sellColor: h.red,
413
+ sellBorderColor: h.white,
414
+ borderWidth: 1,
415
+ opacity: 1
416
+ },
417
+ connectorLine: {
418
+ ...N,
419
+ color: h.white,
420
+ opacity: 0.65
421
+ }
422
+ },
423
+ colors: {
424
+ bullish: h.green,
425
+ bearish: h.red,
426
+ background: h.background
427
+ },
428
+ shapes: Dt
429
+ }, nt = [
430
+ 300 * 1e3,
431
+ 900 * 1e3,
432
+ 1800 * 1e3,
433
+ 3600 * 1e3,
434
+ 5400 * 1e3,
435
+ 10800 * 1e3,
436
+ 360 * 60 * 1e3,
437
+ 720 * 60 * 1e3,
438
+ 1440 * 60 * 1e3
439
+ ], Bt = (t) => {
440
+ if (t.length < 2) return 900 * 1e3;
441
+ const e = [];
442
+ for (let i = 1; i < Math.min(t.length, 10); i++) e.push(t[i].time - t[i - 1].time);
443
+ return e.sort((i, r) => i - r), e[Math.floor(e.length / 2)];
444
+ }, Xt = ({ candleSpacing: t, candleTimeframeMs: e, labelWidth: i, minLabelGap: r }) => {
445
+ if (t <= 0 || e <= 0) return nt[0];
446
+ const s = i + r;
447
+ for (const o of nt) if (o / e * t >= s) return o;
448
+ const n = 1440 * 60 * 1e3, a = n / e * t;
449
+ return n * Math.ceil(s / a);
450
+ }, At = (t, e) => {
451
+ const i = new Date(t), r = (i.getHours() * 60 + i.getMinutes()) * 60 * 1e3 + i.getSeconds() * 1e3 + i.getMilliseconds();
452
+ return e >= 1440 * 60 * 1e3 ? r === 0 : r % e === 0;
453
+ }, It = (t) => {
454
+ const e = new Date(t), i = e.getHours(), r = e.getMinutes();
455
+ return i === 0 && r === 0 ? String(e.getDate()) : `${String(i).padStart(2, "0")}:${String(r).padStart(2, "0")}`;
456
+ }, Ft = (t) => {
457
+ const e = new Date(t), i = e.toLocaleDateString("en-US", { weekday: "short" }), r = e.toLocaleDateString("en-US", { month: "short" });
458
+ return `${i} ${e.getDate()} ${r} ${e.getHours().toString().padStart(2, "0")}.${e.getMinutes().toString().padStart(2, "0")}`;
459
+ }, Yt = class {
460
+ #t;
461
+ #e;
462
+ candles;
463
+ viewport = null;
464
+ crosshair = {
465
+ visible: !1,
466
+ x: 0,
467
+ candle: null
468
+ };
469
+ constructor(t) {
470
+ this.#t = t.canvas, this.candles = t.candles;
471
+ const e = this.#t.getContext("2d");
472
+ if (!e) throw new Error("Canvas 2D context not supported");
473
+ this.#e = e;
474
+ }
475
+ setCandles(t) {
476
+ this.candles = t;
477
+ }
478
+ setViewport(t) {
479
+ this.viewport = t;
480
+ }
481
+ setCrosshair(t) {
482
+ this.crosshair = t;
483
+ }
484
+ hideCrosshair() {
485
+ this.crosshair.visible = !1;
486
+ }
487
+ render() {
488
+ this.viewport && (this.clearCanvas(), this.drawBackground(), this.drawTopBorder(), this.candles.length > 0 && this.drawAxisLabels(), this.drawCrosshairTimeLabel());
489
+ }
490
+ drawCrosshairTimeLabel() {
491
+ if (!this.crosshair.visible || !this.crosshair.candle) return;
492
+ const t = this.#e, e = this.#t.width, i = this.#t.height, r = c.axis.axisX.crosshairLabel, s = Ft(this.crosshair.candle.time);
493
+ t.save(), t.font = r.font;
494
+ const n = t.measureText(s).width + r.paddingX * 2, a = r.height, o = this.getCrosshairLabelX(n, e), l = Math.max(0, (i - a) / 2);
495
+ t.fillStyle = r.backgroundColor, t.fillRect(o, l, n, a), t.strokeStyle = r.borderColor, t.lineWidth = r.borderWidth, t.strokeRect(o, l, n, a), t.fillStyle = r.textColor, t.textAlign = "center", t.textBaseline = "middle", t.fillText(s, o + n / 2, l + a / 2), t.restore();
496
+ }
497
+ clearCanvas() {
498
+ this.#e.clearRect(0, 0, this.#t.width, this.#t.height);
499
+ }
500
+ drawBackground() {
501
+ const t = this.#e, e = c.axis.axisX;
502
+ t.save(), t.fillStyle = e.backgroundColor, t.fillRect(0, 0, this.#t.width, this.#t.height), t.restore();
503
+ }
504
+ drawTopBorder() {
505
+ const t = this.#e, e = c.axis.axisX;
506
+ t.save(), t.strokeStyle = e.borderColor, t.lineWidth = e.borderWidth, t.beginPath(), t.moveTo(0, 0), t.lineTo(this.#t.width, 0), t.stroke(), t.restore();
507
+ }
508
+ drawAxisLabels() {
509
+ if (!this.viewport) return;
510
+ const t = this.#e, e = c.axis.axisX, i = this.#t.width, r = Bt(this.candles);
511
+ t.save(), t.font = e.font, t.fillStyle = e.textColor, t.textAlign = e.textAlign, t.textBaseline = "middle";
512
+ const s = t.measureText("00:00").width, n = Xt({
513
+ candleSpacing: this.viewport.candleSpacing,
514
+ candleTimeframeMs: r,
515
+ labelWidth: s,
516
+ minLabelGap: e.minLabelGap
517
+ }), { startIndex: a, endIndex: o } = this.getVisibleCandleIndexRange(i);
518
+ for (let l = a; l <= o; l += 1) {
519
+ const d = this.getSlotTime(l, r);
520
+ if (d === null || !At(d, n)) continue;
521
+ const u = this.getCandleCenterX(l), g = It(d), m = t.measureText(g).width;
522
+ this.isLabelOutsideCanvas(u, m, i) || (this.drawTick(u), this.drawLabel(g, u));
523
+ }
524
+ t.restore();
525
+ }
526
+ getVisibleCandleIndexRange(t) {
527
+ if (!this.viewport) return {
528
+ startIndex: 0,
529
+ endIndex: -1
530
+ };
531
+ const { candleSpacing: e, offsetX: i } = this.viewport;
532
+ return {
533
+ startIndex: Math.max(0, Math.floor(-i / e)),
534
+ endIndex: Math.ceil((t - i) / e)
535
+ };
536
+ }
537
+ getSlotTime(t, e) {
538
+ if (t < this.candles.length) {
539
+ const s = this.candles[t];
540
+ return s ? s.time : null;
541
+ }
542
+ const i = this.candles[this.candles.length - 1];
543
+ if (!i) return null;
544
+ const r = t - (this.candles.length - 1);
545
+ return i.time + r * e;
546
+ }
547
+ getCandleCenterX(t) {
548
+ if (!this.viewport) return 0;
549
+ const { candleSpacing: e, candleWidth: i, offsetX: r } = this.viewport;
550
+ return t * e + r + i / 2;
551
+ }
552
+ isLabelOutsideCanvas(t, e, i) {
553
+ return t + e / 2 < 0 || t - e / 2 > i;
554
+ }
555
+ drawTick(t) {
556
+ const e = this.#e, i = c.axis.axisX;
557
+ e.strokeStyle = i.tickColor, e.lineWidth = i.tickWidth, e.beginPath(), e.moveTo(t, 0), e.lineTo(t, i.tickLength), e.stroke();
558
+ }
559
+ drawLabel(t, e) {
560
+ const i = this.#e, r = c.axis.axisX;
561
+ i.fillStyle = r.textColor, i.fillText(t, e, r.labelOffsetY);
562
+ }
563
+ getCrosshairLabelX(t, e) {
564
+ return Math.max(120, Math.min(e - t, this.crosshair.x - t / 2));
565
+ }
566
+ }, p = (t, e = 5) => Number(t.toFixed(e)), Y = ({ price: t, minPrice: e, priceRange: i, chartHeight: r }) => r - (t - e) / i * r, ft = ({ y: t, minPrice: e, priceRange: i, chartHeight: r }) => e + (1 - t / r) * i, Ot = ({ priceRange: t, canvasHeight: e }) => {
567
+ const i = t / (e / c.axis.axisY.tickGap), r = 10 ** Math.floor(Math.log10(i));
568
+ for (const s of [
569
+ 1,
570
+ 2,
571
+ 5,
572
+ 10
573
+ ]) {
574
+ const n = s * r;
575
+ if (n >= i) return n;
576
+ }
577
+ return 10 * r;
578
+ }, Vt = class {
579
+ #t;
580
+ #e;
581
+ viewport = null;
582
+ crosshair = {
583
+ visible: !1,
584
+ y: 0,
585
+ price: 0
586
+ };
587
+ constructor(t) {
588
+ this.#t = t.canvas;
589
+ const e = this.#t.getContext("2d");
590
+ if (!e) throw new Error("Canvas 2D context not supported");
591
+ this.#e = e;
592
+ }
593
+ setViewport(t) {
594
+ this.viewport = t;
595
+ }
596
+ setCrosshair(t) {
597
+ this.crosshair = t;
598
+ }
599
+ hideCrosshair() {
600
+ this.crosshair.visible = !1;
601
+ }
602
+ render() {
603
+ this.viewport && (this.clearCanvas(), this.drawBackground(), this.drawLeftBorder(), this.drawPriceLabels(), this.drawCrosshairPriceLabel());
604
+ }
605
+ drawCrosshairPriceLabel() {
606
+ if (!this.crosshair.visible) return;
607
+ const t = this.#e, e = this.#t.width, i = this.#t.height, r = c.axis.axisY.crosshairLabel, s = p(this.crosshair.price).toFixed(5), n = r.height, a = this.getCrosshairLabelY(n, i);
608
+ t.save(), t.fillStyle = r.backgroundColor, t.fillRect(0, a, e, n), t.strokeStyle = r.borderColor, t.lineWidth = r.borderWidth, t.strokeRect(0, a, e, n), t.font = r.font, t.fillStyle = r.textColor, t.textAlign = "left", t.textBaseline = "middle", t.fillText(s, r.paddingX, a + n / 2), t.restore();
609
+ }
610
+ clearCanvas() {
611
+ this.#e.clearRect(0, 0, this.#t.width, this.#t.height);
612
+ }
613
+ drawBackground() {
614
+ const t = this.#e, e = c.axis.axisY;
615
+ t.save(), t.fillStyle = e.backgroundColor, t.fillRect(0, 0, this.#t.width, this.#t.height), t.restore();
616
+ }
617
+ drawLeftBorder() {
618
+ const t = this.#e, e = c.axis.axisY;
619
+ t.save(), t.strokeStyle = e.borderColor, t.lineWidth = e.borderWidth, t.beginPath(), t.moveTo(0, 0), t.lineTo(0, this.#t.height), t.stroke(), t.restore();
620
+ }
621
+ drawPriceLabels() {
622
+ if (!this.viewport) return;
623
+ const t = this.#e, e = this.#t.height, i = c.axis.axisY, r = Ot({
624
+ priceRange: this.viewport.priceRange,
625
+ canvasHeight: e
626
+ });
627
+ t.save(), t.font = i.font, t.fillStyle = i.textColor, t.textAlign = i.textAlign, t.textBaseline = "middle";
628
+ const { startPrice: s, endPrice: n } = this.getVisiblePriceRange(r);
629
+ for (let a = s; a <= n; a += r) {
630
+ const o = this.getPriceY(a, e);
631
+ this.isPriceOutsideCanvas(o, e) || (this.drawTick(o), this.drawPriceLabel(a, o));
632
+ }
633
+ t.restore();
634
+ }
635
+ getVisiblePriceRange(t) {
636
+ return this.viewport ? {
637
+ startPrice: Math.floor(this.viewport.minPrice / t) * t,
638
+ endPrice: this.viewport.maxPrice + t
639
+ } : {
640
+ startPrice: 0,
641
+ endPrice: -1
642
+ };
643
+ }
644
+ getPriceY(t, e) {
645
+ return this.viewport ? Y({
646
+ price: t,
647
+ minPrice: this.viewport.minPrice,
648
+ priceRange: this.viewport.priceRange,
649
+ chartHeight: e
650
+ }) : 0;
651
+ }
652
+ isPriceOutsideCanvas(t, e) {
653
+ return t < 0 || t > e;
654
+ }
655
+ drawTick(t) {
656
+ const e = this.#e, i = c.axis.axisY;
657
+ e.strokeStyle = i.tickColor, e.lineWidth = i.tickWidth, e.beginPath(), e.moveTo(0, t), e.lineTo(i.tickLength, t), e.stroke();
658
+ }
659
+ drawPriceLabel(t, e) {
660
+ const i = this.#e, r = c.axis.axisY;
661
+ i.fillStyle = r.textColor, i.fillText(p(t).toFixed(5), r.labelOffsetX, e);
662
+ }
663
+ getCrosshairLabelY(t, e) {
664
+ return Math.max(0, Math.min(e - t, this.crosshair.y - t / 2));
665
+ }
666
+ }, Et = class {
667
+ #t;
668
+ #e;
669
+ mouseX = 0;
670
+ mouseY = 0;
671
+ visible = !1;
672
+ crosshairColor;
673
+ crosshairThickness;
674
+ crosshairStyle;
675
+ constructor(t) {
676
+ this.#t = t.canvas;
677
+ const e = this.#t.getContext("2d");
678
+ if (!e) throw new Error("Canvas 2D context not supported");
679
+ this.#e = e, this.crosshairColor = t.crosshairColor ?? c.crosshair.color, this.crosshairThickness = t.crosshairThickness ?? c.crosshair.thickness, this.crosshairStyle = t.crosshairStyle ?? c.crosshair.style;
680
+ }
681
+ updateMousePosition(t, e, i = {}) {
682
+ const r = this.getLocalCanvasPoint(t, e), s = i.snapX ?? r.x;
683
+ this.mouseX = this.clampX(s), this.mouseY = this.clampY(r.y), this.visible = !0;
684
+ }
685
+ hide() {
686
+ this.visible = !1;
687
+ }
688
+ applyCrosshairStyle() {
689
+ const t = this.#e;
690
+ t.strokeStyle = this.crosshairColor, t.lineWidth = this.crosshairThickness, t.setLineDash(this.getLineDash());
691
+ }
692
+ drawVerticalCrosshairLine(t, e) {
693
+ this.drawLine({
694
+ ctx: t,
695
+ fromX: this.mouseX,
696
+ fromY: 0,
697
+ toX: this.mouseX,
698
+ toY: e
699
+ });
700
+ }
701
+ drawHorizontalCrosshairLine(t, e) {
702
+ this.drawLine({
703
+ ctx: t,
704
+ fromX: 0,
705
+ fromY: this.mouseY,
706
+ toX: e,
707
+ toY: this.mouseY
708
+ });
709
+ }
710
+ render() {
711
+ const t = this.#e, e = this.#t.width, i = this.#t.height;
712
+ t.clearRect(0, 0, e, i), this.visible && (t.save(), this.applyCrosshairStyle(), this.drawVerticalCrosshairLine(t, i), this.drawHorizontalCrosshairLine(t, e), t.restore());
713
+ }
714
+ getLocalCanvasPoint(t, e) {
715
+ const i = this.#t.getBoundingClientRect(), r = this.#t.width / i.width, s = this.#t.height / i.height;
716
+ return {
717
+ x: (t - i.left) * r,
718
+ y: (e - i.top) * s
719
+ };
720
+ }
721
+ clampX(t) {
722
+ return this.clamp(t, 0, this.#t.width);
723
+ }
724
+ clampY(t) {
725
+ return this.clamp(t, 0, this.#t.height);
726
+ }
727
+ clamp(t, e, i) {
728
+ return Math.max(e, Math.min(i, t));
729
+ }
730
+ getLineDash() {
731
+ switch (this.crosshairStyle) {
732
+ case "solid":
733
+ return [];
734
+ case "dashed":
735
+ return [8, 6];
736
+ case "dotted":
737
+ return [2, 6];
738
+ }
739
+ }
740
+ drawLine({ ctx: t, fromX: e, fromY: i, toX: r, toY: s }) {
741
+ t.beginPath(), t.moveTo(e, i), t.lineTo(r, s), t.stroke();
742
+ }
743
+ }, F = 60 * 1e3, pt = 60 * F, zt = 24 * pt, Nt = class {
744
+ #t;
745
+ #e;
746
+ candles;
747
+ baseCandleWidth;
748
+ baseCandleGap;
749
+ bullishColor;
750
+ bearishColor;
751
+ offsetX;
752
+ zoomX;
753
+ priceRange;
754
+ priceCenter;
755
+ initialPriceRange;
756
+ autoFollowLatestCandle;
757
+ isFollowingLatest = !1;
758
+ autoFollowThresholdCandles;
759
+ rightOffsetCandles;
760
+ constructor(t) {
761
+ this.#t = t.canvas;
762
+ const e = this.#t.getContext("2d");
763
+ if (!e) throw new Error("Canvas 2D context not supported");
764
+ this.#e = e, this.candles = t.candles, this.baseCandleWidth = t.baseCandleWidth ?? c.candles.defaultWidth, this.baseCandleGap = t.baseCandleGap ?? c.candles.defaultGap, this.bullishColor = t.bullishColor ?? c.colors.bullish, this.bearishColor = t.bearishColor ?? c.colors.bearish, this.zoomX = t.zoomX ?? 1, this.autoFollowLatestCandle = t.autoFollowLatestCandle ?? c.candles.autoFollowLatestCandle, this.autoFollowThresholdCandles = t.autoFollowThresholdCandles ?? c.candles.autoFollowThresholdCandles, this.rightOffsetCandles = t.rightOffsetCandles ?? c.candles.rightOffsetCandles, this.offsetX = t.offsetX ?? this.getDefaultOffsetX(), this.isFollowingLatest = this.isWithinAutoFollowThreshold(), this.priceCenter = 0, this.priceRange = 1, this.initialPriceRange = 1, this.initializeViewport(), this.applyManualViewportOverrides(t);
765
+ }
766
+ initializeViewport() {
767
+ if (this.candles.length === 0) {
768
+ this.setFallbackViewport();
769
+ return;
770
+ }
771
+ const t = this.getInitialVisibleCandles(), { minPrice: e, maxPrice: i } = this.getCandlesPriceBounds(t), r = this.getInitialPriceRange(e, i);
772
+ this.priceCenter = (e + i) / 2, this.priceRange = r, this.initialPriceRange = r;
773
+ }
774
+ isWithinAutoFollowThreshold() {
775
+ return this.getRightGap() <= this.autoFollowThresholdCandles * this.candleSpacing + 5;
776
+ }
777
+ updateLiveCandle(t) {
778
+ const e = this.candles.at(-1);
779
+ if (e && e.time === t.time) {
780
+ this.candles[this.candles.length - 1] = t;
781
+ return;
782
+ }
783
+ this.closeLastCandle(e), this.appendLiveCandle(t), this.updateAutoFollowAfterAppend();
784
+ }
785
+ get candleWidth() {
786
+ return this.baseCandleWidth * this.zoomX;
787
+ }
788
+ get candleGap() {
789
+ return this.baseCandleGap * this.zoomX;
790
+ }
791
+ get candleSpacing() {
792
+ return this.candleWidth + this.candleGap;
793
+ }
794
+ get minPrice() {
795
+ return this.priceCenter - this.priceRange / 2;
796
+ }
797
+ get maxPrice() {
798
+ return this.priceCenter + this.priceRange / 2;
799
+ }
800
+ get liveCandle() {
801
+ return this.candles[this.candles.length - 1];
802
+ }
803
+ get viewport() {
804
+ return {
805
+ minPrice: this.minPrice,
806
+ maxPrice: this.maxPrice,
807
+ priceRange: this.priceRange,
808
+ offsetX: this.offsetX,
809
+ zoomX: this.zoomX,
810
+ candleWidth: this.candleWidth,
811
+ candleGap: this.candleGap,
812
+ candleSpacing: this.candleSpacing
813
+ };
814
+ }
815
+ getVisibleRange(t) {
816
+ return {
817
+ startIndex: Math.max(0, Math.floor(-this.offsetX / this.candleSpacing)),
818
+ endIndex: Math.min(this.candles.length - 1, Math.ceil((t - this.offsetX) / this.candleSpacing))
819
+ };
820
+ }
821
+ getCandleX(t) {
822
+ return t * this.candleSpacing + this.offsetX;
823
+ }
824
+ getCandleCenterX(t) {
825
+ return this.getCandleX(t) + this.candleWidth / 2;
826
+ }
827
+ getNearestCandleIndexByX(t) {
828
+ if (this.candles.length === 0) return null;
829
+ const e = Math.round((t - this.offsetX - this.candleWidth / 2) / this.candleSpacing);
830
+ return this.clampCandleIndex(e);
831
+ }
832
+ getRawCandleSnapX(t) {
833
+ const e = Math.round((t - this.offsetX - this.candleWidth / 2) / this.candleSpacing);
834
+ return this.getCandleCenterX(e);
835
+ }
836
+ getPriceAtY(t) {
837
+ return p(ft({
838
+ y: t,
839
+ minPrice: this.minPrice,
840
+ priceRange: this.priceRange,
841
+ chartHeight: this.#t.height
842
+ }));
843
+ }
844
+ panHorizontally(t) {
845
+ this.offsetX += t, this.isFollowingLatest = this.isWithinAutoFollowThreshold();
846
+ }
847
+ panVertically(t) {
848
+ const e = t / this.#t.height * this.priceRange;
849
+ this.priceCenter += e;
850
+ }
851
+ zoomHorizontally(t) {
852
+ const e = this.getRightEdgeCandleIndex();
853
+ this.zoomX = this.getNextHorizontalZoom(t), this.offsetX = this.#t.width - e * this.candleSpacing, this.isFollowingLatest = this.isWithinAutoFollowThreshold();
854
+ }
855
+ zoomVertically(t) {
856
+ this.priceRange = this.getNextVerticalPriceRange(t);
857
+ }
858
+ resetView() {
859
+ this.zoomX = 1, this.offsetX = this.getDefaultOffsetX(), this.initializeViewport(), this.isFollowingLatest = this.isWithinAutoFollowThreshold();
860
+ }
861
+ render() {
862
+ const t = this.#e, e = this.#t.width, i = this.#t.height;
863
+ if (this.clearCanvas(t, e, i), this.candles.length === 0) return;
864
+ const { startIndex: r, endIndex: s } = this.getVisibleRange(e), n = this.candles.slice(r, s + 1);
865
+ this.drawCandles({
866
+ ctx: t,
867
+ candles: n,
868
+ startIndex: r,
869
+ chartHeight: i
870
+ });
871
+ const a = {
872
+ ctx: t,
873
+ chartWidth: e,
874
+ chartHeight: i
875
+ };
876
+ this.drawAskPriceLine(a), this.drawBidPriceLine(a);
877
+ }
878
+ drawCandles({ ctx: t, candles: e, startIndex: i, chartHeight: r }) {
879
+ e.forEach((s, n) => {
880
+ const a = i + n, o = this.getCandleX(a), l = this.getCandleColor(s), d = this.getCandleYCoordinates(s, r);
881
+ this.drawSingleCandle({
882
+ ctx: t,
883
+ candleX: o,
884
+ candleColor: l,
885
+ ...d
886
+ });
887
+ });
888
+ }
889
+ drawSingleCandle({ ctx: t, candleX: e, openY: i, closeY: r, highY: s, lowY: n, candleColor: a }) {
890
+ t.strokeStyle = a, t.fillStyle = a;
891
+ const o = e + this.candleWidth / 2;
892
+ t.beginPath(), t.moveTo(o, s), t.lineTo(o, n), t.stroke();
893
+ const l = Math.min(i, r), d = Math.max(Math.abs(r - i), c.candles.minBodyHeight);
894
+ t.fillRect(e, l, this.candleWidth, d);
895
+ }
896
+ drawPriceLine({ ctx: t, chartWidth: e, chartHeight: i, price: r, color: s, opacity: n, lineWidth: a, dash: o }) {
897
+ const l = this.getPriceY(p(r), i);
898
+ t.save(), t.globalAlpha = n, t.strokeStyle = s, t.lineWidth = a, t.setLineDash(o), t.beginPath(), t.moveTo(0, l), t.lineTo(e, l), t.stroke(), t.restore();
899
+ }
900
+ drawBidPriceLine({ ctx: t, chartWidth: e, chartHeight: i }) {
901
+ const r = this.candles.at(-1);
902
+ !r || !c.candles.liveBidPriceLine.visible || this.drawPriceLine({
903
+ ctx: t,
904
+ chartWidth: e,
905
+ chartHeight: i,
906
+ price: r.close,
907
+ color: this.getBidPriceLineColor(r),
908
+ opacity: c.candles.liveBidPriceLine.opacity,
909
+ lineWidth: c.candles.liveBidPriceLine.width,
910
+ dash: c.candles.liveBidPriceLine.dash
911
+ });
912
+ }
913
+ drawAskPriceLine({ ctx: t, chartWidth: e, chartHeight: i }) {
914
+ const r = this.candles.at(-1);
915
+ !r || !c.candles.liveAskPriceLine.visible || r.ask == null || this.drawPriceLine({
916
+ ctx: t,
917
+ chartWidth: e,
918
+ chartHeight: i,
919
+ price: r.ask,
920
+ color: this.getAskPriceLineColor(r),
921
+ opacity: c.candles.liveAskPriceLine.opacity,
922
+ lineWidth: c.candles.liveAskPriceLine.width,
923
+ dash: c.candles.liveAskPriceLine.dash
924
+ });
925
+ }
926
+ getDefaultOffsetX() {
927
+ const t = this.getTotalChartWidth(), e = this.getRightOffsetPixels();
928
+ return this.#t.width - t - e;
929
+ }
930
+ getTotalChartWidth() {
931
+ return this.candles.length * this.candleSpacing;
932
+ }
933
+ getRightOffsetPixels() {
934
+ return this.rightOffsetCandles * this.candleSpacing;
935
+ }
936
+ getRightGap() {
937
+ return this.#t.width - (this.getTotalChartWidth() + this.offsetX) - this.getRightOffsetPixels();
938
+ }
939
+ setFallbackViewport() {
940
+ this.priceCenter = 100, this.priceRange = 20, this.initialPriceRange = this.priceRange;
941
+ }
942
+ getInitialVisibleCandles() {
943
+ const t = Math.ceil(this.#t.width / this.candleSpacing), e = Math.max(0, this.candles.length - t);
944
+ return this.candles.slice(e);
945
+ }
946
+ getCandlesPriceBounds(t) {
947
+ let e = Number.POSITIVE_INFINITY, i = Number.NEGATIVE_INFINITY;
948
+ for (const r of t)
949
+ r.low < e && (e = r.low), r.high > i && (i = r.high);
950
+ return {
951
+ minPrice: e,
952
+ maxPrice: i
953
+ };
954
+ }
955
+ getInitialPriceRange(t, e) {
956
+ const i = this.getInitialFallbackPercent(), r = p(e - t), s = Math.max(Math.abs((t + e) / 2) * i, 1e-5), n = Math.max(r, s);
957
+ return n + n * 0.2;
958
+ }
959
+ getInitialFallbackPercent() {
960
+ const t = this.detectCandleTimeframeMs();
961
+ return t <= F ? 25e-4 : t <= 5 * F ? 35e-4 : t <= 15 * F ? 5e-3 : t <= pt ? 75e-4 : (t < zt, 0.01);
962
+ }
963
+ detectCandleTimeframeMs() {
964
+ if (this.candles.length < 2) return 15 * F;
965
+ const t = [];
966
+ for (let e = 1; e < Math.min(this.candles.length, 10); e++) t.push(this.candles[e].time - this.candles[e - 1].time);
967
+ return t.sort((e, i) => e - i), t[Math.floor(t.length / 2)];
968
+ }
969
+ applyManualViewportOverrides(t) {
970
+ t.priceCenter !== void 0 && (this.priceCenter = t.priceCenter), t.priceRange !== void 0 && (this.priceRange = t.priceRange, this.initialPriceRange = t.priceRange);
971
+ }
972
+ closeLastCandle(t) {
973
+ t && (t.isClosed = !0);
974
+ }
975
+ appendLiveCandle(t) {
976
+ this.candles.push(t);
977
+ }
978
+ updateAutoFollowAfterAppend() {
979
+ this.isFollowingLatest = this.isWithinAutoFollowThreshold(), this.autoFollowLatestCandle && this.isFollowingLatest && (this.offsetX -= this.candleSpacing);
980
+ }
981
+ clampCandleIndex(t) {
982
+ return Math.max(0, Math.min(this.candles.length - 1, t));
983
+ }
984
+ getRightEdgeCandleIndex() {
985
+ return (this.#t.width - this.offsetX) / this.candleSpacing;
986
+ }
987
+ getNextHorizontalZoom(t) {
988
+ const { speed: e, min: i, max: r } = c.zoom.x, s = this.zoomX + t * e;
989
+ return Math.max(i, Math.min(s, r));
990
+ }
991
+ getNextVerticalPriceRange(t) {
992
+ const { speed: e, min: i, max: r } = c.zoom.y, s = 1 - t * e, n = this.priceRange * s, a = this.initialPriceRange * (i / 100), o = this.initialPriceRange * (r / 100);
993
+ return Math.max(a, Math.min(n, o));
994
+ }
995
+ clearCanvas(t, e, i) {
996
+ t.clearRect(0, 0, e, i);
997
+ }
998
+ getCandleColor(t) {
999
+ return t.close >= t.open ? this.bullishColor : this.bearishColor;
1000
+ }
1001
+ getCandleYCoordinates(t, e) {
1002
+ return {
1003
+ openY: this.getPriceY(t.open, e),
1004
+ closeY: this.getPriceY(t.close, e),
1005
+ highY: this.getPriceY(t.high, e),
1006
+ lowY: this.getPriceY(t.low, e)
1007
+ };
1008
+ }
1009
+ getPriceY(t, e) {
1010
+ return Y({
1011
+ price: t,
1012
+ minPrice: this.minPrice,
1013
+ priceRange: this.priceRange,
1014
+ chartHeight: e
1015
+ });
1016
+ }
1017
+ getBidPriceLineColor(t) {
1018
+ return t.close >= t.open ? c.candles.liveBidPriceLine.bullishColor : c.candles.liveBidPriceLine.bearishColor;
1019
+ }
1020
+ getAskPriceLineColor(t) {
1021
+ return t.close >= t.open ? c.candles.liveAskPriceLine.bullishColor : c.candles.liveAskPriceLine.bearishColor;
1022
+ }
1023
+ }, Gt = class {
1024
+ #t;
1025
+ #e;
1026
+ #r = null;
1027
+ #s = null;
1028
+ constructor({ ctx: t, config: e }) {
1029
+ this.#t = t, this.#e = e;
1030
+ }
1031
+ setViewport(t) {
1032
+ this.#r = t;
1033
+ }
1034
+ setConfig(t) {
1035
+ this.#e = t;
1036
+ }
1037
+ setPreviousDay(t) {
1038
+ this.#s = t ?? null;
1039
+ }
1040
+ render() {
1041
+ if (!this.#r || !this.#s) return;
1042
+ const { high: t, low: e } = this.#s;
1043
+ Number.isFinite(t) && t > 0 && this.#c(t, "PDH"), Number.isFinite(e) && e > 0 && this.#c(e, "PDL");
1044
+ }
1045
+ #c(t, e) {
1046
+ if (!this.#r || !this.#e) return;
1047
+ const i = this.#t, r = i.canvas, s = Y({
1048
+ price: t,
1049
+ minPrice: this.#r.minPrice,
1050
+ priceRange: this.#r.priceRange,
1051
+ chartHeight: r.height
1052
+ });
1053
+ if (s < 0 || s > r.height) return;
1054
+ const { line: n, text: a } = this.#e, o = r.width;
1055
+ i.save(), i.font = a.font;
1056
+ const l = i.measureText(e).width, d = o / 2, u = l / 2 + a.paddingX;
1057
+ i.globalAlpha = n.opacity, i.strokeStyle = n.color, i.lineWidth = n.width, i.setLineDash(n.dash), i.beginPath(), i.moveTo(0, s), i.lineTo(d - u, s), i.stroke(), i.beginPath(), i.moveTo(d + u, s), i.lineTo(o, s), i.stroke(), i.setLineDash([]), i.globalAlpha = 1, i.fillStyle = a.color, i.textAlign = "center", i.textBaseline = "middle", i.fillText(e, d, s), i.restore();
1058
+ }
1059
+ }, mt = (t, e) => {
1060
+ const i = t.getBoundingClientRect(), r = t.width / i.width, s = t.height / i.height;
1061
+ return {
1062
+ x: (e.clientX - i.left) * r,
1063
+ y: (e.clientY - i.top) * s
1064
+ };
1065
+ };
1066
+ var U = () => typeof crypto < "u" && "randomUUID" in crypto ? crypto.randomUUID() : `shape-${Date.now()}-${Math.random().toString(16).slice(2)}`, at = (t, e) => mt(t, e), ot = (t, e) => ({
1067
+ time: e.getNearestCandleTimeForX(t.x),
1068
+ price: e.getPriceForY(t.y)
1069
+ }), v = (t, e) => ({
1070
+ x: e.getXForTime(t.time),
1071
+ y: e.getYForPrice(t.price)
1072
+ }), Ut = (t, e) => {
1073
+ const i = Math.min(t.x, e.x), r = Math.max(t.x, e.x), s = Math.min(t.y, e.y), n = Math.max(t.y, e.y);
1074
+ return {
1075
+ left: i,
1076
+ top: s,
1077
+ right: r,
1078
+ bottom: n,
1079
+ width: r - i,
1080
+ height: n - s
1081
+ };
1082
+ }, _ = (t, e, i) => Ut(v(t, i), v(e, i)), $ = (t, e) => {
1083
+ const i = t.x - e.x, r = t.y - e.y;
1084
+ return Math.sqrt(i * i + r * r);
1085
+ }, Ct = (t, e, i) => {
1086
+ const r = i.x - e.x, s = i.y - e.y, n = r * r + s * s;
1087
+ if (n === 0) return $(t, e);
1088
+ const a = Math.max(0, Math.min(1, ((t.x - e.x) * r + (t.y - e.y) * s) / n));
1089
+ return $(t, {
1090
+ x: e.x + a * r,
1091
+ y: e.y + a * s
1092
+ });
1093
+ }, vt = (t, e, i = 0) => t.x >= e.left - i && t.x <= e.right + i && t.y >= e.top - i && t.y <= e.bottom + i, _t = (t, e, i = 8) => {
1094
+ if (!vt(t, e, i)) return !1;
1095
+ const r = Math.abs(t.x - e.left) <= i, s = Math.abs(t.x - e.right) <= i, n = Math.abs(t.y - e.top) <= i, a = Math.abs(t.y - e.bottom) <= i, o = t.x >= e.left - i && t.x <= e.right + i, l = t.y >= e.top - i && t.y <= e.bottom + i;
1096
+ return (r || s) && l || (n || a) && o;
1097
+ }, b = ({ shapeId: t, type: e, point: i, radius: r = 5, cursor: s, index: n }) => ({
1098
+ shapeId: t,
1099
+ type: e,
1100
+ x: i.x,
1101
+ y: i.y,
1102
+ radius: r,
1103
+ cursor: s,
1104
+ index: n
1105
+ }), J = (t, e) => $(t, e) <= e.radius + 2, E = (t, e = 1) => t === "dashed" ? [e * 6, e * 4] : t === "dotted" ? [e, e * 3] : [], K = (t, e, i) => {
1106
+ t.strokeStyle = P(e.lineColor, e.lineOpacity), t.lineWidth = i ?? e.lineWidth, t.setLineDash(E(e.lineStyle, t.lineWidth));
1107
+ }, yt = (t, e, i) => !e || i === void 0 || i <= 0 ? !1 : (t.fillStyle = P(e, i), !0), O = (t) => {
1108
+ t.setLineDash([]);
1109
+ }, $t = (t, e, i = {
1110
+ fillColor: "#ffffff",
1111
+ borderColor: "#2962ff",
1112
+ borderThickness: 3
1113
+ }) => {
1114
+ t.save(), t.beginPath(), t.arc(e.x, e.y, e.radius, 0, Math.PI * 2), t.fillStyle = i.fillColor, t.fill(), t.lineWidth = i.borderThickness, t.strokeStyle = i.borderColor, t.stroke(), t.restore();
1115
+ }, V = (t, e, i) => {
1116
+ e.forEach((r) => {
1117
+ $t(t, r, i);
1118
+ });
1119
+ }, P = (t, e) => {
1120
+ const i = jt(e, 0, 1);
1121
+ return t.startsWith("rgba(") ? t : t.startsWith("rgb(") ? t.replace("rgb(", "rgba(").replace(")", `, ${i})`) : t.startsWith("#") ? Jt(t, i) : t;
1122
+ }, Jt = (t, e) => {
1123
+ const i = t.replace("#", ""), r = i.length === 3 ? i.split("").map((s) => `${s}${s}`).join("") : i;
1124
+ return `rgba(${parseInt(r.slice(0, 2), 16)}, ${parseInt(r.slice(2, 4), 16)}, ${parseInt(r.slice(4, 6), 16)}, ${e})`;
1125
+ }, jt = (t, e, i) => Math.min(i, Math.max(e, t)), lt = (t, e, i) => e + (t - e) * i, j = (t, e) => {
1126
+ if (t.length === 0) return -1;
1127
+ let i = 0, r = Math.abs(t[0].time - e);
1128
+ for (let s = 1; s < t.length; s += 1) {
1129
+ const n = Math.abs(t[s].time - e);
1130
+ n < r && (r = n, i = s);
1131
+ }
1132
+ return i;
1133
+ }, Pt = (t) => {
1134
+ if (t.length < 2) return 900 * 1e3;
1135
+ const e = [];
1136
+ for (let i = 1; i < t.length; i += 1) {
1137
+ const r = t[i].time - t[i - 1].time;
1138
+ r > 0 && e.push(r);
1139
+ }
1140
+ return e.length === 0 ? 900 * 1e3 : e.reduce((i, r) => i + r, 0) / e.length;
1141
+ }, Kt = (t, e, i) => {
1142
+ const r = j(t, e), s = r + i;
1143
+ return r >= 0 && s >= 0 && s < t.length ? t[s].time : e + Pt(t) * i;
1144
+ }, ht = (t) => t instanceof MouseEvent && t.type === "contextmenu" ? !1 : "button" in t && t.type === "pointerdown" ? t.button === 0 : !0, S = class D {
1145
+ static defaultConfig = {
1146
+ levels: [
1147
+ {
1148
+ level: 1,
1149
+ label: "1",
1150
+ color: "#808080",
1151
+ opacity: 0.8,
1152
+ lineWidth: 1,
1153
+ lineStyle: "dashed"
1154
+ },
1155
+ {
1156
+ level: 0.72,
1157
+ label: "0.720",
1158
+ color: "#ff3b30",
1159
+ opacity: 0.8,
1160
+ lineWidth: 1,
1161
+ lineStyle: "dashed"
1162
+ },
1163
+ {
1164
+ level: 0.618,
1165
+ label: "0.618",
1166
+ color: "#ff9500",
1167
+ opacity: 0.8,
1168
+ lineWidth: 1,
1169
+ lineStyle: "dashed"
1170
+ },
1171
+ {
1172
+ level: 0.5,
1173
+ label: "0.5",
1174
+ color: "#af52de",
1175
+ opacity: 0.8,
1176
+ lineWidth: 1,
1177
+ lineStyle: "dashed"
1178
+ },
1179
+ {
1180
+ level: 0,
1181
+ label: "0",
1182
+ color: "#808080",
1183
+ opacity: 0.8,
1184
+ lineWidth: 1,
1185
+ lineStyle: "dashed"
1186
+ }
1187
+ ],
1188
+ fillColor: "#808080",
1189
+ fillOpacity: 0.04,
1190
+ handleColor: "#ffffff",
1191
+ handleBorderColor: "#2962ff",
1192
+ handleBorderThickness: 1.5,
1193
+ handleRadius: 5,
1194
+ hoverLineWidth: 2,
1195
+ selectedLineWidth: 2
1196
+ };
1197
+ static draw({ ctx: e, shape: i, converter: r, config: s = D.defaultConfig, selected: n = !1, hovered: a = !1 }) {
1198
+ const [o, l] = i.vertices, d = v(o, r), u = v(l, r), g = _(o, l, r), m = Math.min(d.x, u.x), w = Math.max(d.x, u.x);
1199
+ e.save(), yt(e, s.fillColor, s.fillOpacity) && e.fillRect(g.left, g.top, g.width, g.height);
1200
+ for (const C of s.levels) {
1201
+ const x = lt(o.price, l.price, C.level), T = r.getYForPrice(x), H = n ? Math.max(C.lineWidth, s.selectedLineWidth) : a ? Math.max(C.lineWidth, s.hoverLineWidth) : C.lineWidth;
1202
+ e.beginPath(), e.strokeStyle = P(C.color, C.opacity), e.lineWidth = H, e.setLineDash(E(C.lineStyle, H)), e.moveTo(m, T), e.lineTo(w, T), e.stroke(), C.label && (e.setLineDash([]), e.fillStyle = P(C.color, C.opacity), e.font = "11px sans-serif", e.textBaseline = "middle", e.fillText(Number(C.label).toFixed(2).toString(), w + 12, T));
1203
+ }
1204
+ O(e), (n || a) && V(e, D.getHandles(i, r, s), {
1205
+ fillColor: s.handleColor,
1206
+ borderColor: s.handleBorderColor,
1207
+ borderThickness: s.handleBorderThickness
1208
+ }), e.restore();
1209
+ }
1210
+ static drawDraft({ ctx: e, vertices: i, converter: r, config: s = D.defaultConfig }) {
1211
+ D.draw({
1212
+ ctx: e,
1213
+ shape: {
1214
+ id: "__draft_fib_retracement__",
1215
+ type: "fibRetracement",
1216
+ vertices: i
1217
+ },
1218
+ converter: r,
1219
+ config: s,
1220
+ selected: !0
1221
+ });
1222
+ }
1223
+ static getHandles(e, i, r = D.defaultConfig) {
1224
+ const [s, n] = e.vertices, a = v(s, i), o = v(n, i);
1225
+ return [b({
1226
+ shapeId: e.id,
1227
+ type: "start",
1228
+ point: a,
1229
+ radius: r.handleRadius,
1230
+ cursor: "grab"
1231
+ }), b({
1232
+ shapeId: e.id,
1233
+ type: "end",
1234
+ point: o,
1235
+ radius: r.handleRadius,
1236
+ cursor: "grab"
1237
+ })];
1238
+ }
1239
+ static hitTest({ point: e, shape: i, converter: r, tolerance: s = 8, config: n = D.defaultConfig }) {
1240
+ const [a, o] = i.vertices, l = v(a, r), d = v(o, r), u = Math.min(l.x, d.x), g = Math.max(l.x, d.x);
1241
+ if (e.x < u - s || e.x > g + s) return !1;
1242
+ for (const m of n.levels) {
1243
+ const w = lt(a.price, o.price, m.level), C = r.getYForPrice(w);
1244
+ if (Math.abs(e.y - C) <= s) return !0;
1245
+ }
1246
+ return !1;
1247
+ }
1248
+ static move(e, i) {
1249
+ return {
1250
+ ...e,
1251
+ vertices: [{
1252
+ time: e.vertices[0].time + i.time,
1253
+ price: e.vertices[0].price + i.price
1254
+ }, {
1255
+ time: e.vertices[1].time + i.time,
1256
+ price: e.vertices[1].price + i.price
1257
+ }]
1258
+ };
1259
+ }
1260
+ static resize(e, i, r) {
1261
+ return i === "start" ? {
1262
+ ...e,
1263
+ vertices: [r, e.vertices[1]]
1264
+ } : i === "end" ? {
1265
+ ...e,
1266
+ vertices: [e.vertices[0], r]
1267
+ } : e;
1268
+ }
1269
+ }, k = class I {
1270
+ static defaultConfig = {
1271
+ lineWidth: 2,
1272
+ lineColor: "#2962ff",
1273
+ lineOpacity: 1,
1274
+ lineStyle: "solid",
1275
+ handleColor: "#ffffff",
1276
+ handleBorderColor: "#2962ff",
1277
+ handleBorderThickness: 1.5,
1278
+ handleRadius: 5,
1279
+ hoverLineWidth: 3,
1280
+ selectedLineWidth: 3
1281
+ };
1282
+ static draw({ ctx: e, shape: i, converter: r, config: s = I.defaultConfig, selected: n = !1, hovered: a = !1 }) {
1283
+ const [o, l] = i.vertices, d = v(o, r), u = v(l, r);
1284
+ e.save(), K(e, s, n ? s.selectedLineWidth : a ? s.hoverLineWidth : void 0), e.beginPath(), e.moveTo(d.x, d.y), e.lineTo(u.x, u.y), e.stroke(), O(e), (n || a) && V(e, I.getHandles(i, r, s), {
1285
+ fillColor: s.handleColor,
1286
+ borderColor: s.handleBorderColor,
1287
+ borderThickness: s.handleBorderThickness
1288
+ }), e.restore();
1289
+ }
1290
+ static drawDraft({ ctx: e, vertices: i, converter: r, config: s = I.defaultConfig }) {
1291
+ I.draw({
1292
+ ctx: e,
1293
+ shape: {
1294
+ id: "__draft_trendline__",
1295
+ type: "trendline",
1296
+ vertices: i
1297
+ },
1298
+ converter: r,
1299
+ config: s,
1300
+ selected: !0
1301
+ });
1302
+ }
1303
+ static getHandles(e, i, r = I.defaultConfig) {
1304
+ const [s, n] = e.vertices, a = v(s, i), o = v(n, i);
1305
+ return [b({
1306
+ shapeId: e.id,
1307
+ type: "start",
1308
+ point: a,
1309
+ radius: r.handleRadius,
1310
+ cursor: "grab"
1311
+ }), b({
1312
+ shapeId: e.id,
1313
+ type: "end",
1314
+ point: o,
1315
+ radius: r.handleRadius,
1316
+ cursor: "grab"
1317
+ })];
1318
+ }
1319
+ static hitTest({ point: e, shape: i, converter: r, tolerance: s = 8 }) {
1320
+ const [n, a] = i.vertices;
1321
+ return Ct(e, v(n, r), v(a, r)) <= s;
1322
+ }
1323
+ static move(e, i) {
1324
+ return {
1325
+ ...e,
1326
+ vertices: [{
1327
+ time: e.vertices[0].time + i.time,
1328
+ price: e.vertices[0].price + i.price
1329
+ }, {
1330
+ time: e.vertices[1].time + i.time,
1331
+ price: e.vertices[1].price + i.price
1332
+ }]
1333
+ };
1334
+ }
1335
+ static resize(e, i, r) {
1336
+ return i === "start" ? {
1337
+ ...e,
1338
+ vertices: [r, e.vertices[1]]
1339
+ } : i === "end" ? {
1340
+ ...e,
1341
+ vertices: [e.vertices[0], r]
1342
+ } : e;
1343
+ }
1344
+ }, M = class B {
1345
+ static defaultConfig = {
1346
+ lineWidth: 2,
1347
+ lineColor: "#2962ff",
1348
+ lineOpacity: 1,
1349
+ lineStyle: "solid",
1350
+ fillColor: "#2962ff",
1351
+ fillOpacity: 0,
1352
+ handleColor: "#ffffff",
1353
+ handleBorderColor: "#2962ff",
1354
+ handleBorderThickness: 1.5,
1355
+ handleRadius: 5,
1356
+ hoverLineWidth: 3,
1357
+ selectedLineWidth: 3
1358
+ };
1359
+ static draw({ ctx: e, shape: i, converter: r, config: s = B.defaultConfig, selected: n = !1, hovered: a = !1 }) {
1360
+ const o = i.vertices.map((l) => v(l, r));
1361
+ if (o.length !== 0) {
1362
+ e.save(), K(e, s, n ? s.selectedLineWidth : a ? s.hoverLineWidth : void 0), e.beginPath(), e.moveTo(o[0].x, o[0].y);
1363
+ for (let l = 1; l < o.length; l += 1) e.lineTo(o[l].x, o[l].y);
1364
+ e.stroke(), O(e), o.length >= 2 && B.drawEndArrow({
1365
+ ctx: e,
1366
+ previousPoint: o[o.length - 2],
1367
+ endPoint: o[o.length - 1],
1368
+ color: s.lineColor,
1369
+ opacity: s.lineOpacity,
1370
+ lineWidth: n ? s.selectedLineWidth ?? s.lineWidth : a ? s.hoverLineWidth ?? s.lineWidth : s.lineWidth
1371
+ }), (n || a) && V(e, B.getHandles(i, r, s), {
1372
+ fillColor: s.handleColor,
1373
+ borderColor: s.handleBorderColor,
1374
+ borderThickness: s.handleBorderThickness
1375
+ }), e.restore();
1376
+ }
1377
+ }
1378
+ static drawDraft({ ctx: e, vertices: i, previewVertex: r, converter: s, config: n = B.defaultConfig }) {
1379
+ const a = r ? [...i, r] : i;
1380
+ B.draw({
1381
+ ctx: e,
1382
+ shape: {
1383
+ id: "__draft_path__",
1384
+ type: "path",
1385
+ vertices: a
1386
+ },
1387
+ converter: s,
1388
+ config: n,
1389
+ selected: !0
1390
+ });
1391
+ }
1392
+ static getHandles(e, i, r = B.defaultConfig) {
1393
+ return e.vertices.map((s, n) => b({
1394
+ shapeId: e.id,
1395
+ type: n === 0 ? "start" : n === e.vertices.length - 1 ? "end" : "move",
1396
+ point: v(s, i),
1397
+ radius: r.handleRadius,
1398
+ cursor: "grab",
1399
+ index: n
1400
+ }));
1401
+ }
1402
+ static hitTest({ point: e, shape: i, converter: r, tolerance: s = 8 }) {
1403
+ const n = i.vertices.map((a) => v(a, r));
1404
+ if (n.length === 1) {
1405
+ const a = n[0], o = e.x - a.x, l = e.y - a.y;
1406
+ return Math.sqrt(o * o + l * l) <= s;
1407
+ }
1408
+ for (let a = 1; a < n.length; a += 1) if (Ct(e, n[a - 1], n[a]) <= s) return !0;
1409
+ return !1;
1410
+ }
1411
+ static move(e, i) {
1412
+ return {
1413
+ ...e,
1414
+ vertices: e.vertices.map((r) => ({
1415
+ time: r.time + i.time,
1416
+ price: r.price + i.price
1417
+ }))
1418
+ };
1419
+ }
1420
+ static resize(e, i, r) {
1421
+ return i < 0 || i >= e.vertices.length ? e : {
1422
+ ...e,
1423
+ vertices: e.vertices.map((s, n) => n === i ? r : s)
1424
+ };
1425
+ }
1426
+ static drawEndArrow({ ctx: e, previousPoint: i, endPoint: r, color: s, opacity: n, lineWidth: a }) {
1427
+ const o = Math.atan2(r.y - i.y, r.x - i.x), l = 12, d = Math.PI / 7;
1428
+ e.save(), e.beginPath(), e.strokeStyle = P(s, n), e.fillStyle = P(s, n), e.lineWidth = a, e.setLineDash([]), e.moveTo(r.x, r.y), e.lineTo(r.x - l * Math.cos(o - d), r.y - l * Math.sin(o - d)), e.lineTo(r.x - l * Math.cos(o + d), r.y - l * Math.sin(o + d)), e.closePath(), e.fill(), e.stroke(), e.restore();
1429
+ }
1430
+ }, R = class X {
1431
+ static defaultConfig = {
1432
+ lineWidth: 2,
1433
+ lineColor: "#2962ff",
1434
+ lineOpacity: 1,
1435
+ lineStyle: "solid",
1436
+ fillColor: "#2962ff",
1437
+ fillOpacity: 0.12,
1438
+ handleColor: "#ffffff",
1439
+ handleBorderColor: "#2962ff",
1440
+ handleBorderThickness: 1.5,
1441
+ handleRadius: 5,
1442
+ hoverLineWidth: 3,
1443
+ selectedLineWidth: 3,
1444
+ textColor: "#ffffff",
1445
+ textOpacity: 1,
1446
+ textFontFamily: "Arial",
1447
+ textFontSize: 20
1448
+ };
1449
+ static draw({ ctx: e, shape: i, converter: r, config: s = X.defaultConfig, selected: n = !1, hovered: a = !1 }) {
1450
+ const [o, l] = i.vertices, d = _(o, l, r);
1451
+ e.save(), yt(e, s.fillColor, s.fillOpacity) && e.fillRect(d.left, d.top, d.width, d.height), K(e, s, n ? s.selectedLineWidth : a ? s.hoverLineWidth : void 0), e.strokeRect(d.left, d.top, d.width, d.height), O(e), X.drawText(e, d, i.text, s), (n || a) && V(e, X.getHandles(i, r, s), {
1452
+ fillColor: s.handleColor,
1453
+ borderColor: s.handleBorderColor,
1454
+ borderThickness: s.handleBorderThickness
1455
+ }), e.restore();
1456
+ }
1457
+ static drawDraft({ ctx: e, vertices: i, converter: r, config: s = X.defaultConfig }) {
1458
+ X.draw({
1459
+ ctx: e,
1460
+ shape: {
1461
+ id: "__draft_rectangle__",
1462
+ type: "rectangle",
1463
+ vertices: i
1464
+ },
1465
+ converter: r,
1466
+ config: s,
1467
+ selected: !0
1468
+ });
1469
+ }
1470
+ static getHandles(e, i, r = X.defaultConfig) {
1471
+ const [s, n] = e.vertices, a = v(s, i), o = v(n, i), l = a.x <= o.x, d = a.y <= o.y, u = l === d ? "nwse-resize" : "nesw-resize", g = l === d ? "nwse-resize" : "nesw-resize";
1472
+ return [b({
1473
+ shapeId: e.id,
1474
+ type: "start",
1475
+ point: a,
1476
+ radius: r.handleRadius,
1477
+ cursor: u
1478
+ }), b({
1479
+ shapeId: e.id,
1480
+ type: "end",
1481
+ point: o,
1482
+ radius: r.handleRadius,
1483
+ cursor: g
1484
+ })];
1485
+ }
1486
+ static hitTest({ point: e, shape: i, converter: r, tolerance: s = 8 }) {
1487
+ const [n, a] = i.vertices;
1488
+ return _t(e, _(n, a, r), s);
1489
+ }
1490
+ static move(e, i) {
1491
+ return {
1492
+ ...e,
1493
+ vertices: [{
1494
+ time: e.vertices[0].time + i.time,
1495
+ price: e.vertices[0].price + i.price
1496
+ }, {
1497
+ time: e.vertices[1].time + i.time,
1498
+ price: e.vertices[1].price + i.price
1499
+ }]
1500
+ };
1501
+ }
1502
+ static resize(e, i, r) {
1503
+ return i === "start" ? {
1504
+ ...e,
1505
+ vertices: [r, e.vertices[1]]
1506
+ } : i === "end" ? {
1507
+ ...e,
1508
+ vertices: [e.vertices[0], r]
1509
+ } : e;
1510
+ }
1511
+ static drawText(e, i, r, s) {
1512
+ const n = r?.trim();
1513
+ n && (e.save(), e.beginPath(), e.rect(i.left, i.top, i.width, i.height), e.clip(), e.font = `${s.textFontSize}px ${s.textFontFamily}`, e.globalAlpha = s.textOpacity, e.fillStyle = s.textColor, e.textAlign = "center", e.textBaseline = "middle", e.fillText(n, i.left + i.width / 2, i.top + i.height / 2), e.restore());
1514
+ }
1515
+ }, wt = [
1516
+ "USDJPY",
1517
+ "GBPJPY",
1518
+ "AUDJPY",
1519
+ "EURJPY",
1520
+ "NZDJPY",
1521
+ "CADJPY"
1522
+ ], bt = ["BTCUSD", "ETHUSD"], xt = [
1523
+ "XAUUSD",
1524
+ "USOIL",
1525
+ "UKOIL",
1526
+ "XAGUSD"
1527
+ ], Tt = (t) => wt.some((e) => t.includes(e.slice(3))) ? 0.01 : bt.includes(t) ? 1 : xt.includes(t) ? 0.01 : 1e-4, Zt = (t, e) => wt.includes(t) ? e * 6.33806 : bt.includes(t) ? e * 1 : (xt.includes(t), e * 10), dt = (t, e, i, r, s) => {
1528
+ const n = s.split(".")[0], a = Tt(n), o = Zt(n, r);
1529
+ return (t === "buy" ? i - e : e - i) / a * o;
1530
+ }, L = class y {
1531
+ static defaultLongConfig = {
1532
+ defaultWidthCandles: 10,
1533
+ defaultStopLossPercent: 0.25,
1534
+ defaultRiskRewardRatio: 1.5,
1535
+ profitFillColor: "#00c853",
1536
+ profitFillOpacity: 0.18,
1537
+ lossFillColor: "#ff3b30",
1538
+ lossFillOpacity: 0.18,
1539
+ borderColor: "#808080",
1540
+ borderOpacity: 0.8,
1541
+ borderWidth: 1,
1542
+ borderStyle: "solid",
1543
+ midLineColor: "#808080",
1544
+ midLineOpacity: 0.9,
1545
+ midLineWidth: 1,
1546
+ midLineStyle: "dashed",
1547
+ handleColor: "#ffffff",
1548
+ handleBorderColor: "#2962ff",
1549
+ handleBorderThickness: 1.5,
1550
+ handleRadius: 5,
1551
+ hoverLineWidth: 2,
1552
+ selectedLineWidth: 2
1553
+ };
1554
+ static defaultShortConfig = { ...y.defaultLongConfig };
1555
+ static draw({ ctx: e, activeSymbol: i, shape: r, converter: s, config: n = r.type === "longPosition" ? y.defaultLongConfig : y.defaultShortConfig, selected: a = !1, hovered: o = !1 }) {
1556
+ const l = y.getGeometry(r, s);
1557
+ e.save(), e.fillStyle = P(n.profitFillColor, n.profitFillOpacity), e.fillRect(l.profitBox.left, l.profitBox.top, l.profitBox.width, l.profitBox.height), e.fillStyle = P(n.lossFillColor, n.lossFillOpacity), e.fillRect(l.lossBox.left, l.lossBox.top, l.lossBox.width, l.lossBox.height);
1558
+ const d = a ? Math.max(n.borderWidth, n.selectedLineWidth) : o ? Math.max(n.borderWidth, n.hoverLineWidth) : n.borderWidth;
1559
+ e.strokeStyle = P(n.borderColor, n.borderOpacity), e.lineWidth = d, e.setLineDash(E(n.borderStyle, d)), e.strokeRect(l.box.left, l.box.top, l.box.width, l.box.height), e.beginPath(), e.strokeStyle = P(n.midLineColor, n.midLineOpacity), e.lineWidth = n.midLineWidth, e.setLineDash(E(n.midLineStyle, n.midLineWidth)), e.moveTo(l.box.left, l.entryY), e.lineTo(l.box.right, l.entryY), e.stroke(), O(e), y.drawLabels({
1560
+ ctx: e,
1561
+ activeSymbol: i,
1562
+ shape: r,
1563
+ geometry: l,
1564
+ config: n
1565
+ }), (a || o) && V(e, y.getHandles(r, s, n), {
1566
+ fillColor: n.handleColor,
1567
+ borderColor: n.handleBorderColor,
1568
+ borderThickness: n.handleBorderThickness
1569
+ }), e.restore();
1570
+ }
1571
+ static createFromVertex({ id: e, type: i, entry: r, converter: s, config: n = i === "longPosition" ? y.defaultLongConfig : y.defaultShortConfig }) {
1572
+ const a = s.getTimeForX(s.getXForTime(r.time) + s.getCandleWidth() * n.defaultWidthCandles), o = n.defaultStopLossPercent;
1573
+ return {
1574
+ id: e,
1575
+ type: i,
1576
+ entry: r,
1577
+ endTime: a,
1578
+ stopLossPercent: o,
1579
+ takeProfitPercent: o * n.defaultRiskRewardRatio
1580
+ };
1581
+ }
1582
+ static getGeometry(e, i) {
1583
+ const r = v(e.entry, i), s = i.getXForTime(e.endTime), n = Math.min(r.x, s), a = Math.max(r.x, s), o = e.type === "longPosition", l = o ? e.takeProfitPercent : e.stopLossPercent, d = o ? e.stopLossPercent : e.takeProfitPercent, u = e.entry.price * (1 + l / 100), g = e.entry.price * (1 - d / 100), m = i.getYForPrice(u), w = i.getYForPrice(g), C = Math.min(m, w, r.y), x = Math.max(m, w, r.y), T = {
1584
+ left: n,
1585
+ top: C,
1586
+ right: a,
1587
+ bottom: r.y,
1588
+ width: a - n,
1589
+ height: Math.abs(r.y - C)
1590
+ }, H = {
1591
+ left: n,
1592
+ top: r.y,
1593
+ right: a,
1594
+ bottom: x,
1595
+ width: a - n,
1596
+ height: Math.abs(x - r.y)
1597
+ };
1598
+ return {
1599
+ entryPoint: r,
1600
+ endX: s,
1601
+ entryY: r.y,
1602
+ upperPrice: u,
1603
+ lowerPrice: g,
1604
+ box: {
1605
+ left: n,
1606
+ top: C,
1607
+ right: a,
1608
+ bottom: x,
1609
+ width: a - n,
1610
+ height: x - C
1611
+ },
1612
+ profitBox: o ? T : H,
1613
+ lossBox: o ? H : T
1614
+ };
1615
+ }
1616
+ static getHandles(e, i, r) {
1617
+ const s = r ?? (e.type === "longPosition" ? y.defaultLongConfig : y.defaultShortConfig), n = y.getGeometry(e, i);
1618
+ return [
1619
+ b({
1620
+ shapeId: e.id,
1621
+ type: "upperHeight",
1622
+ point: {
1623
+ x: n.box.left,
1624
+ y: n.box.top
1625
+ },
1626
+ radius: s.handleRadius,
1627
+ cursor: "ns-resize"
1628
+ }),
1629
+ b({
1630
+ shapeId: e.id,
1631
+ type: "lowerHeight",
1632
+ point: {
1633
+ x: n.box.left,
1634
+ y: n.box.bottom
1635
+ },
1636
+ radius: s.handleRadius,
1637
+ cursor: "ns-resize"
1638
+ }),
1639
+ b({
1640
+ shapeId: e.id,
1641
+ type: "start",
1642
+ point: {
1643
+ x: n.entryPoint.x,
1644
+ y: n.entryY
1645
+ },
1646
+ radius: s.handleRadius,
1647
+ cursor: "ns-resize"
1648
+ }),
1649
+ b({
1650
+ shapeId: e.id,
1651
+ type: "end",
1652
+ point: {
1653
+ x: n.endX,
1654
+ y: n.entryY
1655
+ },
1656
+ radius: s.handleRadius,
1657
+ cursor: "ew-resize"
1658
+ })
1659
+ ];
1660
+ }
1661
+ static hitTest({ point: e, shape: i, converter: r, config: s }) {
1662
+ return y.getHandles(i, r, s).some((n) => J(e, n)) ? !0 : vt(e, y.getGeometry(i, r).box);
1663
+ }
1664
+ static move(e, i) {
1665
+ return {
1666
+ ...e,
1667
+ entry: {
1668
+ time: e.entry.time + i.time,
1669
+ price: e.entry.price + i.price
1670
+ },
1671
+ endTime: e.endTime + i.time
1672
+ };
1673
+ }
1674
+ static resize({ shape: e, handleType: i, vertex: r, converter: s }) {
1675
+ return i === "start" ? {
1676
+ ...e,
1677
+ entry: {
1678
+ time: e.entry.time,
1679
+ price: r.price
1680
+ }
1681
+ } : i === "end" ? {
1682
+ ...e,
1683
+ endTime: r.time
1684
+ } : i === "upperHeight" ? y.resizeUpperHeight(e, r.price) : i === "lowerHeight" ? y.resizeLowerHeight(e, r.price) : {
1685
+ ...e,
1686
+ endTime: s.getTimeForX(s.getXForTime(e.endTime))
1687
+ };
1688
+ }
1689
+ static resizeUpperHeight(e, i) {
1690
+ const r = Math.max(0, (i - e.entry.price) / e.entry.price * 100);
1691
+ return e.type === "longPosition" ? {
1692
+ ...e,
1693
+ takeProfitPercent: r
1694
+ } : {
1695
+ ...e,
1696
+ stopLossPercent: r
1697
+ };
1698
+ }
1699
+ static resizeLowerHeight(e, i) {
1700
+ const r = Math.max(0, (e.entry.price - i) / e.entry.price * 100);
1701
+ return e.type === "longPosition" ? {
1702
+ ...e,
1703
+ stopLossPercent: r
1704
+ } : {
1705
+ ...e,
1706
+ takeProfitPercent: r
1707
+ };
1708
+ }
1709
+ static drawLabels({ ctx: e, activeSymbol: i, shape: r, geometry: s, config: n }) {
1710
+ e.save(), e.font = "11px sans-serif", e.textBaseline = "middle";
1711
+ const a = r.type, o = Tt(i), l = r.entry.price * (r.takeProfitPercent / 100) / o, d = r.entry.price * (r.stopLossPercent / 100) / o, u = s.box.left + 6, g = l / d;
1712
+ e.fillStyle = P(n.profitFillColor, 0.95);
1713
+ const m = a === "longPosition" ? s.profitBox.top + s.profitBox.height / 10 : s.profitBox.bottom - s.profitBox.height / 10;
1714
+ e.fillText(`${l.toFixed(2)} (${g.toFixed(2)})`, u, m), e.fillStyle = P(n.lossFillColor, 0.95);
1715
+ const w = a === "shortPosition" ? s.lossBox.top + s.lossBox.height / 10 : s.lossBox.bottom - s.lossBox.height / 10;
1716
+ e.fillText(d.toFixed(1), u, w);
1717
+ }
1718
+ }, qt = class {
1719
+ canvas;
1720
+ ctx;
1721
+ activeSymbol;
1722
+ candles;
1723
+ viewport = null;
1724
+ shapes;
1725
+ activeTool;
1726
+ config;
1727
+ selectedShapeId = null;
1728
+ hoveredShapeId = null;
1729
+ handleHitboxes = [];
1730
+ draft = null;
1731
+ dragState = null;
1732
+ onShapeAdded;
1733
+ onShapeModified;
1734
+ onShapeSelected;
1735
+ onToolChange;
1736
+ constructor(t) {
1737
+ this.canvas = t.canvas;
1738
+ const e = this.canvas.getContext("2d");
1739
+ if (!e) throw new Error("Failed to get 2d context for ShapesLayer");
1740
+ this.ctx = e, this.activeSymbol = t.activeSymbol, this.candles = t.candles, this.shapes = t.shapes, this.activeTool = t.activeTool ?? null, this.config = this.mergeConfig(t.config), this.onShapeAdded = t.onShapeAdded, this.onShapeModified = t.onShapeModified, this.onShapeSelected = t.onShapeSelected, this.onToolChange = t.onToolChange;
1741
+ }
1742
+ setCandles(t) {
1743
+ this.candles = t;
1744
+ }
1745
+ setViewport(t) {
1746
+ this.viewport = t;
1747
+ }
1748
+ setShapes(t) {
1749
+ this.shapes = t, this.selectedShapeId && !this.shapes.some((e) => e.id === this.selectedShapeId) && this.setSelectedShapeId(null), this.rebuildHandleHitboxes();
1750
+ }
1751
+ setConfig(t) {
1752
+ this.config = this.mergeConfig(t);
1753
+ }
1754
+ setActiveTool(t) {
1755
+ this.activeTool = t, t || (this.draft = null), this.canvas.style.cursor = "crosshair";
1756
+ }
1757
+ clearHoverState() {
1758
+ this.hoveredShapeId = null, this.canvas.style.cursor = "crosshair";
1759
+ }
1760
+ render() {
1761
+ if (this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), !this.viewport) return;
1762
+ const t = this.getConverter();
1763
+ for (const e of this.shapes) this.drawShape({
1764
+ shape: e,
1765
+ converter: t,
1766
+ selected: e.id === this.selectedShapeId,
1767
+ hovered: e.id === this.hoveredShapeId
1768
+ });
1769
+ this.drawDraft(t), this.rebuildHandleHitboxes();
1770
+ }
1771
+ handlePointerEvent(t) {
1772
+ return !this.viewport || this.candles.length === 0 ? !1 : t.type === "contextmenu" ? this.handleContextMenu() : t.type === "pointerdown" || t.type === "mousedown" ? this.handlePointerDown(t) : t.type === "pointermove" || t.type === "mousemove" ? this.handlePointerMove(t) : t.type === "pointerup" || t.type === "mouseup" ? this.handlePointerUp() : !1;
1773
+ }
1774
+ handleKeyboardEvent(t) {
1775
+ return t.key === "Escape" && this.draft?.type === "path" ? (this.completePathDraft(), !0) : t.key === "Escape" && this.draft ? (this.draft = null, this.setActiveToolAndEmit(null), !0) : t.key === "Escape" && this.selectedShapeId ? (this.setSelectedShapeId(null), !0) : !1;
1776
+ }
1777
+ handlePointerDown(t) {
1778
+ const e = at(this.canvas, t), i = ot(e, this.getConverter());
1779
+ if (this.activeTool)
1780
+ return ht(t) && this.handleToolPointerDown({
1781
+ point: e,
1782
+ vertex: i
1783
+ }), !0;
1784
+ if (!ht(t)) return !1;
1785
+ const r = this.findHandleAtPoint(e);
1786
+ if (r) {
1787
+ const n = this.shapes.find((a) => a.id === r.shapeId);
1788
+ return n ? (this.setSelectedShapeId(n.id), this.dragState = {
1789
+ edit: {
1790
+ mode: "resize",
1791
+ shape: n,
1792
+ handleType: r.type,
1793
+ handleIndex: r.index,
1794
+ startPoint: e,
1795
+ startVertex: i
1796
+ },
1797
+ lastModifiedShape: n
1798
+ }, this.canvas.style.cursor = r.cursor ?? "grab", !0) : !1;
1799
+ }
1800
+ const s = this.findShapeAtPoint(e);
1801
+ if (s) {
1802
+ const n = this.findHandleAtPointForShape(s, e);
1803
+ return n ? (this.setSelectedShapeId(s.id), this.dragState = {
1804
+ edit: {
1805
+ mode: "resize",
1806
+ shape: s,
1807
+ handleType: n.type,
1808
+ handleIndex: n.index,
1809
+ startPoint: e,
1810
+ startVertex: i
1811
+ },
1812
+ lastModifiedShape: s
1813
+ }, this.canvas.style.cursor = n.cursor ?? "grab", !0) : (this.setSelectedShapeId(s.id), this.hoveredShapeId = s.id, this.dragState = {
1814
+ edit: {
1815
+ mode: "move",
1816
+ shape: s,
1817
+ startPoint: e,
1818
+ startVertex: i
1819
+ },
1820
+ lastModifiedShape: s
1821
+ }, this.canvas.style.cursor = "move", !0);
1822
+ }
1823
+ return this.setSelectedShapeId(null), this.hoveredShapeId = null, this.canvas.style.cursor = "crosshair", !1;
1824
+ }
1825
+ handlePointerMove(t) {
1826
+ const e = at(this.canvas, t), i = ot(e, this.getConverter());
1827
+ if (this.draft)
1828
+ return this.updateDraftPreview(i), !0;
1829
+ if (this.dragState) {
1830
+ const n = this.applyDrag(i);
1831
+ return this.dragState.lastModifiedShape = n, this.emitShapeModified(n), !0;
1832
+ }
1833
+ if (this.activeTool)
1834
+ return this.canvas.style.cursor = "crosshair", !0;
1835
+ const r = this.findHandleAtPoint(e);
1836
+ if (r)
1837
+ return this.canvas.style.cursor = "pointer", this.hoveredShapeId = r.shapeId, !0;
1838
+ const s = this.findShapeAtPoint(e);
1839
+ return s ? (this.hoveredShapeId = s.id, this.canvas.style.cursor = "pointer", !0) : this.hoveredShapeId !== null ? (this.hoveredShapeId = null, this.canvas.style.cursor = "crosshair", !0) : !1;
1840
+ }
1841
+ handlePointerUp() {
1842
+ if (!this.dragState) return !1;
1843
+ const t = this.dragState.lastModifiedShape;
1844
+ return this.dragState = null, this.setSelectedShapeId(t.id), this.emitShapeModified(t), !0;
1845
+ }
1846
+ handleContextMenu() {
1847
+ return this.draft?.type === "path" ? (this.completePathDraft(), !0) : this.activeTool !== null;
1848
+ }
1849
+ handleToolPointerDown({ vertex: t }) {
1850
+ if (this.activeTool) {
1851
+ if (this.activeTool === "trendline") {
1852
+ this.handleTwoPointDraft("trendline", t);
1853
+ return;
1854
+ }
1855
+ if (this.activeTool === "rectangle") {
1856
+ this.handleTwoPointDraft("rectangle", t);
1857
+ return;
1858
+ }
1859
+ if (this.activeTool === "fibRetracement") {
1860
+ this.handleTwoPointDraft("fibRetracement", t);
1861
+ return;
1862
+ }
1863
+ if (this.activeTool === "path") {
1864
+ this.handlePathDraft(t);
1865
+ return;
1866
+ }
1867
+ (this.activeTool === "shortPosition" || this.activeTool === "longPosition") && this.addPositionShape(this.activeTool, t);
1868
+ }
1869
+ }
1870
+ handleTwoPointDraft(t, e) {
1871
+ if (!this.draft || this.draft.type !== t) {
1872
+ this.draft = {
1873
+ type: t,
1874
+ id: U(),
1875
+ vertices: [e, e]
1876
+ };
1877
+ return;
1878
+ }
1879
+ const i = {
1880
+ id: this.draft.id,
1881
+ type: t,
1882
+ vertices: [this.draft.vertices[0], e]
1883
+ };
1884
+ this.draft = null, this.addShape(i), this.setActiveToolAndEmit(null);
1885
+ }
1886
+ handlePathDraft(t) {
1887
+ if (!this.draft || this.draft.type !== "path") {
1888
+ this.draft = {
1889
+ type: "path",
1890
+ id: U(),
1891
+ vertices: [t],
1892
+ previewVertex: null
1893
+ };
1894
+ return;
1895
+ }
1896
+ this.draft = {
1897
+ ...this.draft,
1898
+ vertices: [...this.draft.vertices, t],
1899
+ previewVertex: null
1900
+ };
1901
+ }
1902
+ completePathDraft() {
1903
+ !this.draft || this.draft.type !== "path" || (this.draft.vertices.length >= 2 && this.addShape({
1904
+ id: this.draft.id,
1905
+ type: "path",
1906
+ vertices: this.draft.vertices
1907
+ }), this.draft = null, this.setActiveToolAndEmit(null));
1908
+ }
1909
+ addPositionShape(t, e) {
1910
+ const i = this.getConverter(), r = t === "longPosition" ? this.config.longPosition : this.config.shortPosition, s = L.createFromVertex({
1911
+ id: U(),
1912
+ type: t,
1913
+ entry: e,
1914
+ converter: i,
1915
+ config: r
1916
+ });
1917
+ this.addShape(s), this.setActiveToolAndEmit(null);
1918
+ }
1919
+ updateDraftPreview(t) {
1920
+ if (this.draft) {
1921
+ if (this.draft.type === "trendline" || this.draft.type === "rectangle" || this.draft.type === "fibRetracement") {
1922
+ this.draft = {
1923
+ ...this.draft,
1924
+ vertices: [this.draft.vertices[0], t]
1925
+ };
1926
+ return;
1927
+ }
1928
+ this.draft.type === "path" && (this.draft = {
1929
+ ...this.draft,
1930
+ previewVertex: t
1931
+ });
1932
+ }
1933
+ }
1934
+ addShape(t) {
1935
+ this.setSelectedShapeId(t.id), this.onShapeAdded?.({
1936
+ type: t.type,
1937
+ id: t.id,
1938
+ shape: t
1939
+ });
1940
+ }
1941
+ emitShapeModified(t) {
1942
+ this.onShapeModified?.({
1943
+ id: t.id,
1944
+ type: t.type,
1945
+ shape: t
1946
+ });
1947
+ }
1948
+ applyDrag(t) {
1949
+ if (!this.dragState) throw new Error("Cannot apply shape drag without active drag state");
1950
+ const { edit: e } = this.dragState;
1951
+ if (e.mode === "move") {
1952
+ const i = {
1953
+ time: t.time - e.startVertex.time,
1954
+ price: t.price - e.startVertex.price
1955
+ };
1956
+ return this.moveShape(e.shape, i);
1957
+ }
1958
+ return this.resizeShape(e.shape, e.handleType, t, e.handleIndex);
1959
+ }
1960
+ moveShape(t, e) {
1961
+ return t.type === "trendline" ? k.move(t, e) : t.type === "rectangle" ? R.move(t, e) : t.type === "path" ? M.move(t, e) : t.type === "fibRetracement" ? S.move(t, e) : L.move(t, e);
1962
+ }
1963
+ resizeShape(t, e, i, r) {
1964
+ return t.type === "trendline" ? k.resize(t, e, i) : t.type === "rectangle" ? R.resize(t, e, i) : t.type === "fibRetracement" ? S.resize(t, e, i) : t.type === "path" ? M.resize(t, r ?? -1, i) : L.resize({
1965
+ shape: t,
1966
+ handleType: e,
1967
+ vertex: i,
1968
+ converter: this.getConverter()
1969
+ });
1970
+ }
1971
+ drawShape({ shape: t, converter: e, selected: i, hovered: r }) {
1972
+ if (t.type === "trendline") {
1973
+ const n = this.getTrendlineConfig(t);
1974
+ k.draw({
1975
+ ctx: this.ctx,
1976
+ shape: t,
1977
+ converter: e,
1978
+ config: n,
1979
+ selected: i,
1980
+ hovered: r
1981
+ });
1982
+ return;
1983
+ }
1984
+ if (t.type === "rectangle") {
1985
+ const n = this.getRectangleConfig(t);
1986
+ R.draw({
1987
+ ctx: this.ctx,
1988
+ shape: t,
1989
+ converter: e,
1990
+ config: n,
1991
+ selected: i,
1992
+ hovered: r
1993
+ });
1994
+ return;
1995
+ }
1996
+ if (t.type === "path") {
1997
+ const n = this.getPathConfig(t);
1998
+ M.draw({
1999
+ ctx: this.ctx,
2000
+ shape: t,
2001
+ converter: e,
2002
+ config: n,
2003
+ selected: i,
2004
+ hovered: r
2005
+ });
2006
+ return;
2007
+ }
2008
+ if (t.type === "fibRetracement") {
2009
+ const n = this.getFibRetracementConfig(t);
2010
+ S.draw({
2011
+ ctx: this.ctx,
2012
+ shape: t,
2013
+ converter: e,
2014
+ config: n,
2015
+ selected: i,
2016
+ hovered: r
2017
+ });
2018
+ return;
2019
+ }
2020
+ const s = this.getPositionConfig(t);
2021
+ L.draw({
2022
+ ctx: this.ctx,
2023
+ activeSymbol: this.activeSymbol,
2024
+ shape: t,
2025
+ converter: e,
2026
+ config: s,
2027
+ selected: i,
2028
+ hovered: r
2029
+ });
2030
+ }
2031
+ drawDraft(t) {
2032
+ if (this.draft) {
2033
+ if (this.draft.type === "trendline") {
2034
+ k.drawDraft({
2035
+ ctx: this.ctx,
2036
+ vertices: this.draft.vertices,
2037
+ converter: t,
2038
+ config: this.config.trendline
2039
+ });
2040
+ return;
2041
+ }
2042
+ if (this.draft.type === "rectangle") {
2043
+ R.drawDraft({
2044
+ ctx: this.ctx,
2045
+ vertices: this.draft.vertices,
2046
+ converter: t,
2047
+ config: this.config.rectangle
2048
+ });
2049
+ return;
2050
+ }
2051
+ if (this.draft.type === "fibRetracement") {
2052
+ S.drawDraft({
2053
+ ctx: this.ctx,
2054
+ vertices: this.draft.vertices,
2055
+ converter: t,
2056
+ config: this.config.fibRetracement
2057
+ });
2058
+ return;
2059
+ }
2060
+ this.draft.type === "path" && M.drawDraft({
2061
+ ctx: this.ctx,
2062
+ vertices: this.draft.vertices,
2063
+ previewVertex: this.draft.previewVertex,
2064
+ converter: t,
2065
+ config: this.config.path
2066
+ });
2067
+ }
2068
+ }
2069
+ rebuildHandleHitboxes() {
2070
+ if (!this.viewport) {
2071
+ this.handleHitboxes = [];
2072
+ return;
2073
+ }
2074
+ const t = this.getConverter(), e = [], i = this.shapes.find((s) => s.id === this.selectedShapeId), r = this.hoveredShapeId && this.hoveredShapeId !== this.selectedShapeId ? this.shapes.find((s) => s.id === this.hoveredShapeId) ?? null : null;
2075
+ i && e.push(i), r && e.push(r), this.handleHitboxes = e.flatMap((s) => this.getShapeHandles(s, t));
2076
+ }
2077
+ getShapeHandles(t, e) {
2078
+ return t.type === "trendline" ? k.getHandles(t, e, this.getTrendlineConfig(t)) : t.type === "rectangle" ? R.getHandles(t, e, this.getRectangleConfig(t)) : t.type === "path" ? M.getHandles(t, e, this.getPathConfig(t)) : t.type === "fibRetracement" ? S.getHandles(t, e, this.getFibRetracementConfig(t)) : L.getHandles(t, e, this.getPositionConfig(t));
2079
+ }
2080
+ findHandleAtPoint(t) {
2081
+ for (let e = this.handleHitboxes.length - 1; e >= 0; e -= 1) {
2082
+ const i = this.handleHitboxes[e];
2083
+ if (J(t, i)) return i;
2084
+ }
2085
+ return null;
2086
+ }
2087
+ findHandleAtPointForShape(t, e) {
2088
+ const i = this.getConverter(), r = this.getShapeHandles(t, i);
2089
+ for (const s of r) if (J(e, s)) return s;
2090
+ return null;
2091
+ }
2092
+ findShapeAtPoint(t) {
2093
+ const e = this.getConverter();
2094
+ for (let i = this.shapes.length - 1; i >= 0; i -= 1) {
2095
+ const r = this.shapes[i];
2096
+ if (this.isShapeHit(r, t, e)) return r;
2097
+ }
2098
+ return null;
2099
+ }
2100
+ isShapeHit(t, e, i) {
2101
+ return t.type === "trendline" ? k.hitTest({
2102
+ point: e,
2103
+ shape: t,
2104
+ converter: i
2105
+ }) : t.type === "rectangle" ? R.hitTest({
2106
+ point: e,
2107
+ shape: t,
2108
+ converter: i
2109
+ }) : t.type === "path" ? M.hitTest({
2110
+ point: e,
2111
+ shape: t,
2112
+ converter: i
2113
+ }) : t.type === "fibRetracement" ? S.hitTest({
2114
+ point: e,
2115
+ shape: t,
2116
+ converter: i,
2117
+ config: this.getFibRetracementConfig(t)
2118
+ }) : L.hitTest({
2119
+ point: e,
2120
+ shape: t,
2121
+ converter: i,
2122
+ config: this.getPositionConfig(t)
2123
+ });
2124
+ }
2125
+ setSelectedShapeId(t) {
2126
+ if (this.selectedShapeId === t) return;
2127
+ if (this.selectedShapeId = t, !t) {
2128
+ this.onShapeSelected?.(null);
2129
+ return;
2130
+ }
2131
+ const e = this.shapes.find((i) => i.id === t);
2132
+ if (!e) {
2133
+ this.onShapeSelected?.(null);
2134
+ return;
2135
+ }
2136
+ this.onShapeSelected?.(this.createShapeSelectedPayload(e));
2137
+ }
2138
+ createShapeSelectedPayload(t) {
2139
+ return t.type === "trendline" ? {
2140
+ shapeId: t.id,
2141
+ type: t.type,
2142
+ config: this.getTrendlineConfig(t)
2143
+ } : t.type === "rectangle" ? {
2144
+ shapeId: t.id,
2145
+ type: t.type,
2146
+ config: this.getRectangleConfig(t)
2147
+ } : t.type === "path" ? {
2148
+ shapeId: t.id,
2149
+ type: t.type,
2150
+ config: this.getPathConfig(t)
2151
+ } : t.type === "fibRetracement" ? {
2152
+ shapeId: t.id,
2153
+ type: t.type,
2154
+ config: this.getFibRetracementConfig(t)
2155
+ } : {
2156
+ shapeId: t.id,
2157
+ type: t.type,
2158
+ config: this.getPositionConfig(t)
2159
+ };
2160
+ }
2161
+ getTrendlineConfig(t) {
2162
+ return {
2163
+ ...this.config.trendline,
2164
+ ...t.config ?? {}
2165
+ };
2166
+ }
2167
+ getRectangleConfig(t) {
2168
+ return {
2169
+ ...this.config.rectangle,
2170
+ ...t.config ?? {}
2171
+ };
2172
+ }
2173
+ getPathConfig(t) {
2174
+ return {
2175
+ ...this.config.path,
2176
+ ...t.config ?? {}
2177
+ };
2178
+ }
2179
+ getFibRetracementConfig(t) {
2180
+ return {
2181
+ ...this.config.fibRetracement,
2182
+ ...t.config ?? {},
2183
+ levels: t.config?.levels ?? this.config.fibRetracement.levels
2184
+ };
2185
+ }
2186
+ getPositionConfig(t) {
2187
+ return {
2188
+ ...t.type === "longPosition" ? this.config.longPosition : this.config.shortPosition,
2189
+ ...t.config ?? {}
2190
+ };
2191
+ }
2192
+ setActiveToolAndEmit(t) {
2193
+ this.setActiveTool(t), this.onToolChange?.(t);
2194
+ }
2195
+ getConverter() {
2196
+ const t = this.viewport;
2197
+ return {
2198
+ candles: this.candles,
2199
+ viewport: t,
2200
+ getXForTime: (e) => this.getXForTime(e),
2201
+ getYForPrice: (e) => this.getYForPrice(e),
2202
+ getTimeForX: (e) => this.getTimeForX(e),
2203
+ getPriceForY: (e) => this.getPriceForY(e),
2204
+ getNearestCandleTimeForX: (e) => {
2205
+ const i = this.getTimeForX(e);
2206
+ if (this.candles.length > 0 && i > this.candles[this.candles.length - 1].time) return i;
2207
+ const r = j(this.candles, i);
2208
+ return r >= 0 ? this.candles[r].time : i;
2209
+ },
2210
+ getCandleWidth: () => t ? this.getCandleStepWidth(t) : 12
2211
+ };
2212
+ }
2213
+ getXForTime(t) {
2214
+ if (!this.viewport || this.candles.length === 0) return 0;
2215
+ if (t > this.candles[this.candles.length - 1].time) {
2216
+ const i = Pt(this.candles);
2217
+ return (t - this.candles[0].time) / i * this.viewport.candleSpacing + this.viewport.offsetX + this.viewport.candleWidth / 2;
2218
+ }
2219
+ const e = j(this.candles, t);
2220
+ return e < 0 ? 0 : e * this.viewport.candleSpacing + this.viewport.offsetX + this.viewport.candleWidth / 2;
2221
+ }
2222
+ getTimeForX(t) {
2223
+ if (!this.viewport) return Date.now();
2224
+ const e = Math.round((t - this.viewport.offsetX - this.viewport.candleWidth / 2) / this.viewport.candleSpacing);
2225
+ return e >= 0 && e < this.candles.length ? this.candles[e].time : this.candles.length === 0 ? Date.now() : Kt(this.candles, this.candles[0].time, e);
2226
+ }
2227
+ getYForPrice(t) {
2228
+ return this.viewport ? Y({
2229
+ price: t,
2230
+ minPrice: this.viewport.minPrice,
2231
+ priceRange: this.viewport.priceRange,
2232
+ chartHeight: this.canvas.height
2233
+ }) : this.canvas.height / 2;
2234
+ }
2235
+ getPriceForY(t) {
2236
+ return this.viewport ? ft({
2237
+ y: t,
2238
+ minPrice: this.viewport.minPrice,
2239
+ priceRange: this.viewport.priceRange,
2240
+ chartHeight: this.canvas.height
2241
+ }) : 0;
2242
+ }
2243
+ getCandleStepWidth(t) {
2244
+ return t.candleSpacing;
2245
+ }
2246
+ mergeConfig(t) {
2247
+ return {
2248
+ trendline: {
2249
+ ...k.defaultConfig,
2250
+ ...t?.trendline ?? {}
2251
+ },
2252
+ rectangle: {
2253
+ ...R.defaultConfig,
2254
+ ...t?.rectangle ?? {}
2255
+ },
2256
+ path: {
2257
+ ...M.defaultConfig,
2258
+ ...t?.path ?? {}
2259
+ },
2260
+ fibRetracement: {
2261
+ ...S.defaultConfig,
2262
+ ...t?.fibRetracement ?? {},
2263
+ levels: t?.fibRetracement?.levels ?? S.defaultConfig.levels
2264
+ },
2265
+ shortPosition: {
2266
+ ...L.defaultShortConfig,
2267
+ ...t?.shortPosition ?? {}
2268
+ },
2269
+ longPosition: {
2270
+ ...L.defaultLongConfig,
2271
+ ...t?.longPosition ?? {}
2272
+ }
2273
+ };
2274
+ }
2275
+ }, Qt = class {
2276
+ #t;
2277
+ #e;
2278
+ isDragging;
2279
+ trades = [];
2280
+ pastTrades = [];
2281
+ candles = [];
2282
+ viewport = null;
2283
+ liveCandle = null;
2284
+ handleHitboxes = [];
2285
+ temporaryProtectionDrag = null;
2286
+ constructor(t) {
2287
+ this.#t = t.canvas, this.isDragging = !1;
2288
+ const e = this.#t.getContext("2d");
2289
+ if (!e) throw new Error("Canvas 2D context not supported");
2290
+ this.#e = e;
2291
+ }
2292
+ setTrades(t) {
2293
+ this.trades = t;
2294
+ }
2295
+ setPastTrades(t) {
2296
+ this.pastTrades = t;
2297
+ }
2298
+ setCandles(t) {
2299
+ this.candles = t;
2300
+ }
2301
+ setViewport(t) {
2302
+ this.viewport = t;
2303
+ }
2304
+ setLiveCandle(t) {
2305
+ this.liveCandle = t;
2306
+ }
2307
+ setIsDragging(t) {
2308
+ this.isDragging = t;
2309
+ }
2310
+ setTemporaryProtectionDrag(t) {
2311
+ this.temporaryProtectionDrag = t;
2312
+ }
2313
+ clearTemporaryProtectionDrag() {
2314
+ this.temporaryProtectionDrag = null;
2315
+ }
2316
+ renderLiveFeed() {
2317
+ this.isDragging || this.render();
2318
+ }
2319
+ render() {
2320
+ this.clearCanvas(), this.resetHitboxes(), this.drawPastTradeIndicators();
2321
+ for (const t of this.trades) this.drawTrade({ trade: t });
2322
+ this.drawTemporaryProtectionDrag();
2323
+ }
2324
+ createHitBox = ({ price: t, trade: e, type: i, x: r, y: s, width: n, height: a, viewport: o }) => {
2325
+ const { showCloseSection: l, widthClose: d } = this.getHandleStyleConfig(i).handle, u = l ? d : 0, g = n - u, m = r + n - u;
2326
+ return {
2327
+ kind: "existingHandle",
2328
+ price: p(t),
2329
+ trade: e,
2330
+ type: i,
2331
+ x: r,
2332
+ y: s,
2333
+ width: n,
2334
+ height: a,
2335
+ viewport: o,
2336
+ labelArea: {
2337
+ x: r,
2338
+ y: s,
2339
+ width: g,
2340
+ height: a
2341
+ },
2342
+ closeButtonArea: {
2343
+ x: m,
2344
+ y: s,
2345
+ width: u,
2346
+ height: a
2347
+ }
2348
+ };
2349
+ };
2350
+ createMissingProtectionHitBox = ({ price: t, trade: e, type: i, x: r, y: s, width: n, height: a, viewport: o }) => ({
2351
+ kind: "missingProtectionHandle",
2352
+ price: p(t),
2353
+ trade: e,
2354
+ type: i,
2355
+ x: r,
2356
+ y: s,
2357
+ width: n,
2358
+ height: a,
2359
+ viewport: o,
2360
+ labelArea: {
2361
+ x: r,
2362
+ y: s,
2363
+ width: n,
2364
+ height: a
2365
+ },
2366
+ closeButtonArea: {
2367
+ x: r + n,
2368
+ y: s,
2369
+ width: 0,
2370
+ height: a
2371
+ }
2372
+ });
2373
+ drawTrade({ trade: t }) {
2374
+ const e = this.getTradeHandleRenderState({
2375
+ price: t.openPrice,
2376
+ type: "startPrice",
2377
+ trade: t
2378
+ });
2379
+ if (!e) return;
2380
+ const i = this.getMissingProtectionRenderStates({
2381
+ trade: t,
2382
+ startPriceRenderState: e
2383
+ });
2384
+ this.drawTradeHandleFromRenderState({
2385
+ renderState: e,
2386
+ blockedRanges: this.getLineBlockedRangesFromMissingProtectionHandles(i)
2387
+ }), t.sl && this.drawTradeHandle({
2388
+ price: t.sl,
2389
+ type: "stopLoss",
2390
+ trade: t
2391
+ }), t.tp && this.drawTradeHandle({
2392
+ price: t.tp,
2393
+ type: "takeProfit",
2394
+ trade: t
2395
+ });
2396
+ for (const r of i)
2397
+ this.addMissingProtectionHitbox(r), this.drawMissingProtectionHandleRect({
2398
+ x: r.handleX,
2399
+ y: r.handleY,
2400
+ width: r.handleWidth,
2401
+ config: r.config
2402
+ }), this.drawMissingProtectionHandleLabel(r);
2403
+ }
2404
+ drawTradeHandle({ price: t, type: e, trade: i }) {
2405
+ const r = this.getTradeHandleRenderState({
2406
+ price: t,
2407
+ type: e,
2408
+ trade: i
2409
+ });
2410
+ return r ? (this.drawTradeHandleFromRenderState({ renderState: r }), r) : null;
2411
+ }
2412
+ drawTradeHandleFromRenderState({ renderState: t, blockedRanges: e = [] }) {
2413
+ this.addHandleHitbox(t), this.drawHandleHorizontalLines({
2414
+ ...t,
2415
+ blockedRanges: e
2416
+ }), this.drawHandleRect({
2417
+ x: t.handleX,
2418
+ y: t.handleY,
2419
+ width: t.handleWidth,
2420
+ config: t.handleConfig
2421
+ }), this.drawHandleSections({
2422
+ handleType: t.type,
2423
+ handleX: t.handleX,
2424
+ handleY: t.handleY,
2425
+ handleWidth: t.handleWidth,
2426
+ handleHeight: t.handleHeight,
2427
+ handleConfig: t.handleConfig,
2428
+ trade: t.trade,
2429
+ y: t.y
2430
+ });
2431
+ }
2432
+ drawHandleLine({ x1: t, x2: e, y: i, config: r }) {
2433
+ if (e <= t) return;
2434
+ const s = this.#e;
2435
+ s.save(), s.globalAlpha = r.opacity, s.strokeStyle = r.color, s.lineWidth = r.width, s.setLineDash(this.getHandleLineDash(r)), s.beginPath(), s.moveTo(t, i), s.lineTo(e, i), s.stroke(), s.restore();
2436
+ }
2437
+ drawConnectorLine({ x1: t, y1: e, x2: i, y2: r, config: s }) {
2438
+ const n = this.#e;
2439
+ n.save(), n.globalAlpha = s.opacity, n.strokeStyle = s.color, n.lineWidth = s.width, n.setLineDash(this.getHandleLineDash(s)), n.beginPath(), n.moveTo(t, e), n.lineTo(i, r), n.stroke(), n.restore();
2440
+ }
2441
+ drawHandleRect({ x: t, y: e, width: i, config: r }) {
2442
+ const s = this.#e;
2443
+ this.clearRectArea({
2444
+ x: t,
2445
+ y: e,
2446
+ width: i,
2447
+ height: r.height,
2448
+ padding: Math.max(1, r.borderWidth)
2449
+ }), s.save(), s.globalAlpha = r.backgroundOpacity, s.fillStyle = r.backgroundColor, s.fillRect(t, e, i, r.height), s.globalAlpha = r.borderOpacity, s.strokeStyle = r.borderColor, s.lineWidth = r.borderWidth, s.strokeRect(t, e, i, r.height), s.restore();
2450
+ }
2451
+ drawHandleSections({ handleType: t, handleX: e, handleY: i, handleWidth: r, handleHeight: s, handleConfig: n, trade: a, y: o }) {
2452
+ const l = this.#e, d = c.tradeHandles, u = this.getHandleSections({
2453
+ handleType: t,
2454
+ handleConfig: n,
2455
+ trade: a
2456
+ });
2457
+ let g = e;
2458
+ l.save(), l.font = d.font, l.textBaseline = "middle", l.globalAlpha = d.textOpacity;
2459
+ for (const m of u)
2460
+ m.visible && (this.drawHandleSectionLabel({
2461
+ section: m,
2462
+ currentX: g,
2463
+ y: o
2464
+ }), g += m.width, g < e + r && this.drawHandleSectionDivider({
2465
+ x: g,
2466
+ y1: i + 4,
2467
+ y2: i + s - 4,
2468
+ config: n
2469
+ }));
2470
+ l.restore();
2471
+ }
2472
+ getHandlePnLLabel({ handleType: t, trade: e }) {
2473
+ if (t === "startPrice") return this.formatPnlLabel(e.pnl);
2474
+ const i = t === "stopLoss" ? e.sl : e.tp;
2475
+ if (!i) return "N/A";
2476
+ const r = dt(e.type, e.openPrice, i, e.volume, e.symbol);
2477
+ return this.formatPnlLabel(r);
2478
+ }
2479
+ getHandleWidth(t) {
2480
+ let e = 0;
2481
+ return t.showVolumeSection && (e += t.widthVolume), t.showPnlSection && (e += t.widthPNL), t.showCloseSection && (e += t.widthClose), e;
2482
+ }
2483
+ getHandleX({ handleWidth: t, config: e }) {
2484
+ return this.getPositionedHandleX({
2485
+ handleWidth: t,
2486
+ position: e.position
2487
+ });
2488
+ }
2489
+ getTemporaryProtectionHandleX({ handleWidth: t, config: e }) {
2490
+ return this.getPositionedHandleX({
2491
+ handleWidth: t,
2492
+ position: e.position
2493
+ });
2494
+ }
2495
+ getHandleStyleConfig(t) {
2496
+ switch (t) {
2497
+ case "startPrice":
2498
+ return c.tradeHandles.startPriceHandle;
2499
+ case "stopLoss":
2500
+ return c.tradeHandles.slHandle;
2501
+ case "takeProfit":
2502
+ return c.tradeHandles.tpHandle;
2503
+ }
2504
+ }
2505
+ getProtectionDirection({ trade: t, type: e }) {
2506
+ const i = this.isBuyTrade(t), r = e === "takeProfit" ? 1 : -1;
2507
+ return i ? r : -r;
2508
+ }
2509
+ constrainProtectionPrice({ price: t, trade: e, type: i }) {
2510
+ return this.getProtectionDirection({
2511
+ trade: e,
2512
+ type: i
2513
+ }) > 0 ? p(Math.max(t, e.openPrice)) : p(Math.min(t, e.openPrice));
2514
+ }
2515
+ clearCanvas() {
2516
+ this.#e.clearRect(0, 0, this.#t.width, this.#t.height);
2517
+ }
2518
+ resetHitboxes() {
2519
+ this.handleHitboxes = [];
2520
+ }
2521
+ drawPastTradeIndicators() {
2522
+ if (!(!c.pastTradeIndicators.visible || !this.viewport || this.candles.length === 0))
2523
+ for (const t of this.pastTrades) {
2524
+ const e = this.getPastTradeIndicatorRenderState(t);
2525
+ e && (this.drawPastTradeConnector(e), this.drawPastTradeArrows(e));
2526
+ }
2527
+ }
2528
+ getPastTradeIndicatorRenderState(t) {
2529
+ if (!this.viewport) return null;
2530
+ const e = this.getTimeX(t.startTime), i = this.getTimeX(t.closeTime);
2531
+ if (e === null || i === null) return null;
2532
+ const r = this.getPriceY(t.openPrice, this.viewport), s = this.getPriceY(t.closePrice, this.viewport), n = c.pastTradeIndicators.arrow, a = n.width, o = n.height;
2533
+ return {
2534
+ trade: t,
2535
+ start: {
2536
+ x: e,
2537
+ y: r,
2538
+ time: t.startTime,
2539
+ price: t.openPrice
2540
+ },
2541
+ close: {
2542
+ x: i,
2543
+ y: s,
2544
+ time: t.closeTime,
2545
+ price: t.closePrice
2546
+ },
2547
+ arrowWidth: a,
2548
+ arrowHeight: o
2549
+ };
2550
+ }
2551
+ drawPastTradeConnector({ start: t, close: e }) {
2552
+ const i = c.pastTradeIndicators.connectorLine;
2553
+ this.drawConnectorLine({
2554
+ x1: t.x,
2555
+ y1: t.y,
2556
+ x2: e.x,
2557
+ y2: e.y,
2558
+ config: i
2559
+ });
2560
+ }
2561
+ drawPastTradeArrows({ trade: t, start: e, close: i, arrowWidth: r, arrowHeight: s }) {
2562
+ const n = c.pastTradeIndicators.arrow;
2563
+ if (!n.visible) return;
2564
+ const a = t.type === "buy" ? "sell" : "buy";
2565
+ this.drawPastTradeArrow({
2566
+ x: e.x,
2567
+ y: e.y,
2568
+ type: t.type,
2569
+ width: r,
2570
+ height: s,
2571
+ config: n
2572
+ }), this.drawPastTradeArrow({
2573
+ x: i.x,
2574
+ y: i.y,
2575
+ type: a,
2576
+ width: r,
2577
+ height: s,
2578
+ config: n
2579
+ });
2580
+ }
2581
+ drawPastTradeArrow({ x: t, y: e, type: i, width: r, height: s, config: n }) {
2582
+ if (r <= 0 || s <= 0) return;
2583
+ const a = this.#e, o = i === "buy", l = o ? n.buyColor : n.sellColor, d = o ? n.buyBorderColor : n.sellBorderColor, u = o ? 1 : -1, g = t, m = e, w = t, C = e + u * s, x = s / 2, T = x / Math.SQRT2, H = x / Math.SQRT2, Lt = g - T, Ht = g + T, Z = m + u * H, q = Math.max(1, n.shaftWidthRatio), kt = q + n.borderWidth * 2, Q = () => {
2584
+ a.beginPath(), a.moveTo(w, C), a.lineTo(g, m), a.moveTo(g, m), a.lineTo(Lt, Z), a.moveTo(g, m), a.lineTo(Ht, Z), a.stroke();
2585
+ };
2586
+ a.save(), a.globalAlpha = n.opacity, a.lineCap = "round", a.lineJoin = "round", a.strokeStyle = d, a.lineWidth = kt, Q(), a.strokeStyle = l, a.lineWidth = q, Q(), a.restore();
2587
+ }
2588
+ getTimeX(t) {
2589
+ if (!this.viewport || this.candles.length === 0 || !Number.isFinite(t)) return null;
2590
+ const e = this.normalizeTimestamp(t), i = this.candles[0], r = this.candles[this.candles.length - 1];
2591
+ if (!i || !r) return null;
2592
+ if (this.candles.length === 1) return this.getCandleCenterX(0);
2593
+ if (e <= i.time) {
2594
+ const d = this.getCandleTimeInterval(0), u = (i.time - e) / d;
2595
+ return this.getCandleCenterX(0) - u * this.viewport.candleSpacing;
2596
+ }
2597
+ if (e >= r.time) {
2598
+ const d = this.candles.length - 1, u = this.getCandleTimeInterval(d), g = (e - r.time) / u;
2599
+ return this.getCandleCenterX(d) + g * this.viewport.candleSpacing;
2600
+ }
2601
+ const s = this.getFirstCandleIndexAtOrAfterTime(e), n = Math.max(0, s - 1), a = this.candles[n], o = this.candles[s];
2602
+ if (!a || !o || o.time === a.time) return this.getCandleCenterX(n);
2603
+ const l = (e - a.time) / (o.time - a.time);
2604
+ return this.getCandleCenterX(n) + l * this.viewport.candleSpacing;
2605
+ }
2606
+ normalizeTimestamp(t) {
2607
+ return t < 1e12 ? t * 1e3 : t;
2608
+ }
2609
+ getFirstCandleIndexAtOrAfterTime(t) {
2610
+ let e = 0, i = this.candles.length - 1;
2611
+ for (; e < i; ) {
2612
+ const r = Math.floor((e + i) / 2);
2613
+ this.candles[r].time < t ? e = r + 1 : i = r;
2614
+ }
2615
+ return e;
2616
+ }
2617
+ getCandleCenterX(t) {
2618
+ return this.viewport ? t * this.viewport.candleSpacing + this.viewport.offsetX + this.viewport.candleWidth / 2 : 0;
2619
+ }
2620
+ getCandleTimeInterval(t) {
2621
+ const e = this.candles[t], i = this.candles[t - 1], r = this.candles[t + 1];
2622
+ return e && r && r.time > e.time ? r.time - e.time : e && i && e.time > i.time ? e.time - i.time : 60 * 1e3;
2623
+ }
2624
+ getTradeHandleRenderState({ price: t, type: e, trade: i }) {
2625
+ if (!this.viewport) return null;
2626
+ const r = this.viewport, s = this.getPriceY(t, r), n = this.getHandleStyleConfig(e), a = n.handle, o = n.handleLine, l = this.getHandleWidth(a), d = a.height;
2627
+ return {
2628
+ price: t,
2629
+ type: e,
2630
+ trade: i,
2631
+ viewport: r,
2632
+ y: s,
2633
+ handleStyleConfig: n,
2634
+ handleConfig: a,
2635
+ lineConfig: o,
2636
+ handleX: this.getHandleX({
2637
+ handleWidth: l,
2638
+ config: a
2639
+ }),
2640
+ handleY: s - d / 2,
2641
+ handleWidth: l,
2642
+ handleHeight: d
2643
+ };
2644
+ }
2645
+ getPriceY(t, e) {
2646
+ return Y({
2647
+ price: t,
2648
+ minPrice: e.minPrice,
2649
+ priceRange: e.priceRange,
2650
+ chartHeight: this.#t.height
2651
+ });
2652
+ }
2653
+ addHandleHitbox(t) {
2654
+ this.handleHitboxes.push(this.createHitBox({
2655
+ price: t.price,
2656
+ trade: t.trade,
2657
+ type: t.type,
2658
+ x: t.handleX,
2659
+ y: t.handleY,
2660
+ width: t.handleWidth,
2661
+ height: t.handleHeight,
2662
+ viewport: t.viewport
2663
+ }));
2664
+ }
2665
+ addMissingProtectionHitbox(t) {
2666
+ this.handleHitboxes.push(this.createMissingProtectionHitBox({
2667
+ price: t.price,
2668
+ trade: t.trade,
2669
+ type: t.type,
2670
+ x: t.handleX,
2671
+ y: t.handleY,
2672
+ width: t.handleWidth,
2673
+ height: t.handleHeight,
2674
+ viewport: t.viewport
2675
+ }));
2676
+ }
2677
+ drawHandleHorizontalLines({ handleX: t, handleWidth: e, y: i, lineConfig: r, blockedRanges: s = [] }) {
2678
+ this.drawHorizontalLineAroundBlockedRanges({
2679
+ y: i,
2680
+ config: r,
2681
+ blockedRanges: [{
2682
+ x1: t,
2683
+ x2: t + e
2684
+ }, ...s]
2685
+ });
2686
+ }
2687
+ drawTemporaryProtectionHorizontalLines({ handleX: t, handleWidth: e, y: i, lineConfig: r }) {
2688
+ this.drawHorizontalLineAroundBlockedRanges({
2689
+ y: i,
2690
+ config: r,
2691
+ blockedRanges: [{
2692
+ x1: t,
2693
+ x2: t + e
2694
+ }]
2695
+ });
2696
+ }
2697
+ drawHorizontalLineAroundBlockedRanges({ y: t, config: e, blockedRanges: i }) {
2698
+ const r = this.getMergedBlockedRanges(i);
2699
+ let s = 0;
2700
+ for (const n of r)
2701
+ n.x1 > s && this.drawHandleLine({
2702
+ x1: s,
2703
+ x2: n.x1,
2704
+ y: t,
2705
+ config: e
2706
+ }), s = Math.max(s, n.x2);
2707
+ s < this.#t.width && this.drawHandleLine({
2708
+ x1: s,
2709
+ x2: this.#t.width,
2710
+ y: t,
2711
+ config: e
2712
+ });
2713
+ }
2714
+ getMergedBlockedRanges(t) {
2715
+ const e = t.map((r) => ({
2716
+ x1: Math.max(0, Math.min(r.x1, this.#t.width)),
2717
+ x2: Math.max(0, Math.min(r.x2, this.#t.width))
2718
+ })).filter((r) => r.x2 > r.x1).sort((r, s) => r.x1 - s.x1), i = [];
2719
+ for (const r of e) {
2720
+ const s = i.at(-1);
2721
+ if (!s || r.x1 > s.x2) {
2722
+ i.push({ ...r });
2723
+ continue;
2724
+ }
2725
+ s.x2 = Math.max(s.x2, r.x2);
2726
+ }
2727
+ return i;
2728
+ }
2729
+ getHandleLineDash(t) {
2730
+ switch (t.style) {
2731
+ case "dashed":
2732
+ return t.dash ?? [4, 4];
2733
+ case "dotted":
2734
+ return t.dash ?? [2, 4];
2735
+ default:
2736
+ return [];
2737
+ }
2738
+ }
2739
+ getHandleSections({ handleType: t, handleConfig: e, trade: i }) {
2740
+ const r = this.getHandlePnLLabel({
2741
+ handleType: t,
2742
+ trade: i
2743
+ });
2744
+ return [
2745
+ {
2746
+ label: `${i.volume.toFixed(2)}L`,
2747
+ width: e.widthVolume,
2748
+ visible: e.showVolumeSection
2749
+ },
2750
+ {
2751
+ label: r,
2752
+ width: e.widthPNL,
2753
+ visible: e.showPnlSection,
2754
+ color: this.getHandlePnlTextColor({
2755
+ handleType: t,
2756
+ trade: i
2757
+ })
2758
+ },
2759
+ {
2760
+ label: "×",
2761
+ width: e.widthClose,
2762
+ visible: e.showCloseSection,
2763
+ color: e.closeButtonColor
2764
+ }
2765
+ ];
2766
+ }
2767
+ drawHandleSectionLabel({ section: t, currentX: e, y: i }) {
2768
+ const r = this.#e, s = c.tradeHandles, n = e + t.width / 2;
2769
+ r.fillStyle = t.color ?? s.textColor, r.fillText(t.label, n - r.measureText(t.label).width / 2, i);
2770
+ }
2771
+ drawHandleSectionDivider({ x: t, y1: e, y2: i, config: r }) {
2772
+ const s = this.#e;
2773
+ s.strokeStyle = r.sectionDividerColor, s.lineWidth = 1, s.beginPath(), s.moveTo(t, e), s.lineTo(t, i), s.stroke();
2774
+ }
2775
+ getMissingProtectionRenderStates({ trade: t, startPriceRenderState: e }) {
2776
+ if (!c.tradeHandles.missingProtectionHandles.visible) return [];
2777
+ const i = this.getMissingProtectionHandleTypes(t);
2778
+ return i.map((r) => this.getMissingProtectionHandleRenderState({
2779
+ type: r,
2780
+ trade: t,
2781
+ startPriceRenderState: e,
2782
+ visibleTypes: i
2783
+ }));
2784
+ }
2785
+ getMissingProtectionHandleTypes(t) {
2786
+ const e = [];
2787
+ return t.sl || e.push("stopLoss"), t.tp || e.push("takeProfit"), e;
2788
+ }
2789
+ getMissingProtectionHandleRenderState({ type: t, trade: e, startPriceRenderState: i, visibleTypes: r }) {
2790
+ const s = this.getMissingProtectionHandleConfig(t), n = s.width, a = s.height, o = this.getMissingProtectionHandleX({
2791
+ type: t,
2792
+ config: s,
2793
+ startPriceRenderState: i,
2794
+ visibleTypes: r
2795
+ }), l = i.y - a / 2;
2796
+ return {
2797
+ price: e.openPrice,
2798
+ type: t,
2799
+ trade: e,
2800
+ viewport: i.viewport,
2801
+ y: i.y,
2802
+ config: s,
2803
+ handleX: o,
2804
+ handleY: l,
2805
+ handleWidth: n,
2806
+ handleHeight: a
2807
+ };
2808
+ }
2809
+ getMissingProtectionHandleX({ type: t, config: e, startPriceRenderState: i, visibleTypes: r }) {
2810
+ const s = r.indexOf(t), n = e.gapFromStartPriceHandle + s * (e.width + e.gapBetweenHandles);
2811
+ return e.placement === "left" ? i.handleX - n - e.width : i.handleX + i.handleWidth + n;
2812
+ }
2813
+ getLineBlockedRangesFromMissingProtectionHandles(t) {
2814
+ return t.map((e) => ({
2815
+ x1: e.handleX,
2816
+ x2: e.handleX + e.handleWidth
2817
+ }));
2818
+ }
2819
+ getMissingProtectionHandleConfig(t) {
2820
+ const e = c.tradeHandles.missingProtectionHandles;
2821
+ return t === "stopLoss" ? e.slHandle : e.tpHandle;
2822
+ }
2823
+ drawMissingProtectionHandleRect({ x: t, y: e, width: i, config: r }) {
2824
+ const s = this.#e;
2825
+ this.clearRectArea({
2826
+ x: t,
2827
+ y: e,
2828
+ width: i,
2829
+ height: r.height,
2830
+ padding: Math.max(1, r.borderWidth)
2831
+ }), s.save(), s.globalAlpha = r.backgroundOpacity, s.fillStyle = r.backgroundColor, s.fillRect(t, e, i, r.height), s.globalAlpha = r.borderOpacity, s.strokeStyle = r.borderColor, s.lineWidth = r.borderWidth, s.strokeRect(t, e, i, r.height), s.restore();
2832
+ }
2833
+ drawMissingProtectionHandleLabel({ type: t, handleX: e, y: i, handleWidth: r, config: s }) {
2834
+ const n = this.#e, a = t === "stopLoss" ? "SL" : "TP", o = e + r / 2;
2835
+ n.save(), n.font = s.font, n.textBaseline = "middle", n.globalAlpha = s.textOpacity, n.fillStyle = s.textColor, n.fillText(a, o - n.measureText(a).width / 2, i), n.restore();
2836
+ }
2837
+ drawTemporaryProtectionDrag() {
2838
+ const t = this.getTemporaryProtectionRenderState();
2839
+ t && (this.drawTemporaryProtectionHorizontalLines(t), this.drawTemporaryProtectionHandleRect({
2840
+ x: t.handleX,
2841
+ y: t.handleY,
2842
+ width: t.handleWidth,
2843
+ config: t.handleConfig
2844
+ }), this.drawTemporaryProtectionPnLLabel(t));
2845
+ }
2846
+ getTemporaryProtectionRenderState() {
2847
+ if (!this.temporaryProtectionDrag) return null;
2848
+ const { trade: t, type: e, viewport: i } = this.temporaryProtectionDrag, r = p(this.temporaryProtectionDrag.price), s = this.getPriceY(r, i), n = this.getTemporaryProtectionHandleConfig(e), a = this.getTemporaryProtectionLineConfig(e), o = n.widthPNL, l = n.height;
2849
+ return {
2850
+ price: r,
2851
+ type: e,
2852
+ trade: t,
2853
+ viewport: i,
2854
+ y: s,
2855
+ handleConfig: n,
2856
+ lineConfig: a,
2857
+ handleX: this.getTemporaryProtectionHandleX({
2858
+ handleWidth: o,
2859
+ config: n
2860
+ }),
2861
+ handleY: s - l / 2,
2862
+ handleWidth: o,
2863
+ handleHeight: l
2864
+ };
2865
+ }
2866
+ getTemporaryProtectionHandleConfig(t) {
2867
+ const e = c.tradeHandles.missingProtectionHandles;
2868
+ return t === "stopLoss" ? e.temporarySlHandle : e.temporaryTpHandle;
2869
+ }
2870
+ getTemporaryProtectionLineConfig(t) {
2871
+ const e = c.tradeHandles.missingProtectionHandles;
2872
+ return t === "stopLoss" ? e.temporarySlLine : e.temporaryTpLine;
2873
+ }
2874
+ drawTemporaryProtectionHandleRect({ x: t, y: e, width: i, config: r }) {
2875
+ const s = this.#e;
2876
+ this.clearRectArea({
2877
+ x: t,
2878
+ y: e,
2879
+ width: i,
2880
+ height: r.height,
2881
+ padding: Math.max(1, r.borderWidth)
2882
+ }), s.save(), s.globalAlpha = r.backgroundOpacity, s.fillStyle = r.backgroundColor, s.fillRect(t, e, i, r.height), s.globalAlpha = r.borderOpacity, s.strokeStyle = r.borderColor, s.lineWidth = r.borderWidth, s.strokeRect(t, e, i, r.height), s.restore();
2883
+ }
2884
+ drawTemporaryProtectionPnLLabel({ trade: t, type: e, price: i, handleX: r, y: s, handleWidth: n, handleConfig: a }) {
2885
+ const o = this.#e, l = dt(t.type, t.openPrice, i, t.volume, t.symbol), d = this.formatPnlLabel(l), u = r + n / 2;
2886
+ o.save(), o.font = a.font, o.textBaseline = "middle", o.globalAlpha = a.textOpacity, o.fillStyle = this.getProtectionPnlTextColor(e), o.fillText(d, u - o.measureText(d).width / 2, s), o.restore();
2887
+ }
2888
+ clearRectArea({ x: t, y: e, width: i, height: r, padding: s = 0 }) {
2889
+ this.#e.clearRect(t - s, e - s, i + s * 2, r + s * 2);
2890
+ }
2891
+ getPositionedHandleX({ handleWidth: t, position: e }) {
2892
+ const i = this.#t.width;
2893
+ switch (e.placement) {
2894
+ case "left":
2895
+ return e.margin ?? 0;
2896
+ case "center":
2897
+ return i / 2 - t / 2;
2898
+ case "right":
2899
+ return i - t - (e.margin ?? 0);
2900
+ }
2901
+ }
2902
+ getHandlePnlTextColor({ handleType: t, trade: e }) {
2903
+ return t === "stopLoss" ? this.getProtectionPnlTextColor("stopLoss") : t === "takeProfit" ? this.getProtectionPnlTextColor("takeProfit") : e.pnl >= 0 ? c.tradeHandles.pnlTextColors.positive : c.tradeHandles.pnlTextColors.negative;
2904
+ }
2905
+ getProtectionPnlTextColor(t) {
2906
+ return t === "takeProfit" ? c.tradeHandles.pnlTextColors.positive : c.tradeHandles.pnlTextColors.negative;
2907
+ }
2908
+ isBuyTrade(t) {
2909
+ return String(t.type).toLowerCase().includes("buy");
2910
+ }
2911
+ formatPnlLabel(t) {
2912
+ return `${t >= 0 ? "+" : ""}${t.toFixed(2)}`;
2913
+ }
2914
+ }, te = [
2915
+ "pointerdown",
2916
+ "pointermove",
2917
+ "pointerup"
2918
+ ], ee = class {
2919
+ #t;
2920
+ #e;
2921
+ #r;
2922
+ #s;
2923
+ #c;
2924
+ #i;
2925
+ #l;
2926
+ #u;
2927
+ #n;
2928
+ #o = null;
2929
+ #a = null;
2930
+ #d = !1;
2931
+ #h = 0;
2932
+ #f = 0;
2933
+ constructor(t) {
2934
+ this.#t = t.canvas, this.#e = t.getHandleHitboxes, this.#r = t.getCurrentPrice, this.#s = t.onDrag, this.#c = t.onMissingProtectionDrag, this.#i = t.onMissingProtectionDragEnd, this.#l = t.onMissingProtectionDragCancel, this.#u = t.onTradeModified, this.#n = t.onTradeCloseClicked;
2935
+ }
2936
+ cancelActiveInteraction() {
2937
+ this.#a && this.#l?.(), this.#o = null, this.#a = null, this.#d = !1, this.#h = 0, this.#f = 0;
2938
+ }
2939
+ handlePointerEvent(t) {
2940
+ if (!this.isSupportedEventType(t.type)) return !1;
2941
+ const e = this.getCanvasMousePoint(t);
2942
+ if (t.type === "pointermove" && this.#a) return this.handleActiveMissingProtectionDrag(t, e);
2943
+ if (t.type === "pointermove" && this.#o) return this.handleActiveDrag(t, e);
2944
+ if (t.type === "pointerup")
2945
+ return this.#a ? this.handleMissingProtectionDragEnd() : this.handleDragEnd();
2946
+ const i = this.getHoveredHitbox(e);
2947
+ return i ? this.handleHoveredHitbox(t, i, e) : (this.resetCursorIfIdle(), !1);
2948
+ }
2949
+ isSupportedEventType(t) {
2950
+ return te.includes(t);
2951
+ }
2952
+ getCanvasMousePoint(t) {
2953
+ const e = this.#t.getBoundingClientRect(), i = this.#t.width / e.width, r = this.#t.height / e.height;
2954
+ return {
2955
+ x: (t.clientX - e.left) * i,
2956
+ y: (t.clientY - e.top) * r
2957
+ };
2958
+ }
2959
+ handleActiveDrag(t, e) {
2960
+ if (!this.#o) return !1;
2961
+ const i = this.getNextDragPrice(e.y);
2962
+ return this.#o.price = i, this.#s?.({
2963
+ trade: this.#o.trade,
2964
+ type: this.#o.type,
2965
+ price: i
2966
+ }), this.consumeEvent(t), !0;
2967
+ }
2968
+ handleActiveMissingProtectionDrag(t, e) {
2969
+ if (!this.#a || !this.isProtectionHandleType(this.#a.type)) return !1;
2970
+ const i = e.y - this.#h, r = c.tradeHandles.missingProtectionHandles.dragActivationThresholdPx;
2971
+ if (!this.#d && Math.abs(i) < r)
2972
+ return this.consumeEvent(t), !0;
2973
+ this.#d = !0;
2974
+ const s = this.getNextMissingProtectionPrice(e.y);
2975
+ return this.#a.price = s, this.#c?.({
2976
+ trade: this.#a.trade,
2977
+ type: this.#a.type,
2978
+ price: s
2979
+ }), this.consumeEvent(t), !0;
2980
+ }
2981
+ getNextDragPrice(t) {
2982
+ if (!this.#o) return 0;
2983
+ const e = t - this.#h, i = this.#o.viewport.priceRange / this.#t.height, r = p(this.#f - e * i);
2984
+ return this.isProtectionHandleType(this.#o.type) ? this.constrainExistingProtectionPrice({
2985
+ price: r,
2986
+ hitbox: this.#o,
2987
+ type: this.#o.type
2988
+ }) : r;
2989
+ }
2990
+ getNextMissingProtectionPrice(t) {
2991
+ if (!this.#a || !this.isProtectionHandleType(this.#a.type)) return 0;
2992
+ const e = p(this.#a.viewport.minPrice + (this.#t.height - t) / this.#t.height * this.#a.viewport.priceRange);
2993
+ return this.constrainProtectionPrice({
2994
+ price: e,
2995
+ hitbox: this.#a,
2996
+ type: this.#a.type
2997
+ });
2998
+ }
2999
+ handleDragEnd() {
3000
+ return this.#o && this.modifyActiveDragTrade(), this.#o = null, !1;
3001
+ }
3002
+ handleMissingProtectionDragEnd() {
3003
+ return this.#a && this.#d && this.isProtectionHandleType(this.#a.type) ? this.#i?.({
3004
+ trade: this.#a.trade,
3005
+ type: this.#a.type,
3006
+ price: p(this.#a.price)
3007
+ }) : this.#l?.(), this.#a = null, this.#d = !1, !1;
3008
+ }
3009
+ modifyActiveDragTrade() {
3010
+ if (!this.#o) return;
3011
+ const t = p(this.#o.price);
3012
+ if (this.#o.type === "stopLoss") {
3013
+ this.#u?.({
3014
+ ticket: this.#o.trade.ticket,
3015
+ sl: t
3016
+ });
3017
+ return;
3018
+ }
3019
+ this.#u?.({
3020
+ ticket: this.#o.trade.ticket,
3021
+ tp: t
3022
+ });
3023
+ }
3024
+ getHoveredHitbox(t) {
3025
+ return this.#e().find((e) => this.isPointInsideArea(t, e)) ?? null;
3026
+ }
3027
+ handleHoveredHitbox(t, e, i) {
3028
+ if (e.kind === "missingProtectionHandle") return this.handleMissingProtectionHandleHover(t, e, i);
3029
+ const r = this.isPointInsideArea(i, e.closeButtonArea), s = this.isPointInsideArea(i, e.labelArea);
3030
+ return r ? this.handleCloseButtonHover(t, e) : (this.updateHandleCursor(e), s && t.type === "pointerdown" && e.type !== "startPrice" && this.startDrag(e, i.y), e.type !== "startPrice" ? (this.consumeEvent(t), !0) : !1);
3031
+ }
3032
+ handleMissingProtectionHandleHover(t, e, i) {
3033
+ return document.body.style.cursor = "ns-resize", t.type === "pointerdown" ? (this.startMissingProtectionDrag(e, i.y), this.consumeEvent(t), !0) : (this.consumeEvent(t), !0);
3034
+ }
3035
+ handleCloseButtonHover(t, e) {
3036
+ return document.body.style.cursor = "pointer", t.type !== "pointerdown" ? e.type !== "startPrice" : (this.handleCloseButtonClick(e), this.consumeEvent(t), !0);
3037
+ }
3038
+ handleCloseButtonClick(t) {
3039
+ if (t.type === "startPrice") {
3040
+ this.#n?.({ ticket: t.trade.ticket });
3041
+ return;
3042
+ }
3043
+ this.#u?.({
3044
+ ticket: t.trade.ticket,
3045
+ ...t.type === "stopLoss" ? { sl: null } : {},
3046
+ ...t.type === "takeProfit" ? { tp: null } : {}
3047
+ });
3048
+ }
3049
+ updateHandleCursor(t) {
3050
+ document.body.style.cursor = t.type === "startPrice" ? "crosshair" : "ns-resize";
3051
+ }
3052
+ startDrag(t, e) {
3053
+ this.#o = t, this.#h = e, this.#f = t.price;
3054
+ }
3055
+ startMissingProtectionDrag(t, e) {
3056
+ this.#a = t, this.#h = e, this.#d = !1;
3057
+ }
3058
+ resetCursorIfIdle() {
3059
+ !this.#o && !this.#a && (document.body.style.cursor = "crosshair");
3060
+ }
3061
+ isPointInsideArea(t, e) {
3062
+ const i = t.x >= e.x && t.x <= e.x + e.width, r = t.y >= e.y && t.y <= e.y + e.height;
3063
+ return i && r;
3064
+ }
3065
+ isProtectionHandleType(t) {
3066
+ return t === "stopLoss" || t === "takeProfit";
3067
+ }
3068
+ constrainExistingProtectionPrice({ price: t, hitbox: e, type: i }) {
3069
+ const r = this.isBuyTrade(e), s = e.trade.openPrice, n = this.#r?.() ?? 0;
3070
+ return p(i === "takeProfit" ? r ? Math.max(t, s, n) : Math.min(t, s, n) : r ? Math.min(t, n) : Math.max(t, n));
3071
+ }
3072
+ constrainProtectionPrice({ price: t, hitbox: e, type: i }) {
3073
+ const r = this.isBuyTrade(e), s = e.trade.openPrice, n = this.#r?.() ?? 0;
3074
+ return p(i === "takeProfit" ? r ? Math.max(t, s, n) : Math.min(t, s, n) : r ? Math.min(t, n) : Math.max(t, n));
3075
+ }
3076
+ isBuyTrade(t) {
3077
+ return String(t.trade.type).toLowerCase().includes("buy");
3078
+ }
3079
+ consumeEvent(t) {
3080
+ t.stopPropagation(), t.preventDefault();
3081
+ }
3082
+ }, ie = class {
3083
+ #t;
3084
+ #e;
3085
+ candles;
3086
+ viewport = null;
3087
+ bullishColor;
3088
+ bearishColor;
3089
+ opacity;
3090
+ height;
3091
+ bottomOffset;
3092
+ minBarHeight;
3093
+ constructor(t) {
3094
+ this.#t = t.canvas;
3095
+ const e = this.#t.getContext("2d");
3096
+ if (!e) throw new Error("Canvas 2D context not supported");
3097
+ this.#e = e, this.candles = t.candles, this.bullishColor = t.bullishColor ?? c.volume.bullishColor, this.bearishColor = t.bearishColor ?? c.volume.bearishColor, this.opacity = this.clampOpacity(t.opacity ?? c.volume.opacity), this.height = Math.max(0, t.height ?? c.volume.height), this.bottomOffset = Math.max(0, t.bottomOffset ?? c.volume.bottomOffset), this.minBarHeight = Math.max(0, t.minBarHeight ?? c.volume.minBarHeight);
3098
+ }
3099
+ setCandles(t) {
3100
+ this.candles = t;
3101
+ }
3102
+ setLiveCandle(t) {
3103
+ }
3104
+ setViewport(t) {
3105
+ this.viewport = t;
3106
+ }
3107
+ render() {
3108
+ if (this.clearCanvas(), !c.volume.visible || !this.viewport || this.candles.length === 0) return;
3109
+ const t = this.#e, e = this.#t.width, i = this.#t.height, { startIndex: r, endIndex: s } = this.getVisibleRange(e);
3110
+ if (s < r) return;
3111
+ const n = this.candles.slice(r, s + 1), a = this.getMaxVolume(n);
3112
+ a <= 0 || this.drawVolumeBars({
3113
+ ctx: t,
3114
+ candles: n,
3115
+ startIndex: r,
3116
+ maxVolume: a,
3117
+ chartHeight: i
3118
+ });
3119
+ }
3120
+ drawVolumeBars({ ctx: t, candles: e, startIndex: i, maxVolume: r, chartHeight: s }) {
3121
+ t.save(), t.globalAlpha = this.opacity, e.forEach((n, a) => {
3122
+ const o = i + a, l = this.getCandleX(o);
3123
+ this.drawSingleVolumeBar({
3124
+ ctx: t,
3125
+ candle: n,
3126
+ candleX: l,
3127
+ maxVolume: r,
3128
+ chartHeight: s
3129
+ });
3130
+ }), t.restore();
3131
+ }
3132
+ drawSingleVolumeBar({ ctx: t, candle: e, candleX: i, maxVolume: r, chartHeight: s }) {
3133
+ if (!this.viewport || e.volume <= 0) return;
3134
+ const n = this.getVolumeAreaHeight(s);
3135
+ if (n <= 0) return;
3136
+ const a = n * (e.volume / r), o = Math.min(n, Math.max(a, this.minBarHeight)), l = s - this.bottomOffset - o, d = Math.max(1, this.viewport.candleWidth);
3137
+ t.fillStyle = this.getVolumeBarColor(e), t.fillRect(i, l, d, o);
3138
+ }
3139
+ getVisibleRange(t) {
3140
+ if (!this.viewport) return {
3141
+ startIndex: 0,
3142
+ endIndex: -1
3143
+ };
3144
+ const { offsetX: e, candleSpacing: i } = this.viewport;
3145
+ return {
3146
+ startIndex: Math.max(0, Math.floor(-e / i)),
3147
+ endIndex: Math.min(this.candles.length - 1, Math.ceil((t - e) / i))
3148
+ };
3149
+ }
3150
+ getCandleX(t) {
3151
+ return this.viewport ? t * this.viewport.candleSpacing + this.viewport.offsetX : 0;
3152
+ }
3153
+ getMaxVolume(t) {
3154
+ return t.reduce((e, i) => Math.max(e, i.volume), 0);
3155
+ }
3156
+ getVolumeAreaHeight(t) {
3157
+ return Math.max(0, Math.min(this.height, t - this.bottomOffset));
3158
+ }
3159
+ getVolumeBarColor(t) {
3160
+ return t.close >= t.open ? this.bullishColor : this.bearishColor;
3161
+ }
3162
+ clampOpacity(t) {
3163
+ return Math.max(0, Math.min(t, 1));
3164
+ }
3165
+ clearCanvas() {
3166
+ this.#e.clearRect(0, 0, this.#t.width, this.#t.height);
3167
+ }
3168
+ }, W = (t) => {
3169
+ const e = document.createElement("canvas");
3170
+ return e.className = t, e;
3171
+ }, re = (t, e) => {
3172
+ const i = document.createElement("div");
3173
+ i.className = "trading-reload-chart", i.style.backgroundColor = e, i.tabIndex = 0;
3174
+ const r = document.createElement("div");
3175
+ r.className = "trading-reload-chart__layout";
3176
+ const s = document.createElement("div");
3177
+ s.className = "trading-reload-chart__stack";
3178
+ const n = W("trading-reload-chart__volume"), a = W("trading-reload-chart__plot"), o = W("trading-reload-chart__shapes"), l = W("trading-reload-chart__trades"), d = W("trading-reload-chart__overlay"), u = W("trading-reload-chart__axis-x");
3179
+ s.append(n, a, o, l, d, u);
3180
+ const g = W("trading-reload-chart__axis-y");
3181
+ return r.append(s, g), i.append(r), t.append(i), {
3182
+ root: i,
3183
+ layout: r,
3184
+ stack: s,
3185
+ volumeCanvas: n,
3186
+ candleCanvas: a,
3187
+ shapesCanvas: o,
3188
+ tradesCanvas: l,
3189
+ overlayCanvas: d,
3190
+ axisXCanvas: u,
3191
+ axisYCanvas: g,
3192
+ interactionTarget: r
3193
+ };
3194
+ }, ct = (t) => typeof t == "object" && t !== null && !Array.isArray(t), St = (t, e) => {
3195
+ const i = { ...t };
3196
+ for (const r of Object.keys(e)) {
3197
+ const s = e[r], n = t[r];
3198
+ if (s !== void 0) {
3199
+ if (ct(s) && ct(n)) {
3200
+ i[r] = St(n, s);
3201
+ continue;
3202
+ }
3203
+ i[r] = s;
3204
+ }
3205
+ }
3206
+ return i;
3207
+ }, ut = (t) => St(c, t ?? {}), gt = (t, e) => e === void 0 || t.time >= 1e12 ? t : {
3208
+ ...t,
3209
+ time: t.time * 1e3 - e
3210
+ }, se = class {
3211
+ #t;
3212
+ #e;
3213
+ #r;
3214
+ #s = null;
3215
+ #c = null;
3216
+ #i = null;
3217
+ #l = null;
3218
+ #u = null;
3219
+ #n = null;
3220
+ #o = null;
3221
+ #a = null;
3222
+ #d = null;
3223
+ #h = null;
3224
+ #f = !1;
3225
+ #v = 0;
3226
+ #y = !1;
3227
+ #x = 0;
3228
+ #T = 0;
3229
+ #p = null;
3230
+ #P = null;
3231
+ #w = null;
3232
+ constructor(t, e) {
3233
+ this.#t = t, this.#e = e, this.#r = ut(e.config), this.#s = re(t, this.#r.colors.background), this.#h = new Et({
3234
+ canvas: this.#s.overlayCanvas,
3235
+ crosshairColor: this.#r.crosshair.color,
3236
+ crosshairThickness: this.#r.crosshair.thickness,
3237
+ crosshairStyle: this.#r.crosshair.style
3238
+ }), this.#R(), this.#W(), this.#H(), this.updateProps(e);
3239
+ }
3240
+ updateProps(t) {
3241
+ const e = this.#e.activeShapeTool;
3242
+ this.#e = t, this.#r = ut(t.config), this.#s && (this.#s.root.style.backgroundColor = this.#r.colors.background, this.#s.layout.style.setProperty("--axis-x-height", `${this.#r.axis.axisX.height}px`)), !this.#f && t.candles.length > 0 && this.#M(t.candles), this.#i && (t.activeShapeTool !== e && this.#B(t.activeShapeTool), this.#l?.setConfig(this.#r.shapes), this.#l?.setShapes(t.shapes), this.#l?.setActiveTool(t.activeShapeTool), this.#u?.setConfig(this.#r.shapes.previousDayHighLow ?? null), this.#u?.setPreviousDay(t.previousDay), this.#n?.setPastTrades(t.pastTrades ?? []), this.#b(), this.#A(), this.#g());
3243
+ }
3244
+ resetChartView() {
3245
+ this.#i?.resetView(), this.#g();
3246
+ }
3247
+ destroy() {
3248
+ this.#w?.abort(), this.#w = null, this.#P?.disconnect(), this.#P = null, this.#s?.root.remove(), this.#s = null, this.#c = null, this.#i = null, this.#l = null, this.#u = null, this.#n = null, this.#o = null, this.#a = null, this.#d = null, this.#h = null, this.#f = !1;
3249
+ }
3250
+ #M(t) {
3251
+ if (!this.#s || this.#f) return;
3252
+ const e = t.map((r) => gt(r, this.#e.brokerTimezoneOffsetMs));
3253
+ this.#c = new ie({
3254
+ canvas: this.#s.volumeCanvas,
3255
+ candles: e,
3256
+ bullishColor: this.#r.volume.bullishColor,
3257
+ bearishColor: this.#r.volume.bearishColor,
3258
+ opacity: this.#r.volume.opacity,
3259
+ height: this.#r.volume.height,
3260
+ bottomOffset: this.#r.volume.bottomOffset,
3261
+ minBarHeight: this.#r.volume.minBarHeight
3262
+ }), this.#i = new Nt({
3263
+ canvas: this.#s.candleCanvas,
3264
+ candles: e,
3265
+ baseCandleWidth: this.#r.candles.defaultWidth,
3266
+ baseCandleGap: this.#r.candles.defaultGap,
3267
+ bullishColor: this.#r.colors.bullish,
3268
+ bearishColor: this.#r.colors.bearish,
3269
+ autoFollowLatestCandle: this.#r.candles.autoFollowLatestCandle,
3270
+ autoFollowThresholdCandles: this.#r.candles.autoFollowThresholdCandles,
3271
+ rightOffsetCandles: this.#r.candles.rightOffsetCandles
3272
+ }), this.#l = new qt({
3273
+ canvas: this.#s.shapesCanvas,
3274
+ activeSymbol: this.#e.activeSymbol,
3275
+ candles: e,
3276
+ shapes: this.#e.shapes,
3277
+ activeTool: this.#e.activeShapeTool,
3278
+ config: this.#r.shapes,
3279
+ onShapeAdded: (r) => {
3280
+ this.#e.onShapeAdded?.(r);
3281
+ },
3282
+ onShapeModified: (r) => {
3283
+ this.#e.onShapeModified?.(r);
3284
+ },
3285
+ onShapeSelected: (r) => {
3286
+ this.#e.onShapeSelected?.(r);
3287
+ },
3288
+ onToolChange: (r) => {
3289
+ this.#e.onActiveShapeToolChange?.(r);
3290
+ }
3291
+ });
3292
+ const i = this.#s.shapesCanvas.getContext("2d");
3293
+ i && (this.#u = new Gt({
3294
+ ctx: i,
3295
+ config: this.#r.shapes.previousDayHighLow ?? null
3296
+ }), this.#u.setPreviousDay(this.#e.previousDay)), this.#n = new Qt({ canvas: this.#s.tradesCanvas }), this.#n.setCandles(e), this.#n.setPastTrades(this.#e.pastTrades ?? []), this.#b(), this.#a = new Yt({
3297
+ canvas: this.#s.axisXCanvas,
3298
+ candles: e
3299
+ }), this.#d = new Vt({ canvas: this.#s.axisYCanvas }), this.#o = new ee({
3300
+ canvas: this.#s.tradesCanvas,
3301
+ getHandleHitboxes: () => this.#m() ? [] : this.#n?.handleHitboxes ?? [],
3302
+ getCurrentPrice: () => this.#i?.liveCandle?.close ?? 0,
3303
+ onDrag: (r) => this.#I(r),
3304
+ onMissingProtectionDrag: (r) => this.#F(r),
3305
+ onMissingProtectionDragEnd: (r) => this.#Y(r),
3306
+ onMissingProtectionDragCancel: () => this.#O(),
3307
+ onTradeModified: (r) => this.#k(r),
3308
+ onTradeCloseClicked: (r) => this.#e.onTradeClose?.(r)
3309
+ }), this.#f = !0, this.#g();
3310
+ }
3311
+ #R() {
3312
+ if (!this.#s) return;
3313
+ this.#w = new AbortController();
3314
+ const { signal: t } = this.#w, e = this.#s.interactionTarget;
3315
+ e.addEventListener("pointerdown", (i) => this.#N(i), { signal: t }), e.addEventListener("pointerup", (i) => this.#U(i), { signal: t }), e.addEventListener("pointermove", (i) => this.#G(i), { signal: t }), e.addEventListener("pointerenter", (i) => this.#_(i), { signal: t }), e.addEventListener("pointerleave", () => this.#$(), { signal: t }), e.addEventListener("contextmenu", (i) => this.#J(i), { signal: t }), e.addEventListener("wheel", (i) => this.#E(i), {
3316
+ passive: !1,
3317
+ signal: t
3318
+ }), this.#s.axisYCanvas.addEventListener("wheel", (i) => this.#z(i), {
3319
+ passive: !1,
3320
+ signal: t
3321
+ }), document.addEventListener("keydown", (i) => this.#j(i), { signal: t });
3322
+ }
3323
+ #W() {
3324
+ this.#P = new ResizeObserver(() => {
3325
+ this.#H(), this.#g();
3326
+ }), this.#P.observe(this.#t);
3327
+ }
3328
+ #D() {
3329
+ const t = this.#r.axis.axisY.width, e = this.#r.axis.axisX.height, i = this.#t.clientWidth, r = this.#t.clientHeight;
3330
+ return {
3331
+ axisYWidth: t,
3332
+ axisXHeight: e,
3333
+ plotWidth: Math.max(0, i - t),
3334
+ plotHeight: Math.max(0, r - e)
3335
+ };
3336
+ }
3337
+ #C(t, e, i) {
3338
+ t.width = e, t.height = i;
3339
+ }
3340
+ #H() {
3341
+ if (!this.#s) return;
3342
+ const { plotWidth: t, plotHeight: e, axisYWidth: i, axisXHeight: r } = this.#D();
3343
+ if (this.#i && this.#v > 0 && t !== this.#v) {
3344
+ const s = t - this.#v;
3345
+ this.#i.panHorizontally(s);
3346
+ }
3347
+ this.#v = t, this.#C(this.#s.volumeCanvas, t, e), this.#C(this.#s.candleCanvas, t, e), this.#C(this.#s.shapesCanvas, t, e), this.#C(this.#s.overlayCanvas, t, e), this.#C(this.#s.tradesCanvas, t, e), this.#C(this.#s.axisXCanvas, t, r), this.#C(this.#s.axisYCanvas, i, e);
3348
+ }
3349
+ #S() {
3350
+ this.#i && (this.#a?.setCandles(this.#i.candles), this.#a?.setViewport(this.#i.viewport), this.#a?.render(), this.#d?.setViewport(this.#i.viewport), this.#d?.render());
3351
+ }
3352
+ #g() {
3353
+ this.#i && (this.#c?.setCandles(this.#i.candles), this.#c?.setLiveCandle(this.#i.liveCandle), this.#c?.setViewport(this.#i.viewport), this.#c?.render(), this.#i.render(), this.#l?.setCandles(this.#i.candles), this.#l?.setViewport(this.#i.viewport), this.#l?.render(), this.#u?.setViewport(this.#i.viewport), this.#u?.render(), this.#n?.setCandles(this.#i.candles), this.#n?.setViewport(this.#i.viewport), this.#n?.isDragging ? this.#n.renderLiveFeed() : this.#n?.render(), this.#S(), this.#h?.render());
3354
+ }
3355
+ #m() {
3356
+ return this.#e.activeShapeTool !== null;
3357
+ }
3358
+ #B(t) {
3359
+ t !== null && (this.#y = !1, this.#n?.setIsDragging(!1), this.#o?.cancelActiveInteraction()), this.#l?.setActiveTool(t);
3360
+ }
3361
+ #X() {
3362
+ const t = this.#e.openTrades ?? [];
3363
+ return this.#p ? t.map((e) => e.ticket !== this.#p?.ticket ? e : {
3364
+ ...e,
3365
+ ...this.#p.sl !== void 0 ? { sl: this.#p.sl } : {},
3366
+ ...this.#p.tp !== void 0 ? { tp: this.#p.tp } : {}
3367
+ }) : t;
3368
+ }
3369
+ #b() {
3370
+ this.#n?.setTrades(this.#X());
3371
+ }
3372
+ #A() {
3373
+ const t = this.#e.liveCandle;
3374
+ if (!t || !this.#i) return;
3375
+ const e = gt(t, this.#e.brokerTimezoneOffsetMs);
3376
+ this.#i.updateLiveCandle(e), this.#n?.setLiveCandle(this.#i.liveCandle), this.#n?.setCandles(this.#i.candles);
3377
+ }
3378
+ #I({ trade: t, type: e, price: i }) {
3379
+ this.#n && (this.#n.setIsDragging(!0), this.#p = {
3380
+ ticket: t.ticket,
3381
+ ...e === "stopLoss" ? { sl: i } : {},
3382
+ ...e === "takeProfit" ? { tp: i } : {}
3383
+ }, this.#b(), this.#n.render());
3384
+ }
3385
+ #F({ trade: t, type: e, price: i }) {
3386
+ this.#n?.viewport && (this.#n.setIsDragging(!0), this.#n.setTemporaryProtectionDrag({
3387
+ visible: !0,
3388
+ trade: t,
3389
+ type: e,
3390
+ price: i,
3391
+ viewport: this.#n.viewport
3392
+ }), this.#n.render());
3393
+ }
3394
+ #Y({ trade: t, type: e, price: i }) {
3395
+ this.#n?.setIsDragging(!1), this.#k({
3396
+ ticket: t.ticket,
3397
+ ...e === "stopLoss" ? { sl: i } : {},
3398
+ ...e === "takeProfit" ? { tp: i } : {}
3399
+ });
3400
+ }
3401
+ #O() {
3402
+ this.#n?.setIsDragging(!1), this.#n?.clearTemporaryProtectionDrag(), this.#n?.render();
3403
+ }
3404
+ #k(t) {
3405
+ this.#n?.setIsDragging(!1), this.#p = null, this.#n?.clearTemporaryProtectionDrag(), this.#e.onTradeModify?.(t), this.#b(), this.#n?.render();
3406
+ }
3407
+ #L(t) {
3408
+ if (!this.#h || !this.#s) return;
3409
+ if (!this.#i) {
3410
+ this.#h.updateMousePosition(t.clientX, t.clientY), this.#h.render();
3411
+ return;
3412
+ }
3413
+ const e = mt(this.#s.overlayCanvas, t), i = this.#i.getNearestCandleIndexByX(e.x), r = i === null ? null : this.#i.candles[i] ?? null, s = i === null ? e.x : this.#i.getRawCandleSnapX(e.x);
3414
+ this.#h.updateMousePosition(t.clientX, t.clientY, { snapX: s });
3415
+ const n = this.#i.getPriceAtY(this.#h.mouseY);
3416
+ this.#d?.setCrosshair({
3417
+ visible: !0,
3418
+ y: this.#h.mouseY,
3419
+ price: n
3420
+ }), this.#a?.setCrosshair({
3421
+ visible: !0,
3422
+ x: this.#h.mouseX,
3423
+ candle: r
3424
+ }), this.#S(), this.#h.render();
3425
+ }
3426
+ #V() {
3427
+ this.#h?.hide(), this.#a?.hideCrosshair(), this.#d?.hideCrosshair(), this.#S(), this.#h?.render();
3428
+ }
3429
+ #E(t) {
3430
+ if (!this.#i || !this.#n) return;
3431
+ this.#m() || this.#o?.handlePointerEvent(t), t.preventDefault();
3432
+ const e = t.deltaY < 0 ? 1 : -1;
3433
+ t.ctrlKey || t.metaKey ? this.#i.zoomVertically(e) : this.#i.zoomHorizontally(e), this.#g();
3434
+ }
3435
+ #z(t) {
3436
+ if (!this.#i) return;
3437
+ t.preventDefault(), t.stopPropagation();
3438
+ const e = t.deltaY < 0 ? 1 : -1;
3439
+ this.#i.zoomVertically(e), this.#g();
3440
+ }
3441
+ #N(t) {
3442
+ if (this.#l?.handlePointerEvent(t) ?? !1) {
3443
+ this.#g();
3444
+ return;
3445
+ }
3446
+ this.#m() || this.#o?.handlePointerEvent(t) || (this.#y = !0, this.#x = t.clientX, this.#T = t.clientY);
3447
+ }
3448
+ #G(t) {
3449
+ const e = this.#l?.handlePointerEvent(t) ?? !1;
3450
+ if (this.#m() || this.#o?.handlePointerEvent(t), this.#L(t), e) {
3451
+ this.#g(), this.#L(t);
3452
+ return;
3453
+ }
3454
+ if (!this.#y || !this.#i || this.#m()) return;
3455
+ const i = t.clientX - this.#x, r = t.clientY - this.#T;
3456
+ this.#x = t.clientX, this.#T = t.clientY, this.#i.panHorizontally(i), this.#i.panVertically(r), this.#g(), this.#L(t);
3457
+ }
3458
+ #U(t) {
3459
+ if (this.#y = !1, this.#l?.handlePointerEvent(t) ?? !1) {
3460
+ this.#g();
3461
+ return;
3462
+ }
3463
+ this.#m() || this.#o?.handlePointerEvent(t);
3464
+ }
3465
+ #_(t) {
3466
+ document.body.style.cursor = "crosshair", this.#l?.handlePointerEvent(t), this.#m() || this.#o?.handlePointerEvent(t);
3467
+ }
3468
+ #$() {
3469
+ this.#l?.clearHoverState(), document.body.style.cursor = "", this.#V();
3470
+ }
3471
+ #J(t) {
3472
+ ((this.#l?.handlePointerEvent(t) ?? !1) || this.#m()) && (t.preventDefault(), t.stopPropagation(), this.#g());
3473
+ }
3474
+ #j(t) {
3475
+ (this.#l?.handleKeyboardEvent(t) ?? !1) && (t.preventDefault(), this.#g());
3476
+ }
3477
+ }, oe = Mt(({ className: t, style: e, ...i }, r) => {
3478
+ const s = z(null), n = z(null), a = z(i);
3479
+ return a.current = i, Rt(r, () => ({ resetChartView() {
3480
+ n.current?.resetChartView();
3481
+ } })), tt(() => {
3482
+ const o = s.current;
3483
+ if (!o) return;
3484
+ const l = new se(o, a.current);
3485
+ return n.current = l, () => {
3486
+ l.destroy(), n.current = null;
3487
+ };
3488
+ }, []), tt(() => {
3489
+ n.current?.updateProps(a.current);
3490
+ }, [i]), /* @__PURE__ */ Wt("div", {
3491
+ ref: s,
3492
+ className: t,
3493
+ style: {
3494
+ width: "100%",
3495
+ height: "100%",
3496
+ ...e
3497
+ }
3498
+ });
3499
+ });
3500
+ export {
3501
+ c as CHART_CONFIG,
3502
+ oe as TradingReload
3503
+ };