hyperprop-charting-library 0.1.79 → 0.1.81

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,20 +2444,6 @@ 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();
2432
- if (data.length > 0) {
2433
- const nowX = xFromDrawingPoint({ index: data.length - 1, price: 0 });
2434
- const darkRight = clamp(nowX, boxX0, boxX1);
2435
- if (darkRight - boxX0 > 0.5) {
2436
- const darkW = darkRight - boxX0;
2437
- ctx.save();
2438
- ctx.globalAlpha = draft ? 0.5 : 1;
2439
- ctx.fillStyle = hexToRgba(profitColor, 0.18);
2440
- ctx.fillRect(boxX0, Math.min(entryY, targetY), darkW, Math.abs(targetY - entryY));
2441
- ctx.fillStyle = hexToRgba(lossColor, 0.18);
2442
- ctx.fillRect(boxX0, Math.min(entryY, stopY), darkW, Math.abs(stopY - entryY));
2443
- ctx.restore();
2444
- }
2445
- }
2446
2447
  if (isSelected) {
2447
2448
  ctx.save();
2448
2449
  ctx.setLineDash([]);
@@ -2477,6 +2478,14 @@ function createChart(element, options = {}) {
2477
2478
  const stopDist = Math.abs(entry.price - stop.price);
2478
2479
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2479
2480
  const cx = (boxX0 + boxX1) / 2;
2481
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2482
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2483
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2484
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2485
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2486
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2487
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2488
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2480
2489
  const drawPositionPill = (text, centerX, centerY, bg) => {
2481
2490
  const prevFont = ctx.font;
2482
2491
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2494,9 +2503,14 @@ function createChart(element, options = {}) {
2494
2503
  ctx.fillText(text, centerX, pillY + pillH / 2);
2495
2504
  ctx.font = prevFont;
2496
2505
  };
2497
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2498
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2499
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2506
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2507
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2508
+ drawPositionPill(
2509
+ hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`,
2510
+ cx,
2511
+ entryY,
2512
+ hexToRgba(drawing.color, 0.92)
2513
+ );
2500
2514
  if (drawing.label) {
2501
2515
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2502
2516
  }
@@ -3959,7 +3973,14 @@ function createChart(element, options = {}) {
3959
3973
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3960
3974
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3961
3975
  style: defaults.style ?? "solid",
3962
- width: defaults.width ?? 1
3976
+ width: defaults.width ?? 1,
3977
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
3978
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
3979
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
3980
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
3981
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
3982
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
3983
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3963
3984
  })
3964
3985
  );
3965
3986
  emitDrawingsChange();
@@ -4529,6 +4550,20 @@ function createChart(element, options = {}) {
4529
4550
  if (region === "outside") {
4530
4551
  return;
4531
4552
  }
4553
+ if (region === "plot" && !activeDrawingTool) {
4554
+ const drawingHit = getDrawingHit(point.x, point.y);
4555
+ if (drawingHit) {
4556
+ selectedDrawingId = drawingHit.drawing.id;
4557
+ drawingDoubleClickHandler?.({
4558
+ drawing: serializeDrawing(drawingHit.drawing),
4559
+ target: drawingHit.target,
4560
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4561
+ x: point.x,
4562
+ y: point.y
4563
+ });
4564
+ return;
4565
+ }
4566
+ }
4532
4567
  if (doubleClickAction === "placeLimitOrder") {
4533
4568
  if (region !== "plot") {
4534
4569
  return;
@@ -4839,6 +4874,9 @@ function createChart(element, options = {}) {
4839
4874
  const onDrawingSelect = (handler) => {
4840
4875
  drawingSelectHandler = handler;
4841
4876
  };
4877
+ const onDrawingDoubleClick = (handler) => {
4878
+ drawingDoubleClickHandler = handler;
4879
+ };
4842
4880
  const onDrawingHover = (handler) => {
4843
4881
  drawingHoverHandler = handler;
4844
4882
  };
@@ -4896,6 +4934,7 @@ function createChart(element, options = {}) {
4896
4934
  clearDrawings,
4897
4935
  onDrawingsChange,
4898
4936
  onDrawingSelect,
4937
+ onDrawingDoubleClick,
4899
4938
  setSelectedDrawing,
4900
4939
  onDrawingHover,
4901
4940
  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,20 +2418,6 @@ 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();
2406
- if (data.length > 0) {
2407
- const nowX = xFromDrawingPoint({ index: data.length - 1, price: 0 });
2408
- const darkRight = clamp(nowX, boxX0, boxX1);
2409
- if (darkRight - boxX0 > 0.5) {
2410
- const darkW = darkRight - boxX0;
2411
- ctx.save();
2412
- ctx.globalAlpha = draft ? 0.5 : 1;
2413
- ctx.fillStyle = hexToRgba(profitColor, 0.18);
2414
- ctx.fillRect(boxX0, Math.min(entryY, targetY), darkW, Math.abs(targetY - entryY));
2415
- ctx.fillStyle = hexToRgba(lossColor, 0.18);
2416
- ctx.fillRect(boxX0, Math.min(entryY, stopY), darkW, Math.abs(stopY - entryY));
2417
- ctx.restore();
2418
- }
2419
- }
2420
2421
  if (isSelected) {
2421
2422
  ctx.save();
2422
2423
  ctx.setLineDash([]);
@@ -2451,6 +2452,14 @@ function createChart(element, options = {}) {
2451
2452
  const stopDist = Math.abs(entry.price - stop.price);
2452
2453
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2453
2454
  const cx = (boxX0 + boxX1) / 2;
2455
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2456
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2457
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2458
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2459
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2460
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2461
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2462
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2454
2463
  const drawPositionPill = (text, centerX, centerY, bg) => {
2455
2464
  const prevFont = ctx.font;
2456
2465
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2468,9 +2477,14 @@ function createChart(element, options = {}) {
2468
2477
  ctx.fillText(text, centerX, pillY + pillH / 2);
2469
2478
  ctx.font = prevFont;
2470
2479
  };
2471
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2472
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2473
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2480
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2481
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2482
+ drawPositionPill(
2483
+ hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`,
2484
+ cx,
2485
+ entryY,
2486
+ hexToRgba(drawing.color, 0.92)
2487
+ );
2474
2488
  if (drawing.label) {
2475
2489
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2476
2490
  }
@@ -3933,7 +3947,14 @@ function createChart(element, options = {}) {
3933
3947
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3934
3948
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3935
3949
  style: defaults.style ?? "solid",
3936
- width: defaults.width ?? 1
3950
+ width: defaults.width ?? 1,
3951
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
3952
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
3953
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
3954
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
3955
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
3956
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
3957
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3937
3958
  })
3938
3959
  );
3939
3960
  emitDrawingsChange();
@@ -4503,6 +4524,20 @@ function createChart(element, options = {}) {
4503
4524
  if (region === "outside") {
4504
4525
  return;
4505
4526
  }
4527
+ if (region === "plot" && !activeDrawingTool) {
4528
+ const drawingHit = getDrawingHit(point.x, point.y);
4529
+ if (drawingHit) {
4530
+ selectedDrawingId = drawingHit.drawing.id;
4531
+ drawingDoubleClickHandler?.({
4532
+ drawing: serializeDrawing(drawingHit.drawing),
4533
+ target: drawingHit.target,
4534
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4535
+ x: point.x,
4536
+ y: point.y
4537
+ });
4538
+ return;
4539
+ }
4540
+ }
4506
4541
  if (doubleClickAction === "placeLimitOrder") {
4507
4542
  if (region !== "plot") {
4508
4543
  return;
@@ -4813,6 +4848,9 @@ function createChart(element, options = {}) {
4813
4848
  const onDrawingSelect = (handler) => {
4814
4849
  drawingSelectHandler = handler;
4815
4850
  };
4851
+ const onDrawingDoubleClick = (handler) => {
4852
+ drawingDoubleClickHandler = handler;
4853
+ };
4816
4854
  const onDrawingHover = (handler) => {
4817
4855
  drawingHoverHandler = handler;
4818
4856
  };
@@ -4870,6 +4908,7 @@ function createChart(element, options = {}) {
4870
4908
  clearDrawings,
4871
4909
  onDrawingsChange,
4872
4910
  onDrawingSelect,
4911
+ onDrawingDoubleClick,
4873
4912
  setSelectedDrawing,
4874
4913
  onDrawingHover,
4875
4914
  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,20 +2444,6 @@ 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();
2432
- if (data.length > 0) {
2433
- const nowX = xFromDrawingPoint({ index: data.length - 1, price: 0 });
2434
- const darkRight = clamp(nowX, boxX0, boxX1);
2435
- if (darkRight - boxX0 > 0.5) {
2436
- const darkW = darkRight - boxX0;
2437
- ctx.save();
2438
- ctx.globalAlpha = draft ? 0.5 : 1;
2439
- ctx.fillStyle = hexToRgba(profitColor, 0.18);
2440
- ctx.fillRect(boxX0, Math.min(entryY, targetY), darkW, Math.abs(targetY - entryY));
2441
- ctx.fillStyle = hexToRgba(lossColor, 0.18);
2442
- ctx.fillRect(boxX0, Math.min(entryY, stopY), darkW, Math.abs(stopY - entryY));
2443
- ctx.restore();
2444
- }
2445
- }
2446
2447
  if (isSelected) {
2447
2448
  ctx.save();
2448
2449
  ctx.setLineDash([]);
@@ -2477,6 +2478,14 @@ function createChart(element, options = {}) {
2477
2478
  const stopDist = Math.abs(entry.price - stop.price);
2478
2479
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2479
2480
  const cx = (boxX0 + boxX1) / 2;
2481
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2482
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2483
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2484
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2485
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2486
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2487
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2488
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2480
2489
  const drawPositionPill = (text, centerX, centerY, bg) => {
2481
2490
  const prevFont = ctx.font;
2482
2491
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2494,9 +2503,14 @@ function createChart(element, options = {}) {
2494
2503
  ctx.fillText(text, centerX, pillY + pillH / 2);
2495
2504
  ctx.font = prevFont;
2496
2505
  };
2497
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2498
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2499
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2506
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2507
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2508
+ drawPositionPill(
2509
+ hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`,
2510
+ cx,
2511
+ entryY,
2512
+ hexToRgba(drawing.color, 0.92)
2513
+ );
2500
2514
  if (drawing.label) {
2501
2515
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2502
2516
  }
@@ -3959,7 +3973,14 @@ function createChart(element, options = {}) {
3959
3973
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3960
3974
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3961
3975
  style: defaults.style ?? "solid",
3962
- width: defaults.width ?? 1
3976
+ width: defaults.width ?? 1,
3977
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
3978
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
3979
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
3980
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
3981
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
3982
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
3983
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3963
3984
  })
3964
3985
  );
3965
3986
  emitDrawingsChange();
@@ -4529,6 +4550,20 @@ function createChart(element, options = {}) {
4529
4550
  if (region === "outside") {
4530
4551
  return;
4531
4552
  }
4553
+ if (region === "plot" && !activeDrawingTool) {
4554
+ const drawingHit = getDrawingHit(point.x, point.y);
4555
+ if (drawingHit) {
4556
+ selectedDrawingId = drawingHit.drawing.id;
4557
+ drawingDoubleClickHandler?.({
4558
+ drawing: serializeDrawing(drawingHit.drawing),
4559
+ target: drawingHit.target,
4560
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4561
+ x: point.x,
4562
+ y: point.y
4563
+ });
4564
+ return;
4565
+ }
4566
+ }
4532
4567
  if (doubleClickAction === "placeLimitOrder") {
4533
4568
  if (region !== "plot") {
4534
4569
  return;
@@ -4839,6 +4874,9 @@ function createChart(element, options = {}) {
4839
4874
  const onDrawingSelect = (handler) => {
4840
4875
  drawingSelectHandler = handler;
4841
4876
  };
4877
+ const onDrawingDoubleClick = (handler) => {
4878
+ drawingDoubleClickHandler = handler;
4879
+ };
4842
4880
  const onDrawingHover = (handler) => {
4843
4881
  drawingHoverHandler = handler;
4844
4882
  };
@@ -4896,6 +4934,7 @@ function createChart(element, options = {}) {
4896
4934
  clearDrawings,
4897
4935
  onDrawingsChange,
4898
4936
  onDrawingSelect,
4937
+ onDrawingDoubleClick,
4899
4938
  setSelectedDrawing,
4900
4939
  onDrawingHover,
4901
4940
  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,20 +2418,6 @@ 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();
2406
- if (data.length > 0) {
2407
- const nowX = xFromDrawingPoint({ index: data.length - 1, price: 0 });
2408
- const darkRight = clamp(nowX, boxX0, boxX1);
2409
- if (darkRight - boxX0 > 0.5) {
2410
- const darkW = darkRight - boxX0;
2411
- ctx.save();
2412
- ctx.globalAlpha = draft ? 0.5 : 1;
2413
- ctx.fillStyle = hexToRgba(profitColor, 0.18);
2414
- ctx.fillRect(boxX0, Math.min(entryY, targetY), darkW, Math.abs(targetY - entryY));
2415
- ctx.fillStyle = hexToRgba(lossColor, 0.18);
2416
- ctx.fillRect(boxX0, Math.min(entryY, stopY), darkW, Math.abs(stopY - entryY));
2417
- ctx.restore();
2418
- }
2419
- }
2420
2421
  if (isSelected) {
2421
2422
  ctx.save();
2422
2423
  ctx.setLineDash([]);
@@ -2451,6 +2452,14 @@ function createChart(element, options = {}) {
2451
2452
  const stopDist = Math.abs(entry.price - stop.price);
2452
2453
  const rr = stopDist > 0 ? targetDist / stopDist : 0;
2453
2454
  const cx = (boxX0 + boxX1) / 2;
2455
+ const effectivePointValue = (drawing.pointValue > 0 ? drawing.pointValue : 1) * (drawing.lotSize > 0 ? drawing.lotSize : 1);
2456
+ const riskAmount = drawing.riskMode === "amount" ? drawing.risk : drawing.accountSize * (drawing.risk / 100);
2457
+ const qtyRaw = riskAmount > 0 && stopDist > 0 ? riskAmount / (stopDist * effectivePointValue) : 0;
2458
+ const hasMoney = qtyRaw > 0 && Number.isFinite(qtyRaw);
2459
+ const qtyText = hasMoney ? qtyRaw.toFixed(Math.max(0, drawing.qtyPrecision)) : "";
2460
+ const formatAmount = (value) => Math.abs(value) >= 1e3 ? value.toFixed(0) : value.toFixed(2);
2461
+ const targetAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * targetDist * effectivePointValue)}` : "";
2462
+ const stopAmountText = hasMoney ? `, Amount: ${formatAmount(qtyRaw * stopDist * effectivePointValue)}` : "";
2454
2463
  const drawPositionPill = (text, centerX, centerY, bg) => {
2455
2464
  const prevFont = ctx.font;
2456
2465
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
@@ -2468,9 +2477,14 @@ function createChart(element, options = {}) {
2468
2477
  ctx.fillText(text, centerX, pillY + pillH / 2);
2469
2478
  ctx.font = prevFont;
2470
2479
  };
2471
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2472
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2473
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2480
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2481
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}${stopAmountText}`, cx, stopY, lossLine);
2482
+ drawPositionPill(
2483
+ hasMoney ? `Entry ${formatPrice(entry.price)} Qty ${qtyText} RR ${rr.toFixed(2)}` : `Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`,
2484
+ cx,
2485
+ entryY,
2486
+ hexToRgba(drawing.color, 0.92)
2487
+ );
2474
2488
  if (drawing.label) {
2475
2489
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2476
2490
  }
@@ -3933,7 +3947,14 @@ function createChart(element, options = {}) {
3933
3947
  color: defaults.color ?? (isLong ? "#26a69a" : "#ef5350"),
3934
3948
  colors: defaults.colors ?? POSITION_DEFAULT_COLORS,
3935
3949
  style: defaults.style ?? "solid",
3936
- width: defaults.width ?? 1
3950
+ width: defaults.width ?? 1,
3951
+ ...defaults.accountSize === void 0 ? {} : { accountSize: defaults.accountSize },
3952
+ ...defaults.lotSize === void 0 ? {} : { lotSize: defaults.lotSize },
3953
+ ...defaults.risk === void 0 ? {} : { risk: defaults.risk },
3954
+ ...defaults.riskMode === void 0 ? {} : { riskMode: defaults.riskMode },
3955
+ ...defaults.leverage === void 0 ? {} : { leverage: defaults.leverage },
3956
+ ...defaults.pointValue === void 0 ? {} : { pointValue: defaults.pointValue },
3957
+ ...defaults.qtyPrecision === void 0 ? {} : { qtyPrecision: defaults.qtyPrecision }
3937
3958
  })
3938
3959
  );
3939
3960
  emitDrawingsChange();
@@ -4503,6 +4524,20 @@ function createChart(element, options = {}) {
4503
4524
  if (region === "outside") {
4504
4525
  return;
4505
4526
  }
4527
+ if (region === "plot" && !activeDrawingTool) {
4528
+ const drawingHit = getDrawingHit(point.x, point.y);
4529
+ if (drawingHit) {
4530
+ selectedDrawingId = drawingHit.drawing.id;
4531
+ drawingDoubleClickHandler?.({
4532
+ drawing: serializeDrawing(drawingHit.drawing),
4533
+ target: drawingHit.target,
4534
+ ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4535
+ x: point.x,
4536
+ y: point.y
4537
+ });
4538
+ return;
4539
+ }
4540
+ }
4506
4541
  if (doubleClickAction === "placeLimitOrder") {
4507
4542
  if (region !== "plot") {
4508
4543
  return;
@@ -4813,6 +4848,9 @@ function createChart(element, options = {}) {
4813
4848
  const onDrawingSelect = (handler) => {
4814
4849
  drawingSelectHandler = handler;
4815
4850
  };
4851
+ const onDrawingDoubleClick = (handler) => {
4852
+ drawingDoubleClickHandler = handler;
4853
+ };
4816
4854
  const onDrawingHover = (handler) => {
4817
4855
  drawingHoverHandler = handler;
4818
4856
  };
@@ -4870,6 +4908,7 @@ function createChart(element, options = {}) {
4870
4908
  clearDrawings,
4871
4909
  onDrawingsChange,
4872
4910
  onDrawingSelect,
4911
+ onDrawingDoubleClick,
4873
4912
  setSelectedDrawing,
4874
4913
  onDrawingHover,
4875
4914
  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.
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.79",
3
+ "version": "0.1.81",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",