hyperprop-charting-library 0.1.66 → 0.1.68
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 +199 -36
- package/dist/hyperprop-charting-library.d.ts +4 -1
- package/dist/hyperprop-charting-library.js +199 -36
- package/dist/index.cjs +199 -36
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +199 -36
- package/docs/API.md +13 -3
- package/docs/RECIPES.md +21 -2
- package/package.json +1 -1
|
@@ -102,7 +102,8 @@ var DEFAULT_PRICE_LINE_OPTIONS = {
|
|
|
102
102
|
labelBackgroundColor: "#0b1220",
|
|
103
103
|
labelTextColor: "#60a5fa",
|
|
104
104
|
labelBorderRadius: 3,
|
|
105
|
-
showLabel: true
|
|
105
|
+
showLabel: true,
|
|
106
|
+
pinOutOfRange: false
|
|
106
107
|
};
|
|
107
108
|
var DEFAULT_ORDER_LINE_OPTIONS = {
|
|
108
109
|
visible: true,
|
|
@@ -136,7 +137,8 @@ var DEFAULT_ORDER_LINE_OPTIONS = {
|
|
|
136
137
|
connectorThickness: 1,
|
|
137
138
|
connectorAnchorPaddingRight: 10,
|
|
138
139
|
fillToPrice: Number.NaN,
|
|
139
|
-
fillColor: "rgba(37,99,235,0.18)"
|
|
140
|
+
fillColor: "rgba(37,99,235,0.18)",
|
|
141
|
+
pinOutOfRange: false
|
|
140
142
|
};
|
|
141
143
|
var DEFAULT_OPTIONS = {
|
|
142
144
|
width: 720,
|
|
@@ -169,6 +171,7 @@ var DEFAULT_OPTIONS = {
|
|
|
169
171
|
candleColorEpsilon: -1,
|
|
170
172
|
autoScaleSmoothing: 0.16,
|
|
171
173
|
autoScaleIgnoreLatestCandle: true,
|
|
174
|
+
pinOutOfRangeLines: false,
|
|
172
175
|
doubleClickEnabled: true,
|
|
173
176
|
doubleClickAction: "reset",
|
|
174
177
|
crosshair: DEFAULT_CROSSHAIR_OPTIONS,
|
|
@@ -1477,12 +1480,27 @@ function createChart(element, options = {}) {
|
|
|
1477
1480
|
const scaleWidth = Math.max(0, Math.floor(width - getRightAxisLabelX(chartRight)));
|
|
1478
1481
|
return Math.max(contentWidth, scaleWidth);
|
|
1479
1482
|
};
|
|
1483
|
+
const getLineY = (price, yFromPrice, chartTop, chartBottom, pinOutOfRange = mergedOptions.pinOutOfRangeLines) => {
|
|
1484
|
+
const y = yFromPrice(price);
|
|
1485
|
+
if (!Number.isFinite(y)) {
|
|
1486
|
+
return null;
|
|
1487
|
+
}
|
|
1488
|
+
const minY = chartTop + 1;
|
|
1489
|
+
const maxY = chartBottom - 1;
|
|
1490
|
+
if (y < minY || y > maxY) {
|
|
1491
|
+
return pinOutOfRange ? clamp(y, minY, maxY) : null;
|
|
1492
|
+
}
|
|
1493
|
+
return y;
|
|
1494
|
+
};
|
|
1480
1495
|
const drawPriceLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
1481
1496
|
const mergedLine = { ...DEFAULT_PRICE_LINE_OPTIONS, ...line };
|
|
1482
1497
|
if (!mergedLine.visible || !Number.isFinite(mergedLine.price)) {
|
|
1483
1498
|
return;
|
|
1484
1499
|
}
|
|
1485
|
-
const lineY =
|
|
1500
|
+
const lineY = getLineY(mergedLine.price, yFromPrice, chartTop, chartBottom, mergedLine.pinOutOfRange);
|
|
1501
|
+
if (lineY === null) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1486
1504
|
const color = mergedLine.color;
|
|
1487
1505
|
ctx.save();
|
|
1488
1506
|
ctx.strokeStyle = color;
|
|
@@ -1528,10 +1546,16 @@ function createChart(element, options = {}) {
|
|
|
1528
1546
|
if (!mergedLine.visible || !Number.isFinite(renderPrice)) {
|
|
1529
1547
|
return;
|
|
1530
1548
|
}
|
|
1531
|
-
const lineY =
|
|
1549
|
+
const lineY = getLineY(renderPrice, yFromPrice, chartTop, chartBottom, mergedLine.pinOutOfRange);
|
|
1550
|
+
if (lineY === null) {
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1532
1553
|
const color = line.color ?? (mergedLine.type === "takeProfit" ? "rgba(45,212,191,0.86)" : mergedLine.type === "stop" ? "rgba(245,158,11,0.86)" : "rgba(59,130,246,0.8)");
|
|
1533
1554
|
if (Number.isFinite(mergedLine.fillToPrice)) {
|
|
1534
|
-
const fillY =
|
|
1555
|
+
const fillY = getLineY(mergedLine.fillToPrice, yFromPrice, chartTop, chartBottom, true);
|
|
1556
|
+
if (fillY === null) {
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1535
1559
|
const topY = Math.min(lineY, fillY);
|
|
1536
1560
|
const heightY = Math.max(1, Math.abs(lineY - fillY));
|
|
1537
1561
|
ctx.save();
|
|
@@ -1549,21 +1573,29 @@ function createChart(element, options = {}) {
|
|
|
1549
1573
|
ctx.stroke();
|
|
1550
1574
|
ctx.restore();
|
|
1551
1575
|
if (Number.isFinite(mergedLine.connectorToPrice)) {
|
|
1552
|
-
const connectorY =
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
mergedLine.connectorStyle,
|
|
1559
|
-
dashPatterns.connectorDotted,
|
|
1560
|
-
dashPatterns.connectorDashed
|
|
1576
|
+
const connectorY = getLineY(
|
|
1577
|
+
mergedLine.connectorToPrice,
|
|
1578
|
+
yFromPrice,
|
|
1579
|
+
chartTop,
|
|
1580
|
+
chartBottom,
|
|
1581
|
+
mergedLine.pinOutOfRange
|
|
1561
1582
|
);
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1583
|
+
if (connectorY !== null) {
|
|
1584
|
+
const connectorX = chartRight - Math.max(6, mergedLine.connectorAnchorPaddingRight);
|
|
1585
|
+
ctx.save();
|
|
1586
|
+
ctx.strokeStyle = mergedLine.connectorColor ?? color;
|
|
1587
|
+
ctx.lineWidth = Math.max(1, mergedLine.connectorThickness);
|
|
1588
|
+
applyDashPattern(
|
|
1589
|
+
mergedLine.connectorStyle,
|
|
1590
|
+
dashPatterns.connectorDotted,
|
|
1591
|
+
dashPatterns.connectorDashed
|
|
1592
|
+
);
|
|
1593
|
+
ctx.beginPath();
|
|
1594
|
+
ctx.moveTo(crisp(connectorX), crisp(lineY));
|
|
1595
|
+
ctx.lineTo(crisp(connectorX), crisp(connectorY));
|
|
1596
|
+
ctx.stroke();
|
|
1597
|
+
ctx.restore();
|
|
1598
|
+
}
|
|
1567
1599
|
}
|
|
1568
1600
|
const qtyText = mergedLine.qty === void 0 ? "" : String(mergedLine.qty);
|
|
1569
1601
|
const typeTextMap = {
|
|
@@ -2096,7 +2128,11 @@ function createChart(element, options = {}) {
|
|
|
2096
2128
|
if (drawing.type === "horizontal-line") {
|
|
2097
2129
|
const point = drawing.points[0];
|
|
2098
2130
|
if (point) {
|
|
2099
|
-
const y =
|
|
2131
|
+
const y = getLineY(point.price, yFromPrice, chartTop, chartBottom);
|
|
2132
|
+
if (y === null) {
|
|
2133
|
+
ctx.restore();
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2100
2136
|
const handleX = chartRight - 64;
|
|
2101
2137
|
ctx.beginPath();
|
|
2102
2138
|
ctx.moveTo(crisp(chartLeft), crisp(y));
|
|
@@ -2108,6 +2144,20 @@ function createChart(element, options = {}) {
|
|
|
2108
2144
|
drawDrawingLabel(drawing.label, midX, y, drawing.color);
|
|
2109
2145
|
}
|
|
2110
2146
|
}
|
|
2147
|
+
} else if (drawing.type === "vertical-line") {
|
|
2148
|
+
const point = drawing.points[0];
|
|
2149
|
+
if (point) {
|
|
2150
|
+
const x = xFromDrawingPoint(point);
|
|
2151
|
+
ctx.beginPath();
|
|
2152
|
+
ctx.moveTo(crisp(x), crisp(chartTop));
|
|
2153
|
+
ctx.lineTo(crisp(x), crisp(chartBottom));
|
|
2154
|
+
ctx.stroke();
|
|
2155
|
+
const handleY = (chartTop + chartBottom) / 2;
|
|
2156
|
+
drawDrawingHandle(x, handleY, drawing.color);
|
|
2157
|
+
if (drawing.label) {
|
|
2158
|
+
drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2111
2161
|
} else if (drawing.type === "trendline") {
|
|
2112
2162
|
const first = drawing.points[0];
|
|
2113
2163
|
const second = drawing.points[1];
|
|
@@ -2128,6 +2178,36 @@ function createChart(element, options = {}) {
|
|
|
2128
2178
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2129
2179
|
}
|
|
2130
2180
|
}
|
|
2181
|
+
} else if (drawing.type === "ray") {
|
|
2182
|
+
const first = drawing.points[0];
|
|
2183
|
+
const second = drawing.points[1];
|
|
2184
|
+
if (first && second) {
|
|
2185
|
+
const firstX = xFromDrawingPoint(first);
|
|
2186
|
+
const firstY = yFromPrice(first.price);
|
|
2187
|
+
const secondX = xFromDrawingPoint(second);
|
|
2188
|
+
const secondY = yFromPrice(second.price);
|
|
2189
|
+
const dx = secondX - firstX;
|
|
2190
|
+
const dy = secondY - firstY;
|
|
2191
|
+
let endX = secondX;
|
|
2192
|
+
let endY = secondY;
|
|
2193
|
+
const len = Math.hypot(dx, dy);
|
|
2194
|
+
if (len > 0) {
|
|
2195
|
+
const far = Math.abs(chartRight - chartLeft) + Math.abs(chartBottom - chartTop) + 2e3;
|
|
2196
|
+
endX = firstX + dx / len * far;
|
|
2197
|
+
endY = firstY + dy / len * far;
|
|
2198
|
+
}
|
|
2199
|
+
ctx.beginPath();
|
|
2200
|
+
ctx.moveTo(firstX, firstY);
|
|
2201
|
+
ctx.lineTo(endX, endY);
|
|
2202
|
+
ctx.stroke();
|
|
2203
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2204
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2205
|
+
if (drawing.label) {
|
|
2206
|
+
const midX = (firstX + secondX) / 2;
|
|
2207
|
+
const midY = (firstY + secondY) / 2;
|
|
2208
|
+
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2131
2211
|
} else if (drawing.type === "fib-retracement") {
|
|
2132
2212
|
const first = drawing.points[0];
|
|
2133
2213
|
const second = drawing.points[1];
|
|
@@ -2536,13 +2616,19 @@ function createChart(element, options = {}) {
|
|
|
2536
2616
|
if (!labels.visible || !Number.isFinite(label.price) || label.text.length === 0) {
|
|
2537
2617
|
return;
|
|
2538
2618
|
}
|
|
2619
|
+
if (getLineY(label.price, yFromPrice, chartTop, chartBottom, label.pinOutOfRange) === null) {
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2539
2622
|
priceAxisLabels.push(label);
|
|
2540
2623
|
};
|
|
2541
|
-
const drawReferenceLine = (price, color, style = "dotted") => {
|
|
2624
|
+
const drawReferenceLine = (price, color, style = "dotted", pinOutOfRange = mergedOptions.pinOutOfRangeLines) => {
|
|
2542
2625
|
if (!Number.isFinite(price)) {
|
|
2543
2626
|
return;
|
|
2544
2627
|
}
|
|
2545
|
-
const y =
|
|
2628
|
+
const y = getLineY(price, yFromPrice, chartTop, chartBottom, pinOutOfRange);
|
|
2629
|
+
if (y === null) {
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2546
2632
|
ctx.save();
|
|
2547
2633
|
ctx.strokeStyle = color;
|
|
2548
2634
|
ctx.lineWidth = 1;
|
|
@@ -2569,22 +2655,23 @@ function createChart(element, options = {}) {
|
|
|
2569
2655
|
let tickerColor = null;
|
|
2570
2656
|
if ((ticker.visible ?? true) && lastPoint) {
|
|
2571
2657
|
tickerPrice = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice : lastPoint.c;
|
|
2572
|
-
const
|
|
2573
|
-
const lineY = clamp(tickerY, chartTop + 1, chartBottom - 1);
|
|
2658
|
+
const lineY = getLineY(tickerPrice, yFromPrice, chartTop, chartBottom);
|
|
2574
2659
|
const lastDirection = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice >= lastPoint.o ? "up" : "down" : getCandleDirectionByIndex(data.length - 1);
|
|
2575
2660
|
tickerColor = ticker.color ?? (lastDirection === "up" ? mergedOptions.upColor : mergedOptions.downColor);
|
|
2576
2661
|
const tickerThickness = Math.max(1, ticker.thickness ?? 1);
|
|
2577
2662
|
const tickerStyle = ticker.style ?? "solid";
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2663
|
+
if (lineY !== null) {
|
|
2664
|
+
ctx.save();
|
|
2665
|
+
ctx.strokeStyle = tickerColor;
|
|
2666
|
+
ctx.lineWidth = tickerThickness;
|
|
2667
|
+
applyDashPattern(tickerStyle, dashPatterns.dotted, dashPatterns.dashed);
|
|
2668
|
+
ctx.beginPath();
|
|
2669
|
+
ctx.moveTo(crisp(chartLeft), crisp(lineY));
|
|
2670
|
+
ctx.lineTo(crisp(chartRight), crisp(lineY));
|
|
2671
|
+
ctx.stroke();
|
|
2672
|
+
ctx.setLineDash([]);
|
|
2673
|
+
ctx.restore();
|
|
2674
|
+
}
|
|
2588
2675
|
}
|
|
2589
2676
|
if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
|
|
2590
2677
|
const tickerSubtexts = [
|
|
@@ -2694,13 +2781,14 @@ function createChart(element, options = {}) {
|
|
|
2694
2781
|
}
|
|
2695
2782
|
const labelTextWidth = Math.max(primaryWidth, subtextWidth);
|
|
2696
2783
|
const labelWidth = getRightAxisLabelWidth(chartRight, labelTextWidth);
|
|
2784
|
+
const labelLineY = getLineY(label.price, yFromPrice, chartTop, chartBottom, label.pinOutOfRange);
|
|
2697
2785
|
return {
|
|
2698
2786
|
...label,
|
|
2699
2787
|
subtexts,
|
|
2700
2788
|
subtextFontSize,
|
|
2701
2789
|
height: labelHeight,
|
|
2702
2790
|
width: labelWidth,
|
|
2703
|
-
targetY:
|
|
2791
|
+
targetY: (labelLineY ?? yFromPrice(label.price)) - labelHeight / 2,
|
|
2704
2792
|
y: 0
|
|
2705
2793
|
};
|
|
2706
2794
|
}).sort((a, b) => a.targetY - b.targetY || b.priority - a.priority);
|
|
@@ -3262,6 +3350,17 @@ function createChart(element, options = {}) {
|
|
|
3262
3350
|
if (Math.abs(y - lineY) <= 7) {
|
|
3263
3351
|
return { drawing, target: "line" };
|
|
3264
3352
|
}
|
|
3353
|
+
} else if (drawing.type === "vertical-line") {
|
|
3354
|
+
const point = drawing.points[0];
|
|
3355
|
+
if (!point) continue;
|
|
3356
|
+
const lineX = canvasXFromDrawingPoint(point);
|
|
3357
|
+
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
3358
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
3359
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3360
|
+
}
|
|
3361
|
+
if (Math.abs(x - lineX) <= 7) {
|
|
3362
|
+
return { drawing, target: "line" };
|
|
3363
|
+
}
|
|
3265
3364
|
} else if (drawing.type === "trendline") {
|
|
3266
3365
|
const first = drawing.points[0];
|
|
3267
3366
|
const second = drawing.points[1];
|
|
@@ -3279,6 +3378,31 @@ function createChart(element, options = {}) {
|
|
|
3279
3378
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3280
3379
|
return { drawing, target: "line" };
|
|
3281
3380
|
}
|
|
3381
|
+
} else if (drawing.type === "ray") {
|
|
3382
|
+
const first = drawing.points[0];
|
|
3383
|
+
const second = drawing.points[1];
|
|
3384
|
+
if (!first || !second) continue;
|
|
3385
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3386
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3387
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3388
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3389
|
+
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
3390
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3391
|
+
}
|
|
3392
|
+
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
3393
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3394
|
+
}
|
|
3395
|
+
const dx = x2 - x1;
|
|
3396
|
+
const dy = y2 - y1;
|
|
3397
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3398
|
+
if (lengthSq > 0) {
|
|
3399
|
+
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
3400
|
+
const projX = x1 + t * dx;
|
|
3401
|
+
const projY = y1 + t * dy;
|
|
3402
|
+
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
3403
|
+
return { drawing, target: "line" };
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3282
3406
|
} else if (drawing.type === "fib-retracement") {
|
|
3283
3407
|
const first = drawing.points[0];
|
|
3284
3408
|
const second = drawing.points[1];
|
|
@@ -3380,6 +3504,21 @@ function createChart(element, options = {}) {
|
|
|
3380
3504
|
draw();
|
|
3381
3505
|
return true;
|
|
3382
3506
|
}
|
|
3507
|
+
if (activeDrawingTool === "vertical-line") {
|
|
3508
|
+
const defaults = getDrawingToolDefaults("vertical-line");
|
|
3509
|
+
drawings.push(
|
|
3510
|
+
normalizeDrawingState({
|
|
3511
|
+
type: "vertical-line",
|
|
3512
|
+
points: [point],
|
|
3513
|
+
color: defaults.color ?? "#38bdf8",
|
|
3514
|
+
style: defaults.style ?? "solid",
|
|
3515
|
+
width: defaults.width ?? 1
|
|
3516
|
+
})
|
|
3517
|
+
);
|
|
3518
|
+
emitDrawingsChange();
|
|
3519
|
+
draw();
|
|
3520
|
+
return true;
|
|
3521
|
+
}
|
|
3383
3522
|
if (activeDrawingTool === "trendline") {
|
|
3384
3523
|
if (draftDrawing?.type === "trendline") {
|
|
3385
3524
|
const completed = normalizeDrawingState({
|
|
@@ -3404,6 +3543,30 @@ function createChart(element, options = {}) {
|
|
|
3404
3543
|
draw();
|
|
3405
3544
|
return true;
|
|
3406
3545
|
}
|
|
3546
|
+
if (activeDrawingTool === "ray") {
|
|
3547
|
+
if (draftDrawing?.type === "ray") {
|
|
3548
|
+
const completed = normalizeDrawingState({
|
|
3549
|
+
...serializeDrawing(draftDrawing),
|
|
3550
|
+
points: [draftDrawing.points[0], point]
|
|
3551
|
+
});
|
|
3552
|
+
drawings.push(completed);
|
|
3553
|
+
draftDrawing = null;
|
|
3554
|
+
activeDrawingTool = null;
|
|
3555
|
+
emitDrawingsChange();
|
|
3556
|
+
draw();
|
|
3557
|
+
return true;
|
|
3558
|
+
}
|
|
3559
|
+
const defaults = getDrawingToolDefaults("ray");
|
|
3560
|
+
draftDrawing = normalizeDrawingState({
|
|
3561
|
+
type: "ray",
|
|
3562
|
+
points: [point, point],
|
|
3563
|
+
color: defaults.color ?? "#2563eb",
|
|
3564
|
+
style: defaults.style ?? "solid",
|
|
3565
|
+
width: defaults.width ?? 2
|
|
3566
|
+
});
|
|
3567
|
+
draw();
|
|
3568
|
+
return true;
|
|
3569
|
+
}
|
|
3407
3570
|
if (activeDrawingTool === "fib-retracement") {
|
|
3408
3571
|
if (draftDrawing?.type === "fib-retracement") {
|
|
3409
3572
|
const completed = normalizeDrawingState({
|
|
@@ -3700,7 +3863,7 @@ function createChart(element, options = {}) {
|
|
|
3700
3863
|
setCrosshairPoint(null);
|
|
3701
3864
|
return;
|
|
3702
3865
|
}
|
|
3703
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3866
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement")) {
|
|
3704
3867
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3705
3868
|
if (nextPoint) {
|
|
3706
3869
|
draftDrawing = {
|