hyperprop-charting-library 0.1.81 → 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.
@@ -2444,6 +2444,40 @@ function createChart(element, options = {}) {
2444
2444
  ctx.fillStyle = lossFill;
2445
2445
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2446
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
+ }
2447
2481
  if (isSelected) {
2448
2482
  ctx.save();
2449
2483
  ctx.setLineDash([]);
@@ -2505,12 +2539,18 @@ function createChart(element, options = {}) {
2505
2539
  };
2506
2540
  drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2507
2541
  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
- );
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);
2514
2554
  if (drawing.label) {
2515
2555
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2516
2556
  }
@@ -2418,6 +2418,40 @@ function createChart(element, options = {}) {
2418
2418
  ctx.fillStyle = lossFill;
2419
2419
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2420
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
+ }
2421
2455
  if (isSelected) {
2422
2456
  ctx.save();
2423
2457
  ctx.setLineDash([]);
@@ -2479,12 +2513,18 @@ function createChart(element, options = {}) {
2479
2513
  };
2480
2514
  drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2481
2515
  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
- );
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);
2488
2528
  if (drawing.label) {
2489
2529
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2490
2530
  }
package/dist/index.cjs CHANGED
@@ -2444,6 +2444,40 @@ function createChart(element, options = {}) {
2444
2444
  ctx.fillStyle = lossFill;
2445
2445
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2446
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
+ }
2447
2481
  if (isSelected) {
2448
2482
  ctx.save();
2449
2483
  ctx.setLineDash([]);
@@ -2505,12 +2539,18 @@ function createChart(element, options = {}) {
2505
2539
  };
2506
2540
  drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2507
2541
  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
- );
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);
2514
2554
  if (drawing.label) {
2515
2555
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2516
2556
  }
package/dist/index.js CHANGED
@@ -2418,6 +2418,40 @@ function createChart(element, options = {}) {
2418
2418
  ctx.fillStyle = lossFill;
2419
2419
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2420
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
+ }
2421
2455
  if (isSelected) {
2422
2456
  ctx.save();
2423
2457
  ctx.setLineDash([]);
@@ -2479,12 +2513,18 @@ function createChart(element, options = {}) {
2479
2513
  };
2480
2514
  drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}${targetAmountText}`, cx, targetY, profitLine);
2481
2515
  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
- );
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);
2488
2528
  if (drawing.label) {
2489
2529
  drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2490
2530
  }
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`). 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.
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`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.81",
3
+ "version": "0.1.82",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",