hyperprop-charting-library 0.1.80 → 0.1.82

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.
@@ -961,6 +961,13 @@ function createChart(element, options = {}) {
961
961
  style: drawing.style ?? "dotted",
962
962
  width: Math.max(1, Number(drawing.width) || 1),
963
963
  locked: drawing.locked ?? false,
964
+ accountSize: Number(drawing.accountSize) || 0,
965
+ lotSize: Number(drawing.lotSize) || 1,
966
+ risk: Number(drawing.risk) || 0,
967
+ riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
968
+ leverage: Number(drawing.leverage) || 1,
969
+ pointValue: Number(drawing.pointValue) || 1,
970
+ qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
964
971
  ...drawing.label === void 0 ? {} : { label: drawing.label }
965
972
  });
966
973
  const serializeDrawing = (drawing) => ({
@@ -973,6 +980,13 @@ function createChart(element, options = {}) {
973
980
  style: drawing.style,
974
981
  width: drawing.width,
975
982
  locked: drawing.locked,
983
+ accountSize: drawing.accountSize,
984
+ lotSize: drawing.lotSize,
985
+ risk: drawing.risk,
986
+ riskMode: drawing.riskMode,
987
+ leverage: drawing.leverage,
988
+ pointValue: drawing.pointValue,
989
+ qtyPrecision: drawing.qtyPrecision,
976
990
  ...drawing.label === void 0 ? {} : { label: drawing.label }
977
991
  });
978
992
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -982,6 +996,7 @@ function createChart(element, options = {}) {
982
996
  let drawingDragState = null;
983
997
  let drawingsChangeHandler = null;
984
998
  let drawingSelectHandler = null;
999
+ let drawingDoubleClickHandler = null;
985
1000
  let drawingHoverHandler = null;
986
1001
  let lastHoveredDrawingId = null;
987
1002
  let selectedDrawingId = null;
@@ -2429,6 +2444,40 @@ function createChart(element, options = {}) {
2429
2444
  ctx.fillStyle = lossFill;
2430
2445
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2431
2446
  ctx.restore();
2447
+ const isLongPosition = drawing.type === "long-position";
2448
+ const simStart = Math.max(0, Math.round(Math.min(entry.index, right.index)));
2449
+ const simEnd = Math.min(data.length - 1, Math.round(Math.max(entry.index, right.index)));
2450
+ let positionHit = null;
2451
+ for (let i = simStart; i <= simEnd; i += 1) {
2452
+ const bar = data[i];
2453
+ if (!bar) continue;
2454
+ const targetTouched = isLongPosition ? bar.h >= target.price : bar.l <= target.price;
2455
+ const stopTouched = isLongPosition ? bar.l <= stop.price : bar.h >= stop.price;
2456
+ if (stopTouched) {
2457
+ positionHit = { index: i, price: stop.price, profit: false };
2458
+ break;
2459
+ }
2460
+ if (targetTouched) {
2461
+ positionHit = { index: i, price: target.price, profit: true };
2462
+ break;
2463
+ }
2464
+ }
2465
+ if (positionHit) {
2466
+ const hitX = clamp(xFromDrawingPoint({ index: positionHit.index, price: positionHit.price }), boxX0, boxX1);
2467
+ const exitY = yFromPrice(positionHit.price);
2468
+ ctx.save();
2469
+ ctx.globalAlpha = draft ? 0.6 : 1;
2470
+ ctx.fillStyle = hexToRgba(positionHit.profit ? profitColor : lossColor, 0.2);
2471
+ ctx.fillRect(boxX0, Math.min(entryY, exitY), Math.max(0, hitX - boxX0), Math.abs(exitY - entryY));
2472
+ ctx.setLineDash([5, 4]);
2473
+ ctx.lineWidth = 1;
2474
+ ctx.strokeStyle = hexToRgba("#787b86", 0.9);
2475
+ ctx.beginPath();
2476
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2477
+ ctx.lineTo(crisp(hitX), crisp(exitY));
2478
+ ctx.stroke();
2479
+ ctx.restore();
2480
+ }
2432
2481
  if (isSelected) {
2433
2482
  ctx.save();
2434
2483
  ctx.setLineDash([]);
@@ -2463,6 +2512,14 @@ function createChart(element, options = {}) {
2463
2512
  const stopDist = Math.abs(entry.price - stop.price);
2464
2513
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2465
2514
  const cx = (boxX0 + boxX1) / 2;
2515
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2516
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2517
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2518
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2519
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2520
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2521
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2522
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2466
2523
  const drawPositionPill = (text, centerX, centerY, bg) => {
2467
2524
  const prevFont = ctx.font;
2468
2525
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2480,9 +2537,20 @@ function createChart(element, options = {}) {
2480
2537
  ctx.fillText(text, centerX, pillY + pillH / 2);
2481
2538
  ctx.font = prevFont;
2482
2539
  };
2483
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2484
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2485
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2540
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2541
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2542
+ let centerText;
2543
+ let centerBg;
2544
+ if (positionHit) {
2545
+ const pnl = positionHit.profit ? qtyRaw * targetDist * effectivePointValue : -(qtyRaw * stopDist * effectivePointValue);
2546
+ const pnlText = hasMoney ? `Closed P&L: ${formatAmount(pnl)}` : `Closed ${positionHit.profit ? "+" : "\u2212"}${formatPrice(Math.abs(positionHit.price - entry.price))}`;
2547
+ centerText = hasMoney ? `${pnlText}, Qty: ${qtyText} RR ${rr.toFixed(2)}` : `${pnlText} RR ${rr.toFixed(2)}`;
2548
+ centerBg = positionHit.profit ? profitLine : lossLine;
2549
+ } else {
2550
+ centerText = hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`;
2551
+ centerBg = hexToRgba(drawing.color, 0.92);
2552
+ }
2553
+ drawPositionPill(centerText, cx, entryY, centerBg);
2486
2554
  if (drawing.label) {
2487
2555
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2488
2556
  }
@@ -3945,7 +4013,14 @@ function createChart(element, options = {}) {
3945
4013
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3946
4014
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3947
4015
  style: defaults.style ?? "solid",
3948
- width: defaults.width ?? 1
4016
+ width: defaults.width ?? 1,
4017
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
4018
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
4019
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
4020
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
4021
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
4022
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
4023
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3949
4024
  })
3950
4025
  );
3951
4026
  emitDrawingsChange();
@@ -4515,6 +4590,20 @@ function createChart(element, options = {}) {
4515
4590
  if (region === "outside") {
4516
4591
  return;
4517
4592
  }
4593
+ if (region === "plot" && !activeDrawingTool) {
4594
+ const drawingHit = getDrawingHit(point.x, point.y);
4595
+ if (drawingHit) {
4596
+ selectedDrawingId = drawingHit.drawing.id;
4597
+ drawingDoubleClickHandler?.({
4598
+ drawing: serializeDrawing(drawingHit.drawing),
4599
+ target: drawingHit.target,
4600
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4601
+ x: point.x,
4602
+ y: point.y
4603
+ });
4604
+ return;
4605
+ }
4606
+ }
4518
4607
  if (doubleClickAction === "placeLimitOrder") {
4519
4608
  if (region !== "plot") {
4520
4609
  return;
@@ -4825,6 +4914,9 @@ function createChart(element, options = {}) {
4825
4914
  const onDrawingSelect = (handler) => {
4826
4915
  drawingSelectHandler = handler;
4827
4916
  };
4917
+ const onDrawingDoubleClick = (handler) => {
4918
+ drawingDoubleClickHandler = handler;
4919
+ };
4828
4920
  const onDrawingHover = (handler) => {
4829
4921
  drawingHoverHandler = handler;
4830
4922
  };
@@ -4882,6 +4974,7 @@ function createChart(element, options = {}) {
4882
4974
  clearDrawings,
4883
4975
  onDrawingsChange,
4884
4976
  onDrawingSelect,
4977
+ onDrawingDoubleClick,
4885
4978
  setSelectedDrawing,
4886
4979
  onDrawingHover,
4887
4980
  setDrawingDefaults,
@@ -66,6 +66,13 @@ interface DrawingObjectOptions {
66
66
  width?: number;
67
67
  label?: string;
68
68
  locked?: boolean;
69
+ accountSize?: number;
70
+ lotSize?: number;
71
+ risk?: number;
72
+ riskMode?: "percent" | "amount";
73
+ leverage?: number;
74
+ pointValue?: number;
75
+ qtyPrecision?: number;
69
76
  }
70
77
  /** Default colors for position tools: [profit, loss, label text]. */
71
78
  declare const POSITION_DEFAULT_COLORS: string[];
@@ -84,7 +91,7 @@ interface DrawingHoverEvent {
84
91
  x: number;
85
92
  y: number;
86
93
  }
87
- type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width">>;
94
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision">>;
88
95
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
89
96
  id?: string;
90
97
  type: string;
@@ -420,6 +427,7 @@ interface ChartInstance {
420
427
  clearDrawings: () => void;
421
428
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
422
429
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
430
+ onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
423
431
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
424
432
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
425
433
  setSelectedDrawing: (id: string | null) => void;
@@ -935,6 +935,13 @@ function createChart(element, options = {}) {
935
935
  style: drawing.style ?? "dotted",
936
936
  width: Math.max(1, Number(drawing.width) || 1),
937
937
  locked: drawing.locked ?? false,
938
+ accountSize: Number(drawing.accountSize) || 0,
939
+ lotSize: Number(drawing.lotSize) || 1,
940
+ risk: Number(drawing.risk) || 0,
941
+ riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
942
+ leverage: Number(drawing.leverage) || 1,
943
+ pointValue: Number(drawing.pointValue) || 1,
944
+ qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
938
945
  ...drawing.label === void 0 ? {} : { label: drawing.label }
939
946
  });
940
947
  const serializeDrawing = (drawing) => ({
@@ -947,6 +954,13 @@ function createChart(element, options = {}) {
947
954
  style: drawing.style,
948
955
  width: drawing.width,
949
956
  locked: drawing.locked,
957
+ accountSize: drawing.accountSize,
958
+ lotSize: drawing.lotSize,
959
+ risk: drawing.risk,
960
+ riskMode: drawing.riskMode,
961
+ leverage: drawing.leverage,
962
+ pointValue: drawing.pointValue,
963
+ qtyPrecision: drawing.qtyPrecision,
950
964
  ...drawing.label === void 0 ? {} : { label: drawing.label }
951
965
  });
952
966
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -956,6 +970,7 @@ function createChart(element, options = {}) {
956
970
  let drawingDragState = null;
957
971
  let drawingsChangeHandler = null;
958
972
  let drawingSelectHandler = null;
973
+ let drawingDoubleClickHandler = null;
959
974
  let drawingHoverHandler = null;
960
975
  let lastHoveredDrawingId = null;
961
976
  let selectedDrawingId = null;
@@ -2403,6 +2418,40 @@ function createChart(element, options = {}) {
2403
2418
  ctx.fillStyle = lossFill;
2404
2419
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2405
2420
  ctx.restore();
2421
+ const isLongPosition = drawing.type === "long-position";
2422
+ const simStart = Math.max(0, Math.round(Math.min(entry.index, right.index)));
2423
+ const simEnd = Math.min(data.length - 1, Math.round(Math.max(entry.index, right.index)));
2424
+ let positionHit = null;
2425
+ for (let i = simStart; i <= simEnd; i += 1) {
2426
+ const bar = data[i];
2427
+ if (!bar) continue;
2428
+ const targetTouched = isLongPosition ? bar.h >= target.price : bar.l <= target.price;
2429
+ const stopTouched = isLongPosition ? bar.l <= stop.price : bar.h >= stop.price;
2430
+ if (stopTouched) {
2431
+ positionHit = { index: i, price: stop.price, profit: false };
2432
+ break;
2433
+ }
2434
+ if (targetTouched) {
2435
+ positionHit = { index: i, price: target.price, profit: true };
2436
+ break;
2437
+ }
2438
+ }
2439
+ if (positionHit) {
2440
+ const hitX = clamp(xFromDrawingPoint({ index: positionHit.index, price: positionHit.price }), boxX0, boxX1);
2441
+ const exitY = yFromPrice(positionHit.price);
2442
+ ctx.save();
2443
+ ctx.globalAlpha = draft ? 0.6 : 1;
2444
+ ctx.fillStyle = hexToRgba(positionHit.profit ? profitColor : lossColor, 0.2);
2445
+ ctx.fillRect(boxX0, Math.min(entryY, exitY), Math.max(0, hitX - boxX0), Math.abs(exitY - entryY));
2446
+ ctx.setLineDash([5, 4]);
2447
+ ctx.lineWidth = 1;
2448
+ ctx.strokeStyle = hexToRgba("#787b86", 0.9);
2449
+ ctx.beginPath();
2450
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2451
+ ctx.lineTo(crisp(hitX), crisp(exitY));
2452
+ ctx.stroke();
2453
+ ctx.restore();
2454
+ }
2406
2455
  if (isSelected) {
2407
2456
  ctx.save();
2408
2457
  ctx.setLineDash([]);
@@ -2437,6 +2486,14 @@ function createChart(element, options = {}) {
2437
2486
  const stopDist = Math.abs(entry.price - stop.price);
2438
2487
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2439
2488
  const cx = (boxX0 + boxX1) / 2;
2489
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2490
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2491
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2492
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2493
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2494
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2495
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2496
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2440
2497
  const drawPositionPill = (text, centerX, centerY, bg) => {
2441
2498
  const prevFont = ctx.font;
2442
2499
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2454,9 +2511,20 @@ function createChart(element, options = {}) {
2454
2511
  ctx.fillText(text, centerX, pillY + pillH / 2);
2455
2512
  ctx.font = prevFont;
2456
2513
  };
2457
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2458
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2459
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2514
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2515
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2516
+ let centerText;
2517
+ let centerBg;
2518
+ if (positionHit) {
2519
+ const pnl = positionHit.profit ? qtyRaw * targetDist * effectivePointValue : -(qtyRaw * stopDist * effectivePointValue);
2520
+ const pnlText = hasMoney ? `Closed P&L: ${formatAmount(pnl)}` : `Closed ${positionHit.profit ? "+" : "\u2212"}${formatPrice(Math.abs(positionHit.price - entry.price))}`;
2521
+ centerText = hasMoney ? `${pnlText}, Qty: ${qtyText} RR ${rr.toFixed(2)}` : `${pnlText} RR ${rr.toFixed(2)}`;
2522
+ centerBg = positionHit.profit ? profitLine : lossLine;
2523
+ } else {
2524
+ centerText = hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`;
2525
+ centerBg = hexToRgba(drawing.color, 0.92);
2526
+ }
2527
+ drawPositionPill(centerText, cx, entryY, centerBg);
2460
2528
  if (drawing.label) {
2461
2529
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2462
2530
  }
@@ -3919,7 +3987,14 @@ function createChart(element, options = {}) {
3919
3987
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3920
3988
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3921
3989
  style: defaults.style ?? "solid",
3922
- width: defaults.width ?? 1
3990
+ width: defaults.width ?? 1,
3991
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
3992
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
3993
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
3994
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
3995
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
3996
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
3997
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3923
3998
  })
3924
3999
  );
3925
4000
  emitDrawingsChange();
@@ -4489,6 +4564,20 @@ function createChart(element, options = {}) {
4489
4564
  if (region === "outside") {
4490
4565
  return;
4491
4566
  }
4567
+ if (region === "plot" && !activeDrawingTool) {
4568
+ const drawingHit = getDrawingHit(point.x, point.y);
4569
+ if (drawingHit) {
4570
+ selectedDrawingId = drawingHit.drawing.id;
4571
+ drawingDoubleClickHandler?.({
4572
+ drawing: serializeDrawing(drawingHit.drawing),
4573
+ target: drawingHit.target,
4574
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4575
+ x: point.x,
4576
+ y: point.y
4577
+ });
4578
+ return;
4579
+ }
4580
+ }
4492
4581
  if (doubleClickAction === "placeLimitOrder") {
4493
4582
  if (region !== "plot") {
4494
4583
  return;
@@ -4799,6 +4888,9 @@ function createChart(element, options = {}) {
4799
4888
  const onDrawingSelect = (handler) => {
4800
4889
  drawingSelectHandler = handler;
4801
4890
  };
4891
+ const onDrawingDoubleClick = (handler) => {
4892
+ drawingDoubleClickHandler = handler;
4893
+ };
4802
4894
  const onDrawingHover = (handler) => {
4803
4895
  drawingHoverHandler = handler;
4804
4896
  };
@@ -4856,6 +4948,7 @@ function createChart(element, options = {}) {
4856
4948
  clearDrawings,
4857
4949
  onDrawingsChange,
4858
4950
  onDrawingSelect,
4951
+ onDrawingDoubleClick,
4859
4952
  setSelectedDrawing,
4860
4953
  onDrawingHover,
4861
4954
  setDrawingDefaults,
package/dist/index.cjs CHANGED
@@ -961,6 +961,13 @@ function createChart(element, options = {}) {
961
961
  style: drawing.style ?? "dotted",
962
962
  width: Math.max(1, Number(drawing.width) || 1),
963
963
  locked: drawing.locked ?? false,
964
+ accountSize: Number(drawing.accountSize) || 0,
965
+ lotSize: Number(drawing.lotSize) || 1,
966
+ risk: Number(drawing.risk) || 0,
967
+ riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
968
+ leverage: Number(drawing.leverage) || 1,
969
+ pointValue: Number(drawing.pointValue) || 1,
970
+ qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
964
971
  ...drawing.label === void 0 ? {} : { label: drawing.label }
965
972
  });
966
973
  const serializeDrawing = (drawing) => ({
@@ -973,6 +980,13 @@ function createChart(element, options = {}) {
973
980
  style: drawing.style,
974
981
  width: drawing.width,
975
982
  locked: drawing.locked,
983
+ accountSize: drawing.accountSize,
984
+ lotSize: drawing.lotSize,
985
+ risk: drawing.risk,
986
+ riskMode: drawing.riskMode,
987
+ leverage: drawing.leverage,
988
+ pointValue: drawing.pointValue,
989
+ qtyPrecision: drawing.qtyPrecision,
976
990
  ...drawing.label === void 0 ? {} : { label: drawing.label }
977
991
  });
978
992
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -982,6 +996,7 @@ function createChart(element, options = {}) {
982
996
  let drawingDragState = null;
983
997
  let drawingsChangeHandler = null;
984
998
  let drawingSelectHandler = null;
999
+ let drawingDoubleClickHandler = null;
985
1000
  let drawingHoverHandler = null;
986
1001
  let lastHoveredDrawingId = null;
987
1002
  let selectedDrawingId = null;
@@ -2429,6 +2444,40 @@ function createChart(element, options = {}) {
2429
2444
  ctx.fillStyle = lossFill;
2430
2445
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2431
2446
  ctx.restore();
2447
+ const isLongPosition = drawing.type === "long-position";
2448
+ const simStart = Math.max(0, Math.round(Math.min(entry.index, right.index)));
2449
+ const simEnd = Math.min(data.length - 1, Math.round(Math.max(entry.index, right.index)));
2450
+ let positionHit = null;
2451
+ for (let i = simStart; i <= simEnd; i += 1) {
2452
+ const bar = data[i];
2453
+ if (!bar) continue;
2454
+ const targetTouched = isLongPosition ? bar.h >= target.price : bar.l <= target.price;
2455
+ const stopTouched = isLongPosition ? bar.l <= stop.price : bar.h >= stop.price;
2456
+ if (stopTouched) {
2457
+ positionHit = { index: i, price: stop.price, profit: false };
2458
+ break;
2459
+ }
2460
+ if (targetTouched) {
2461
+ positionHit = { index: i, price: target.price, profit: true };
2462
+ break;
2463
+ }
2464
+ }
2465
+ if (positionHit) {
2466
+ const hitX = clamp(xFromDrawingPoint({ index: positionHit.index, price: positionHit.price }), boxX0, boxX1);
2467
+ const exitY = yFromPrice(positionHit.price);
2468
+ ctx.save();
2469
+ ctx.globalAlpha = draft ? 0.6 : 1;
2470
+ ctx.fillStyle = hexToRgba(positionHit.profit ? profitColor : lossColor, 0.2);
2471
+ ctx.fillRect(boxX0, Math.min(entryY, exitY), Math.max(0, hitX - boxX0), Math.abs(exitY - entryY));
2472
+ ctx.setLineDash([5, 4]);
2473
+ ctx.lineWidth = 1;
2474
+ ctx.strokeStyle = hexToRgba("#787b86", 0.9);
2475
+ ctx.beginPath();
2476
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2477
+ ctx.lineTo(crisp(hitX), crisp(exitY));
2478
+ ctx.stroke();
2479
+ ctx.restore();
2480
+ }
2432
2481
  if (isSelected) {
2433
2482
  ctx.save();
2434
2483
  ctx.setLineDash([]);
@@ -2463,6 +2512,14 @@ function createChart(element, options = {}) {
2463
2512
  const stopDist = Math.abs(entry.price - stop.price);
2464
2513
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2465
2514
  const cx = (boxX0 + boxX1) / 2;
2515
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2516
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2517
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2518
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2519
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2520
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2521
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2522
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2466
2523
  const drawPositionPill = (text, centerX, centerY, bg) => {
2467
2524
  const prevFont = ctx.font;
2468
2525
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2480,9 +2537,20 @@ function createChart(element, options = {}) {
2480
2537
  ctx.fillText(text, centerX, pillY + pillH / 2);
2481
2538
  ctx.font = prevFont;
2482
2539
  };
2483
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2484
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2485
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2540
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2541
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2542
+ let centerText;
2543
+ let centerBg;
2544
+ if (positionHit) {
2545
+ const pnl = positionHit.profit ? qtyRaw * targetDist * effectivePointValue : -(qtyRaw * stopDist * effectivePointValue);
2546
+ const pnlText = hasMoney ? `Closed P&L: ${formatAmount(pnl)}` : `Closed ${positionHit.profit ? "+" : "\u2212"}${formatPrice(Math.abs(positionHit.price - entry.price))}`;
2547
+ centerText = hasMoney ? `${pnlText}, Qty: ${qtyText} RR ${rr.toFixed(2)}` : `${pnlText} RR ${rr.toFixed(2)}`;
2548
+ centerBg = positionHit.profit ? profitLine : lossLine;
2549
+ } else {
2550
+ centerText = hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`;
2551
+ centerBg = hexToRgba(drawing.color, 0.92);
2552
+ }
2553
+ drawPositionPill(centerText, cx, entryY, centerBg);
2486
2554
  if (drawing.label) {
2487
2555
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2488
2556
  }
@@ -3945,7 +4013,14 @@ function createChart(element, options = {}) {
3945
4013
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3946
4014
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3947
4015
  style: defaults.style ?? "solid",
3948
- width: defaults.width ?? 1
4016
+ width: defaults.width ?? 1,
4017
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
4018
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
4019
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
4020
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
4021
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
4022
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
4023
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3949
4024
  })
3950
4025
  );
3951
4026
  emitDrawingsChange();
@@ -4515,6 +4590,20 @@ function createChart(element, options = {}) {
4515
4590
  if (region === "outside") {
4516
4591
  return;
4517
4592
  }
4593
+ if (region === "plot" && !activeDrawingTool) {
4594
+ const drawingHit = getDrawingHit(point.x, point.y);
4595
+ if (drawingHit) {
4596
+ selectedDrawingId = drawingHit.drawing.id;
4597
+ drawingDoubleClickHandler?.({
4598
+ drawing: serializeDrawing(drawingHit.drawing),
4599
+ target: drawingHit.target,
4600
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4601
+ x: point.x,
4602
+ y: point.y
4603
+ });
4604
+ return;
4605
+ }
4606
+ }
4518
4607
  if (doubleClickAction === "placeLimitOrder") {
4519
4608
  if (region !== "plot") {
4520
4609
  return;
@@ -4825,6 +4914,9 @@ function createChart(element, options = {}) {
4825
4914
  const onDrawingSelect = (handler) => {
4826
4915
  drawingSelectHandler = handler;
4827
4916
  };
4917
+ const onDrawingDoubleClick = (handler) => {
4918
+ drawingDoubleClickHandler = handler;
4919
+ };
4828
4920
  const onDrawingHover = (handler) => {
4829
4921
  drawingHoverHandler = handler;
4830
4922
  };
@@ -4882,6 +4974,7 @@ function createChart(element, options = {}) {
4882
4974
  clearDrawings,
4883
4975
  onDrawingsChange,
4884
4976
  onDrawingSelect,
4977
+ onDrawingDoubleClick,
4885
4978
  setSelectedDrawing,
4886
4979
  onDrawingHover,
4887
4980
  setDrawingDefaults,
package/dist/index.d.cts CHANGED
@@ -66,6 +66,13 @@ interface DrawingObjectOptions {
66
66
  width?: number;
67
67
  label?: string;
68
68
  locked?: boolean;
69
+ accountSize?: number;
70
+ lotSize?: number;
71
+ risk?: number;
72
+ riskMode?: "percent" | "amount";
73
+ leverage?: number;
74
+ pointValue?: number;
75
+ qtyPrecision?: number;
69
76
  }
70
77
  /** Default colors for position tools: [profit, loss, label text]. */
71
78
  declare const POSITION_DEFAULT_COLORS: string[];
@@ -84,7 +91,7 @@ interface DrawingHoverEvent {
84
91
  x: number;
85
92
  y: number;
86
93
  }
87
- type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width">>;
94
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision">>;
88
95
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
89
96
  id?: string;
90
97
  type: string;
@@ -420,6 +427,7 @@ interface ChartInstance {
420
427
  clearDrawings: () => void;
421
428
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
422
429
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
430
+ onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
423
431
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
424
432
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
425
433
  setSelectedDrawing: (id: string | null) => void;
package/dist/index.d.ts CHANGED
@@ -66,6 +66,13 @@ interface DrawingObjectOptions {
66
66
  width?: number;
67
67
  label?: string;
68
68
  locked?: boolean;
69
+ accountSize?: number;
70
+ lotSize?: number;
71
+ risk?: number;
72
+ riskMode?: "percent" | "amount";
73
+ leverage?: number;
74
+ pointValue?: number;
75
+ qtyPrecision?: number;
69
76
  }
70
77
  /** Default colors for position tools: [profit, loss, label text]. */
71
78
  declare const POSITION_DEFAULT_COLORS: string[];
@@ -84,7 +91,7 @@ interface DrawingHoverEvent {
84
91
  x: number;
85
92
  y: number;
86
93
  }
87
- type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width">>;
94
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision">>;
88
95
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
89
96
  id?: string;
90
97
  type: string;
@@ -420,6 +427,7 @@ interface ChartInstance {
420
427
  clearDrawings: () => void;
421
428
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
422
429
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
430
+ onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
423
431
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
424
432
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
425
433
  setSelectedDrawing: (id: string | null) => void;
package/dist/index.js CHANGED
@@ -935,6 +935,13 @@ function createChart(element, options = {}) {
935
935
  style: drawing.style ?? "dotted",
936
936
  width: Math.max(1, Number(drawing.width) || 1),
937
937
  locked: drawing.locked ?? false,
938
+ accountSize: Number(drawing.accountSize) || 0,
939
+ lotSize: Number(drawing.lotSize) || 1,
940
+ risk: Number(drawing.risk) || 0,
941
+ riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
942
+ leverage: Number(drawing.leverage) || 1,
943
+ pointValue: Number(drawing.pointValue) || 1,
944
+ qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
938
945
  ...drawing.label === void 0 ? {} : { label: drawing.label }
939
946
  });
940
947
  const serializeDrawing = (drawing) => ({
@@ -947,6 +954,13 @@ function createChart(element, options = {}) {
947
954
  style: drawing.style,
948
955
  width: drawing.width,
949
956
  locked: drawing.locked,
957
+ accountSize: drawing.accountSize,
958
+ lotSize: drawing.lotSize,
959
+ risk: drawing.risk,
960
+ riskMode: drawing.riskMode,
961
+ leverage: drawing.leverage,
962
+ pointValue: drawing.pointValue,
963
+ qtyPrecision: drawing.qtyPrecision,
950
964
  ...drawing.label === void 0 ? {} : { label: drawing.label }
951
965
  });
952
966
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -956,6 +970,7 @@ function createChart(element, options = {}) {
956
970
  let drawingDragState = null;
957
971
  let drawingsChangeHandler = null;
958
972
  let drawingSelectHandler = null;
973
+ let drawingDoubleClickHandler = null;
959
974
  let drawingHoverHandler = null;
960
975
  let lastHoveredDrawingId = null;
961
976
  let selectedDrawingId = null;
@@ -2403,6 +2418,40 @@ function createChart(element, options = {}) {
2403
2418
  ctx.fillStyle = lossFill;
2404
2419
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2405
2420
  ctx.restore();
2421
+ const isLongPosition = drawing.type === "long-position";
2422
+ const simStart = Math.max(0, Math.round(Math.min(entry.index, right.index)));
2423
+ const simEnd = Math.min(data.length - 1, Math.round(Math.max(entry.index, right.index)));
2424
+ let positionHit = null;
2425
+ for (let i = simStart; i <= simEnd; i += 1) {
2426
+ const bar = data[i];
2427
+ if (!bar) continue;
2428
+ const targetTouched = isLongPosition ? bar.h >= target.price : bar.l <= target.price;
2429
+ const stopTouched = isLongPosition ? bar.l <= stop.price : bar.h >= stop.price;
2430
+ if (stopTouched) {
2431
+ positionHit = { index: i, price: stop.price, profit: false };
2432
+ break;
2433
+ }
2434
+ if (targetTouched) {
2435
+ positionHit = { index: i, price: target.price, profit: true };
2436
+ break;
2437
+ }
2438
+ }
2439
+ if (positionHit) {
2440
+ const hitX = clamp(xFromDrawingPoint({ index: positionHit.index, price: positionHit.price }), boxX0, boxX1);
2441
+ const exitY = yFromPrice(positionHit.price);
2442
+ ctx.save();
2443
+ ctx.globalAlpha = draft ? 0.6 : 1;
2444
+ ctx.fillStyle = hexToRgba(positionHit.profit ? profitColor : lossColor, 0.2);
2445
+ ctx.fillRect(boxX0, Math.min(entryY, exitY), Math.max(0, hitX - boxX0), Math.abs(exitY - entryY));
2446
+ ctx.setLineDash([5, 4]);
2447
+ ctx.lineWidth = 1;
2448
+ ctx.strokeStyle = hexToRgba("#787b86", 0.9);
2449
+ ctx.beginPath();
2450
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2451
+ ctx.lineTo(crisp(hitX), crisp(exitY));
2452
+ ctx.stroke();
2453
+ ctx.restore();
2454
+ }
2406
2455
  if (isSelected) {
2407
2456
  ctx.save();
2408
2457
  ctx.setLineDash([]);
@@ -2437,6 +2486,14 @@ function createChart(element, options = {}) {
2437
2486
  const stopDist = Math.abs(entry.price - stop.price);
2438
2487
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2439
2488
  const cx = (boxX0 + boxX1) / 2;
2489
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2490
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2491
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2492
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2493
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2494
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2495
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2496
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2440
2497
  const drawPositionPill = (text, centerX, centerY, bg) => {
2441
2498
  const prevFont = ctx.font;
2442
2499
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2454,9 +2511,20 @@ function createChart(element, options = {}) {
2454
2511
  ctx.fillText(text, centerX, pillY + pillH / 2);
2455
2512
  ctx.font = prevFont;
2456
2513
  };
2457
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2458
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2459
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2514
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2515
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2516
+ let centerText;
2517
+ let centerBg;
2518
+ if (positionHit) {
2519
+ const pnl = positionHit.profit ? qtyRaw * targetDist * effectivePointValue : -(qtyRaw * stopDist * effectivePointValue);
2520
+ const pnlText = hasMoney ? `Closed P&L: ${formatAmount(pnl)}` : `Closed ${positionHit.profit ? "+" : "\u2212"}${formatPrice(Math.abs(positionHit.price - entry.price))}`;
2521
+ centerText = hasMoney ? `${pnlText}, Qty: ${qtyText} RR ${rr.toFixed(2)}` : `${pnlText} RR ${rr.toFixed(2)}`;
2522
+ centerBg = positionHit.profit ? profitLine : lossLine;
2523
+ } else {
2524
+ centerText = hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`;
2525
+ centerBg = hexToRgba(drawing.color, 0.92);
2526
+ }
2527
+ drawPositionPill(centerText, cx, entryY, centerBg);
2460
2528
  if (drawing.label) {
2461
2529
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2462
2530
  }
@@ -3919,7 +3987,14 @@ function createChart(element, options = {}) {
3919
3987
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3920
3988
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3921
3989
  style: defaults.style ?? "solid",
3922
- width: defaults.width ?? 1
3990
+ width: defaults.width ?? 1,
3991
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
3992
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
3993
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
3994
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
3995
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
3996
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
3997
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3923
3998
  })
3924
3999
  );
3925
4000
  emitDrawingsChange();
@@ -4489,6 +4564,20 @@ function createChart(element, options = {}) {
4489
4564
  if (region === "outside") {
4490
4565
  return;
4491
4566
  }
4567
+ if (region === "plot" && !activeDrawingTool) {
4568
+ const drawingHit = getDrawingHit(point.x, point.y);
4569
+ if (drawingHit) {
4570
+ selectedDrawingId = drawingHit.drawing.id;
4571
+ drawingDoubleClickHandler?.({
4572
+ drawing: serializeDrawing(drawingHit.drawing),
4573
+ target: drawingHit.target,
4574
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4575
+ x: point.x,
4576
+ y: point.y
4577
+ });
4578
+ return;
4579
+ }
4580
+ }
4492
4581
  if (doubleClickAction === "placeLimitOrder") {
4493
4582
  if (region !== "plot") {
4494
4583
  return;
@@ -4799,6 +4888,9 @@ function createChart(element, options = {}) {
4799
4888
  const onDrawingSelect = (handler) => {
4800
4889
  drawingSelectHandler = handler;
4801
4890
  };
4891
+ const onDrawingDoubleClick = (handler) => {
4892
+ drawingDoubleClickHandler = handler;
4893
+ };
4802
4894
  const onDrawingHover = (handler) => {
4803
4895
  drawingHoverHandler = handler;
4804
4896
  };
@@ -4856,6 +4948,7 @@ function createChart(element, options = {}) {
4856
4948
  clearDrawings,
4857
4949
  onDrawingsChange,
4858
4950
  onDrawingSelect,
4951
+ onDrawingDoubleClick,
4859
4952
  setSelectedDrawing,
4860
4953
  onDrawingHover,
4861
4954
  setDrawingDefaults,
package/docs/API.md CHANGED
@@ -422,7 +422,7 @@ Drawings are user-created chart tools, separate from indicators. They are intera
422
422
  - `ray`: two-point line that extends infinitely past the second point
423
423
  - `fib-retracement`: two-point retracement with levels/bands
424
424
  - `fib-extension`: trend-based three-point extension (click trend start, trend end, then the projection origin); levels project from the third point by the first→second move
425
- - `long-position` / `short-position`: risk/reward forecasting box (single click drops a default box). Points are `[entry, target, stop, rightEdge]`; anchors are target/entry/stop on the left and a time-width handle on the right. Labels show price, % move, ticks, and risk/reward ratio. `color` sets the entry line; `colors` is `[profitColor, lossColor, labelTextColor]` (defaults `POSITION_DEFAULT_COLORS`).
425
+ - `long-position` / `short-position`: risk/reward forecasting box (single click drops a default box). Points are `[entry, target, stop, rightEdge]`; anchors are target/entry/stop on the left and a time-width handle on the right. Labels show price, % move, ticks, and risk/reward ratio. `color` sets the entry line; `colors` is `[profitColor, lossColor, labelTextColor]` (defaults `POSITION_DEFAULT_COLORS`). Sizing inputs `accountSize`, `lotSize`, `risk`, `riskMode` (`"percent"|"amount"`), `leverage`, `pointValue`, `qtyPrecision` drive the Qty/Amount labels — set `pointValue` (contract $/point) from your app for real money values. The box also runs a trade simulation across the bars it covers: it shades the traversed region and draws a diagonal to the first bar that touches the target or stop, and the center label switches to "Closed P&L" (green if the target was hit first, red if the stop was hit first).
426
426
  - `points: DrawingPoint[]`
427
427
  - `visible?: boolean`
428
428
  - `color?: string`
@@ -482,6 +482,7 @@ Use `getDrawings()` / `setDrawings()` for persistence.
482
482
  - `fitContent(): void` (x-only fit, keeps y zoom)
483
483
  - `resetViewport(): void` (fit x + reset y auto-scale)
484
484
  - `setSelectedDrawing(id: string | null): void` (marks a drawing selected; only the selected/drafted drawing renders its handles, and position tools render their lines/labels only while selected)
485
+ - `onDrawingDoubleClick(handler): void` (fires when a drawing is double-clicked; use it to open a settings dialog, e.g. for position tools)
485
486
  - `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
486
487
  - `getActiveDrawingTool(): DrawingToolType | null`
487
488
  - `setDrawings(drawings: DrawingObjectOptions[]): void`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",