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.
- package/dist/hyperprop-charting-library.cjs +46 -6
- package/dist/hyperprop-charting-library.js +46 -6
- package/dist/index.cjs +46 -6
- package/dist/index.js +46 -6
- package/docs/API.md +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
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
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
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
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
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
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
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`
|