hyperprop-charting-library 0.1.59 → 0.1.60
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 +122 -1
- package/dist/hyperprop-charting-library.d.ts +1 -1
- package/dist/hyperprop-charting-library.js +122 -1
- package/dist/index.cjs +122 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +122 -1
- package/package.json +1 -1
|
@@ -2021,6 +2021,16 @@ function createChart(element, options = {}) {
|
|
|
2021
2021
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
2022
2022
|
};
|
|
2023
2023
|
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
2024
|
+
const FIB_LEVELS = [
|
|
2025
|
+
{ ratio: 0, color: "#787b86" },
|
|
2026
|
+
{ ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
|
|
2027
|
+
{ ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
|
|
2028
|
+
{ ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
|
|
2029
|
+
{ ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
|
|
2030
|
+
{ ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
|
|
2031
|
+
{ ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
|
|
2032
|
+
{ ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
|
|
2033
|
+
];
|
|
2024
2034
|
const drawDrawingHandle = (x, y, color) => {
|
|
2025
2035
|
ctx.save();
|
|
2026
2036
|
ctx.setLineDash([]);
|
|
@@ -2097,6 +2107,72 @@ function createChart(element, options = {}) {
|
|
|
2097
2107
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2098
2108
|
}
|
|
2099
2109
|
}
|
|
2110
|
+
} else if (drawing.type === "fib-retracement") {
|
|
2111
|
+
const first = drawing.points[0];
|
|
2112
|
+
const second = drawing.points[1];
|
|
2113
|
+
if (first && second) {
|
|
2114
|
+
const firstX = xFromDrawingPoint(first);
|
|
2115
|
+
const secondX = xFromDrawingPoint(second);
|
|
2116
|
+
const firstY = yFromPrice(first.price);
|
|
2117
|
+
const secondY = yFromPrice(second.price);
|
|
2118
|
+
const lineLeft = chartLeft;
|
|
2119
|
+
const lineRight = chartRight;
|
|
2120
|
+
const levelLines = FIB_LEVELS.map((level) => {
|
|
2121
|
+
const price = first.price + (second.price - first.price) * (1 - level.ratio);
|
|
2122
|
+
return { ...level, price, y: yFromPrice(price) };
|
|
2123
|
+
});
|
|
2124
|
+
for (let index = 0; index < levelLines.length - 1; index += 1) {
|
|
2125
|
+
const top = levelLines[index];
|
|
2126
|
+
const bottom = levelLines[index + 1];
|
|
2127
|
+
const fillColor = bottom.band ?? top.band;
|
|
2128
|
+
if (!fillColor) continue;
|
|
2129
|
+
const bandTop = Math.min(top.y, bottom.y);
|
|
2130
|
+
const bandBottom = Math.max(top.y, bottom.y);
|
|
2131
|
+
ctx.save();
|
|
2132
|
+
ctx.fillStyle = fillColor;
|
|
2133
|
+
ctx.globalAlpha = draft ? 0.5 : 1;
|
|
2134
|
+
ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
|
|
2135
|
+
ctx.restore();
|
|
2136
|
+
}
|
|
2137
|
+
ctx.save();
|
|
2138
|
+
ctx.lineWidth = drawing.width;
|
|
2139
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2140
|
+
for (const level of levelLines) {
|
|
2141
|
+
ctx.strokeStyle = level.color;
|
|
2142
|
+
ctx.beginPath();
|
|
2143
|
+
ctx.moveTo(crisp(lineLeft), crisp(level.y));
|
|
2144
|
+
ctx.lineTo(crisp(lineRight), crisp(level.y));
|
|
2145
|
+
ctx.stroke();
|
|
2146
|
+
}
|
|
2147
|
+
ctx.restore();
|
|
2148
|
+
ctx.save();
|
|
2149
|
+
ctx.strokeStyle = "rgba(148,163,184,0.7)";
|
|
2150
|
+
ctx.setLineDash([4, 4]);
|
|
2151
|
+
ctx.lineWidth = 1;
|
|
2152
|
+
ctx.beginPath();
|
|
2153
|
+
ctx.moveTo(firstX, firstY);
|
|
2154
|
+
ctx.lineTo(secondX, secondY);
|
|
2155
|
+
ctx.stroke();
|
|
2156
|
+
ctx.restore();
|
|
2157
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2158
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2159
|
+
const prevFont = ctx.font;
|
|
2160
|
+
ctx.font = `500 11px ${mergedOptions.fontFamily}`;
|
|
2161
|
+
for (const level of levelLines) {
|
|
2162
|
+
const labelText = `${level.ratio} (${formatPrice(level.price)})`;
|
|
2163
|
+
const textWidth = ctx.measureText(labelText).width;
|
|
2164
|
+
const padding = 4;
|
|
2165
|
+
const bgX = lineLeft + 4;
|
|
2166
|
+
const bgY = level.y - 9;
|
|
2167
|
+
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
2168
|
+
fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
|
|
2169
|
+
ctx.fillStyle = level.color;
|
|
2170
|
+
ctx.textAlign = "left";
|
|
2171
|
+
ctx.textBaseline = "middle";
|
|
2172
|
+
ctx.fillText(labelText, bgX + padding, bgY + 8);
|
|
2173
|
+
}
|
|
2174
|
+
ctx.font = prevFont;
|
|
2175
|
+
}
|
|
2100
2176
|
}
|
|
2101
2177
|
ctx.restore();
|
|
2102
2178
|
};
|
|
@@ -3183,6 +3259,28 @@ function createChart(element, options = {}) {
|
|
|
3183
3259
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3184
3260
|
return { drawing, target: "line" };
|
|
3185
3261
|
}
|
|
3262
|
+
} else if (drawing.type === "fib-retracement") {
|
|
3263
|
+
const first = drawing.points[0];
|
|
3264
|
+
const second = drawing.points[1];
|
|
3265
|
+
if (!first || !second) continue;
|
|
3266
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3267
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3268
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3269
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3270
|
+
if (Math.hypot(x - x1, y - y1) <= 9) {
|
|
3271
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3272
|
+
}
|
|
3273
|
+
if (Math.hypot(x - x2, y - y2) <= 9) {
|
|
3274
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3275
|
+
}
|
|
3276
|
+
const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
3277
|
+
for (const ratio of fibRatios) {
|
|
3278
|
+
const price = first.price + (second.price - first.price) * (1 - ratio);
|
|
3279
|
+
const lineY = canvasYFromDrawingPrice(price);
|
|
3280
|
+
if (Math.abs(y - lineY) <= 5) {
|
|
3281
|
+
return { drawing, target: "line" };
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3186
3284
|
}
|
|
3187
3285
|
}
|
|
3188
3286
|
return null;
|
|
@@ -3284,6 +3382,29 @@ function createChart(element, options = {}) {
|
|
|
3284
3382
|
draw();
|
|
3285
3383
|
return true;
|
|
3286
3384
|
}
|
|
3385
|
+
if (activeDrawingTool === "fib-retracement") {
|
|
3386
|
+
if (draftDrawing?.type === "fib-retracement") {
|
|
3387
|
+
const completed = normalizeDrawingState({
|
|
3388
|
+
...serializeDrawing(draftDrawing),
|
|
3389
|
+
points: [draftDrawing.points[0], point]
|
|
3390
|
+
});
|
|
3391
|
+
drawings.push(completed);
|
|
3392
|
+
draftDrawing = null;
|
|
3393
|
+
activeDrawingTool = null;
|
|
3394
|
+
emitDrawingsChange();
|
|
3395
|
+
draw();
|
|
3396
|
+
return true;
|
|
3397
|
+
}
|
|
3398
|
+
draftDrawing = normalizeDrawingState({
|
|
3399
|
+
type: "fib-retracement",
|
|
3400
|
+
points: [point, point],
|
|
3401
|
+
color: "#2563eb",
|
|
3402
|
+
style: "solid",
|
|
3403
|
+
width: 1
|
|
3404
|
+
});
|
|
3405
|
+
draw();
|
|
3406
|
+
return true;
|
|
3407
|
+
}
|
|
3287
3408
|
return false;
|
|
3288
3409
|
};
|
|
3289
3410
|
const updateDrawingDrag = (x, y) => {
|
|
@@ -3519,7 +3640,7 @@ function createChart(element, options = {}) {
|
|
|
3519
3640
|
setCrosshairPoint(null);
|
|
3520
3641
|
return;
|
|
3521
3642
|
}
|
|
3522
|
-
if (draftDrawing && activeDrawingTool === "trendline") {
|
|
3643
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3523
3644
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3524
3645
|
if (nextPoint) {
|
|
3525
3646
|
draftDrawing = {
|
|
@@ -43,7 +43,7 @@ interface ChartOptions {
|
|
|
43
43
|
drawings?: DrawingObjectOptions[];
|
|
44
44
|
}
|
|
45
45
|
type IndicatorPane = "overlay" | "separate";
|
|
46
|
-
type DrawingToolType = "horizontal-line" | "trendline";
|
|
46
|
+
type DrawingToolType = "horizontal-line" | "trendline" | "fib-retracement";
|
|
47
47
|
interface DrawingPoint {
|
|
48
48
|
index: number;
|
|
49
49
|
price: number;
|
|
@@ -1997,6 +1997,16 @@ function createChart(element, options = {}) {
|
|
|
1997
1997
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
1998
1998
|
};
|
|
1999
1999
|
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
2000
|
+
const FIB_LEVELS = [
|
|
2001
|
+
{ ratio: 0, color: "#787b86" },
|
|
2002
|
+
{ ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
|
|
2003
|
+
{ ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
|
|
2004
|
+
{ ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
|
|
2005
|
+
{ ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
|
|
2006
|
+
{ ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
|
|
2007
|
+
{ ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
|
|
2008
|
+
{ ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
|
|
2009
|
+
];
|
|
2000
2010
|
const drawDrawingHandle = (x, y, color) => {
|
|
2001
2011
|
ctx.save();
|
|
2002
2012
|
ctx.setLineDash([]);
|
|
@@ -2073,6 +2083,72 @@ function createChart(element, options = {}) {
|
|
|
2073
2083
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2074
2084
|
}
|
|
2075
2085
|
}
|
|
2086
|
+
} else if (drawing.type === "fib-retracement") {
|
|
2087
|
+
const first = drawing.points[0];
|
|
2088
|
+
const second = drawing.points[1];
|
|
2089
|
+
if (first && second) {
|
|
2090
|
+
const firstX = xFromDrawingPoint(first);
|
|
2091
|
+
const secondX = xFromDrawingPoint(second);
|
|
2092
|
+
const firstY = yFromPrice(first.price);
|
|
2093
|
+
const secondY = yFromPrice(second.price);
|
|
2094
|
+
const lineLeft = chartLeft;
|
|
2095
|
+
const lineRight = chartRight;
|
|
2096
|
+
const levelLines = FIB_LEVELS.map((level) => {
|
|
2097
|
+
const price = first.price + (second.price - first.price) * (1 - level.ratio);
|
|
2098
|
+
return { ...level, price, y: yFromPrice(price) };
|
|
2099
|
+
});
|
|
2100
|
+
for (let index = 0; index < levelLines.length - 1; index += 1) {
|
|
2101
|
+
const top = levelLines[index];
|
|
2102
|
+
const bottom = levelLines[index + 1];
|
|
2103
|
+
const fillColor = bottom.band ?? top.band;
|
|
2104
|
+
if (!fillColor) continue;
|
|
2105
|
+
const bandTop = Math.min(top.y, bottom.y);
|
|
2106
|
+
const bandBottom = Math.max(top.y, bottom.y);
|
|
2107
|
+
ctx.save();
|
|
2108
|
+
ctx.fillStyle = fillColor;
|
|
2109
|
+
ctx.globalAlpha = draft ? 0.5 : 1;
|
|
2110
|
+
ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
|
|
2111
|
+
ctx.restore();
|
|
2112
|
+
}
|
|
2113
|
+
ctx.save();
|
|
2114
|
+
ctx.lineWidth = drawing.width;
|
|
2115
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2116
|
+
for (const level of levelLines) {
|
|
2117
|
+
ctx.strokeStyle = level.color;
|
|
2118
|
+
ctx.beginPath();
|
|
2119
|
+
ctx.moveTo(crisp(lineLeft), crisp(level.y));
|
|
2120
|
+
ctx.lineTo(crisp(lineRight), crisp(level.y));
|
|
2121
|
+
ctx.stroke();
|
|
2122
|
+
}
|
|
2123
|
+
ctx.restore();
|
|
2124
|
+
ctx.save();
|
|
2125
|
+
ctx.strokeStyle = "rgba(148,163,184,0.7)";
|
|
2126
|
+
ctx.setLineDash([4, 4]);
|
|
2127
|
+
ctx.lineWidth = 1;
|
|
2128
|
+
ctx.beginPath();
|
|
2129
|
+
ctx.moveTo(firstX, firstY);
|
|
2130
|
+
ctx.lineTo(secondX, secondY);
|
|
2131
|
+
ctx.stroke();
|
|
2132
|
+
ctx.restore();
|
|
2133
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2134
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2135
|
+
const prevFont = ctx.font;
|
|
2136
|
+
ctx.font = `500 11px ${mergedOptions.fontFamily}`;
|
|
2137
|
+
for (const level of levelLines) {
|
|
2138
|
+
const labelText = `${level.ratio} (${formatPrice(level.price)})`;
|
|
2139
|
+
const textWidth = ctx.measureText(labelText).width;
|
|
2140
|
+
const padding = 4;
|
|
2141
|
+
const bgX = lineLeft + 4;
|
|
2142
|
+
const bgY = level.y - 9;
|
|
2143
|
+
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
2144
|
+
fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
|
|
2145
|
+
ctx.fillStyle = level.color;
|
|
2146
|
+
ctx.textAlign = "left";
|
|
2147
|
+
ctx.textBaseline = "middle";
|
|
2148
|
+
ctx.fillText(labelText, bgX + padding, bgY + 8);
|
|
2149
|
+
}
|
|
2150
|
+
ctx.font = prevFont;
|
|
2151
|
+
}
|
|
2076
2152
|
}
|
|
2077
2153
|
ctx.restore();
|
|
2078
2154
|
};
|
|
@@ -3159,6 +3235,28 @@ function createChart(element, options = {}) {
|
|
|
3159
3235
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3160
3236
|
return { drawing, target: "line" };
|
|
3161
3237
|
}
|
|
3238
|
+
} else if (drawing.type === "fib-retracement") {
|
|
3239
|
+
const first = drawing.points[0];
|
|
3240
|
+
const second = drawing.points[1];
|
|
3241
|
+
if (!first || !second) continue;
|
|
3242
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3243
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3244
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3245
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3246
|
+
if (Math.hypot(x - x1, y - y1) <= 9) {
|
|
3247
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3248
|
+
}
|
|
3249
|
+
if (Math.hypot(x - x2, y - y2) <= 9) {
|
|
3250
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3251
|
+
}
|
|
3252
|
+
const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
3253
|
+
for (const ratio of fibRatios) {
|
|
3254
|
+
const price = first.price + (second.price - first.price) * (1 - ratio);
|
|
3255
|
+
const lineY = canvasYFromDrawingPrice(price);
|
|
3256
|
+
if (Math.abs(y - lineY) <= 5) {
|
|
3257
|
+
return { drawing, target: "line" };
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3162
3260
|
}
|
|
3163
3261
|
}
|
|
3164
3262
|
return null;
|
|
@@ -3260,6 +3358,29 @@ function createChart(element, options = {}) {
|
|
|
3260
3358
|
draw();
|
|
3261
3359
|
return true;
|
|
3262
3360
|
}
|
|
3361
|
+
if (activeDrawingTool === "fib-retracement") {
|
|
3362
|
+
if (draftDrawing?.type === "fib-retracement") {
|
|
3363
|
+
const completed = normalizeDrawingState({
|
|
3364
|
+
...serializeDrawing(draftDrawing),
|
|
3365
|
+
points: [draftDrawing.points[0], point]
|
|
3366
|
+
});
|
|
3367
|
+
drawings.push(completed);
|
|
3368
|
+
draftDrawing = null;
|
|
3369
|
+
activeDrawingTool = null;
|
|
3370
|
+
emitDrawingsChange();
|
|
3371
|
+
draw();
|
|
3372
|
+
return true;
|
|
3373
|
+
}
|
|
3374
|
+
draftDrawing = normalizeDrawingState({
|
|
3375
|
+
type: "fib-retracement",
|
|
3376
|
+
points: [point, point],
|
|
3377
|
+
color: "#2563eb",
|
|
3378
|
+
style: "solid",
|
|
3379
|
+
width: 1
|
|
3380
|
+
});
|
|
3381
|
+
draw();
|
|
3382
|
+
return true;
|
|
3383
|
+
}
|
|
3263
3384
|
return false;
|
|
3264
3385
|
};
|
|
3265
3386
|
const updateDrawingDrag = (x, y) => {
|
|
@@ -3495,7 +3616,7 @@ function createChart(element, options = {}) {
|
|
|
3495
3616
|
setCrosshairPoint(null);
|
|
3496
3617
|
return;
|
|
3497
3618
|
}
|
|
3498
|
-
if (draftDrawing && activeDrawingTool === "trendline") {
|
|
3619
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3499
3620
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3500
3621
|
if (nextPoint) {
|
|
3501
3622
|
draftDrawing = {
|
package/dist/index.cjs
CHANGED
|
@@ -2021,6 +2021,16 @@ function createChart(element, options = {}) {
|
|
|
2021
2021
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
2022
2022
|
};
|
|
2023
2023
|
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
2024
|
+
const FIB_LEVELS = [
|
|
2025
|
+
{ ratio: 0, color: "#787b86" },
|
|
2026
|
+
{ ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
|
|
2027
|
+
{ ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
|
|
2028
|
+
{ ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
|
|
2029
|
+
{ ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
|
|
2030
|
+
{ ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
|
|
2031
|
+
{ ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
|
|
2032
|
+
{ ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
|
|
2033
|
+
];
|
|
2024
2034
|
const drawDrawingHandle = (x, y, color) => {
|
|
2025
2035
|
ctx.save();
|
|
2026
2036
|
ctx.setLineDash([]);
|
|
@@ -2097,6 +2107,72 @@ function createChart(element, options = {}) {
|
|
|
2097
2107
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2098
2108
|
}
|
|
2099
2109
|
}
|
|
2110
|
+
} else if (drawing.type === "fib-retracement") {
|
|
2111
|
+
const first = drawing.points[0];
|
|
2112
|
+
const second = drawing.points[1];
|
|
2113
|
+
if (first && second) {
|
|
2114
|
+
const firstX = xFromDrawingPoint(first);
|
|
2115
|
+
const secondX = xFromDrawingPoint(second);
|
|
2116
|
+
const firstY = yFromPrice(first.price);
|
|
2117
|
+
const secondY = yFromPrice(second.price);
|
|
2118
|
+
const lineLeft = chartLeft;
|
|
2119
|
+
const lineRight = chartRight;
|
|
2120
|
+
const levelLines = FIB_LEVELS.map((level) => {
|
|
2121
|
+
const price = first.price + (second.price - first.price) * (1 - level.ratio);
|
|
2122
|
+
return { ...level, price, y: yFromPrice(price) };
|
|
2123
|
+
});
|
|
2124
|
+
for (let index = 0; index < levelLines.length - 1; index += 1) {
|
|
2125
|
+
const top = levelLines[index];
|
|
2126
|
+
const bottom = levelLines[index + 1];
|
|
2127
|
+
const fillColor = bottom.band ?? top.band;
|
|
2128
|
+
if (!fillColor) continue;
|
|
2129
|
+
const bandTop = Math.min(top.y, bottom.y);
|
|
2130
|
+
const bandBottom = Math.max(top.y, bottom.y);
|
|
2131
|
+
ctx.save();
|
|
2132
|
+
ctx.fillStyle = fillColor;
|
|
2133
|
+
ctx.globalAlpha = draft ? 0.5 : 1;
|
|
2134
|
+
ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
|
|
2135
|
+
ctx.restore();
|
|
2136
|
+
}
|
|
2137
|
+
ctx.save();
|
|
2138
|
+
ctx.lineWidth = drawing.width;
|
|
2139
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2140
|
+
for (const level of levelLines) {
|
|
2141
|
+
ctx.strokeStyle = level.color;
|
|
2142
|
+
ctx.beginPath();
|
|
2143
|
+
ctx.moveTo(crisp(lineLeft), crisp(level.y));
|
|
2144
|
+
ctx.lineTo(crisp(lineRight), crisp(level.y));
|
|
2145
|
+
ctx.stroke();
|
|
2146
|
+
}
|
|
2147
|
+
ctx.restore();
|
|
2148
|
+
ctx.save();
|
|
2149
|
+
ctx.strokeStyle = "rgba(148,163,184,0.7)";
|
|
2150
|
+
ctx.setLineDash([4, 4]);
|
|
2151
|
+
ctx.lineWidth = 1;
|
|
2152
|
+
ctx.beginPath();
|
|
2153
|
+
ctx.moveTo(firstX, firstY);
|
|
2154
|
+
ctx.lineTo(secondX, secondY);
|
|
2155
|
+
ctx.stroke();
|
|
2156
|
+
ctx.restore();
|
|
2157
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2158
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2159
|
+
const prevFont = ctx.font;
|
|
2160
|
+
ctx.font = `500 11px ${mergedOptions.fontFamily}`;
|
|
2161
|
+
for (const level of levelLines) {
|
|
2162
|
+
const labelText = `${level.ratio} (${formatPrice(level.price)})`;
|
|
2163
|
+
const textWidth = ctx.measureText(labelText).width;
|
|
2164
|
+
const padding = 4;
|
|
2165
|
+
const bgX = lineLeft + 4;
|
|
2166
|
+
const bgY = level.y - 9;
|
|
2167
|
+
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
2168
|
+
fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
|
|
2169
|
+
ctx.fillStyle = level.color;
|
|
2170
|
+
ctx.textAlign = "left";
|
|
2171
|
+
ctx.textBaseline = "middle";
|
|
2172
|
+
ctx.fillText(labelText, bgX + padding, bgY + 8);
|
|
2173
|
+
}
|
|
2174
|
+
ctx.font = prevFont;
|
|
2175
|
+
}
|
|
2100
2176
|
}
|
|
2101
2177
|
ctx.restore();
|
|
2102
2178
|
};
|
|
@@ -3183,6 +3259,28 @@ function createChart(element, options = {}) {
|
|
|
3183
3259
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3184
3260
|
return { drawing, target: "line" };
|
|
3185
3261
|
}
|
|
3262
|
+
} else if (drawing.type === "fib-retracement") {
|
|
3263
|
+
const first = drawing.points[0];
|
|
3264
|
+
const second = drawing.points[1];
|
|
3265
|
+
if (!first || !second) continue;
|
|
3266
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3267
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3268
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3269
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3270
|
+
if (Math.hypot(x - x1, y - y1) <= 9) {
|
|
3271
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3272
|
+
}
|
|
3273
|
+
if (Math.hypot(x - x2, y - y2) <= 9) {
|
|
3274
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3275
|
+
}
|
|
3276
|
+
const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
3277
|
+
for (const ratio of fibRatios) {
|
|
3278
|
+
const price = first.price + (second.price - first.price) * (1 - ratio);
|
|
3279
|
+
const lineY = canvasYFromDrawingPrice(price);
|
|
3280
|
+
if (Math.abs(y - lineY) <= 5) {
|
|
3281
|
+
return { drawing, target: "line" };
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3186
3284
|
}
|
|
3187
3285
|
}
|
|
3188
3286
|
return null;
|
|
@@ -3284,6 +3382,29 @@ function createChart(element, options = {}) {
|
|
|
3284
3382
|
draw();
|
|
3285
3383
|
return true;
|
|
3286
3384
|
}
|
|
3385
|
+
if (activeDrawingTool === "fib-retracement") {
|
|
3386
|
+
if (draftDrawing?.type === "fib-retracement") {
|
|
3387
|
+
const completed = normalizeDrawingState({
|
|
3388
|
+
...serializeDrawing(draftDrawing),
|
|
3389
|
+
points: [draftDrawing.points[0], point]
|
|
3390
|
+
});
|
|
3391
|
+
drawings.push(completed);
|
|
3392
|
+
draftDrawing = null;
|
|
3393
|
+
activeDrawingTool = null;
|
|
3394
|
+
emitDrawingsChange();
|
|
3395
|
+
draw();
|
|
3396
|
+
return true;
|
|
3397
|
+
}
|
|
3398
|
+
draftDrawing = normalizeDrawingState({
|
|
3399
|
+
type: "fib-retracement",
|
|
3400
|
+
points: [point, point],
|
|
3401
|
+
color: "#2563eb",
|
|
3402
|
+
style: "solid",
|
|
3403
|
+
width: 1
|
|
3404
|
+
});
|
|
3405
|
+
draw();
|
|
3406
|
+
return true;
|
|
3407
|
+
}
|
|
3287
3408
|
return false;
|
|
3288
3409
|
};
|
|
3289
3410
|
const updateDrawingDrag = (x, y) => {
|
|
@@ -3519,7 +3640,7 @@ function createChart(element, options = {}) {
|
|
|
3519
3640
|
setCrosshairPoint(null);
|
|
3520
3641
|
return;
|
|
3521
3642
|
}
|
|
3522
|
-
if (draftDrawing && activeDrawingTool === "trendline") {
|
|
3643
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3523
3644
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3524
3645
|
if (nextPoint) {
|
|
3525
3646
|
draftDrawing = {
|
package/dist/index.d.cts
CHANGED
|
@@ -43,7 +43,7 @@ interface ChartOptions {
|
|
|
43
43
|
drawings?: DrawingObjectOptions[];
|
|
44
44
|
}
|
|
45
45
|
type IndicatorPane = "overlay" | "separate";
|
|
46
|
-
type DrawingToolType = "horizontal-line" | "trendline";
|
|
46
|
+
type DrawingToolType = "horizontal-line" | "trendline" | "fib-retracement";
|
|
47
47
|
interface DrawingPoint {
|
|
48
48
|
index: number;
|
|
49
49
|
price: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ interface ChartOptions {
|
|
|
43
43
|
drawings?: DrawingObjectOptions[];
|
|
44
44
|
}
|
|
45
45
|
type IndicatorPane = "overlay" | "separate";
|
|
46
|
-
type DrawingToolType = "horizontal-line" | "trendline";
|
|
46
|
+
type DrawingToolType = "horizontal-line" | "trendline" | "fib-retracement";
|
|
47
47
|
interface DrawingPoint {
|
|
48
48
|
index: number;
|
|
49
49
|
price: number;
|
package/dist/index.js
CHANGED
|
@@ -1997,6 +1997,16 @@ function createChart(element, options = {}) {
|
|
|
1997
1997
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
1998
1998
|
};
|
|
1999
1999
|
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
2000
|
+
const FIB_LEVELS = [
|
|
2001
|
+
{ ratio: 0, color: "#787b86" },
|
|
2002
|
+
{ ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
|
|
2003
|
+
{ ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
|
|
2004
|
+
{ ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
|
|
2005
|
+
{ ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
|
|
2006
|
+
{ ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
|
|
2007
|
+
{ ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
|
|
2008
|
+
{ ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
|
|
2009
|
+
];
|
|
2000
2010
|
const drawDrawingHandle = (x, y, color) => {
|
|
2001
2011
|
ctx.save();
|
|
2002
2012
|
ctx.setLineDash([]);
|
|
@@ -2073,6 +2083,72 @@ function createChart(element, options = {}) {
|
|
|
2073
2083
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2074
2084
|
}
|
|
2075
2085
|
}
|
|
2086
|
+
} else if (drawing.type === "fib-retracement") {
|
|
2087
|
+
const first = drawing.points[0];
|
|
2088
|
+
const second = drawing.points[1];
|
|
2089
|
+
if (first && second) {
|
|
2090
|
+
const firstX = xFromDrawingPoint(first);
|
|
2091
|
+
const secondX = xFromDrawingPoint(second);
|
|
2092
|
+
const firstY = yFromPrice(first.price);
|
|
2093
|
+
const secondY = yFromPrice(second.price);
|
|
2094
|
+
const lineLeft = chartLeft;
|
|
2095
|
+
const lineRight = chartRight;
|
|
2096
|
+
const levelLines = FIB_LEVELS.map((level) => {
|
|
2097
|
+
const price = first.price + (second.price - first.price) * (1 - level.ratio);
|
|
2098
|
+
return { ...level, price, y: yFromPrice(price) };
|
|
2099
|
+
});
|
|
2100
|
+
for (let index = 0; index < levelLines.length - 1; index += 1) {
|
|
2101
|
+
const top = levelLines[index];
|
|
2102
|
+
const bottom = levelLines[index + 1];
|
|
2103
|
+
const fillColor = bottom.band ?? top.band;
|
|
2104
|
+
if (!fillColor) continue;
|
|
2105
|
+
const bandTop = Math.min(top.y, bottom.y);
|
|
2106
|
+
const bandBottom = Math.max(top.y, bottom.y);
|
|
2107
|
+
ctx.save();
|
|
2108
|
+
ctx.fillStyle = fillColor;
|
|
2109
|
+
ctx.globalAlpha = draft ? 0.5 : 1;
|
|
2110
|
+
ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
|
|
2111
|
+
ctx.restore();
|
|
2112
|
+
}
|
|
2113
|
+
ctx.save();
|
|
2114
|
+
ctx.lineWidth = drawing.width;
|
|
2115
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2116
|
+
for (const level of levelLines) {
|
|
2117
|
+
ctx.strokeStyle = level.color;
|
|
2118
|
+
ctx.beginPath();
|
|
2119
|
+
ctx.moveTo(crisp(lineLeft), crisp(level.y));
|
|
2120
|
+
ctx.lineTo(crisp(lineRight), crisp(level.y));
|
|
2121
|
+
ctx.stroke();
|
|
2122
|
+
}
|
|
2123
|
+
ctx.restore();
|
|
2124
|
+
ctx.save();
|
|
2125
|
+
ctx.strokeStyle = "rgba(148,163,184,0.7)";
|
|
2126
|
+
ctx.setLineDash([4, 4]);
|
|
2127
|
+
ctx.lineWidth = 1;
|
|
2128
|
+
ctx.beginPath();
|
|
2129
|
+
ctx.moveTo(firstX, firstY);
|
|
2130
|
+
ctx.lineTo(secondX, secondY);
|
|
2131
|
+
ctx.stroke();
|
|
2132
|
+
ctx.restore();
|
|
2133
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2134
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2135
|
+
const prevFont = ctx.font;
|
|
2136
|
+
ctx.font = `500 11px ${mergedOptions.fontFamily}`;
|
|
2137
|
+
for (const level of levelLines) {
|
|
2138
|
+
const labelText = `${level.ratio} (${formatPrice(level.price)})`;
|
|
2139
|
+
const textWidth = ctx.measureText(labelText).width;
|
|
2140
|
+
const padding = 4;
|
|
2141
|
+
const bgX = lineLeft + 4;
|
|
2142
|
+
const bgY = level.y - 9;
|
|
2143
|
+
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
2144
|
+
fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
|
|
2145
|
+
ctx.fillStyle = level.color;
|
|
2146
|
+
ctx.textAlign = "left";
|
|
2147
|
+
ctx.textBaseline = "middle";
|
|
2148
|
+
ctx.fillText(labelText, bgX + padding, bgY + 8);
|
|
2149
|
+
}
|
|
2150
|
+
ctx.font = prevFont;
|
|
2151
|
+
}
|
|
2076
2152
|
}
|
|
2077
2153
|
ctx.restore();
|
|
2078
2154
|
};
|
|
@@ -3159,6 +3235,28 @@ function createChart(element, options = {}) {
|
|
|
3159
3235
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3160
3236
|
return { drawing, target: "line" };
|
|
3161
3237
|
}
|
|
3238
|
+
} else if (drawing.type === "fib-retracement") {
|
|
3239
|
+
const first = drawing.points[0];
|
|
3240
|
+
const second = drawing.points[1];
|
|
3241
|
+
if (!first || !second) continue;
|
|
3242
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3243
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3244
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3245
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3246
|
+
if (Math.hypot(x - x1, y - y1) <= 9) {
|
|
3247
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3248
|
+
}
|
|
3249
|
+
if (Math.hypot(x - x2, y - y2) <= 9) {
|
|
3250
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3251
|
+
}
|
|
3252
|
+
const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
3253
|
+
for (const ratio of fibRatios) {
|
|
3254
|
+
const price = first.price + (second.price - first.price) * (1 - ratio);
|
|
3255
|
+
const lineY = canvasYFromDrawingPrice(price);
|
|
3256
|
+
if (Math.abs(y - lineY) <= 5) {
|
|
3257
|
+
return { drawing, target: "line" };
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3162
3260
|
}
|
|
3163
3261
|
}
|
|
3164
3262
|
return null;
|
|
@@ -3260,6 +3358,29 @@ function createChart(element, options = {}) {
|
|
|
3260
3358
|
draw();
|
|
3261
3359
|
return true;
|
|
3262
3360
|
}
|
|
3361
|
+
if (activeDrawingTool === "fib-retracement") {
|
|
3362
|
+
if (draftDrawing?.type === "fib-retracement") {
|
|
3363
|
+
const completed = normalizeDrawingState({
|
|
3364
|
+
...serializeDrawing(draftDrawing),
|
|
3365
|
+
points: [draftDrawing.points[0], point]
|
|
3366
|
+
});
|
|
3367
|
+
drawings.push(completed);
|
|
3368
|
+
draftDrawing = null;
|
|
3369
|
+
activeDrawingTool = null;
|
|
3370
|
+
emitDrawingsChange();
|
|
3371
|
+
draw();
|
|
3372
|
+
return true;
|
|
3373
|
+
}
|
|
3374
|
+
draftDrawing = normalizeDrawingState({
|
|
3375
|
+
type: "fib-retracement",
|
|
3376
|
+
points: [point, point],
|
|
3377
|
+
color: "#2563eb",
|
|
3378
|
+
style: "solid",
|
|
3379
|
+
width: 1
|
|
3380
|
+
});
|
|
3381
|
+
draw();
|
|
3382
|
+
return true;
|
|
3383
|
+
}
|
|
3263
3384
|
return false;
|
|
3264
3385
|
};
|
|
3265
3386
|
const updateDrawingDrag = (x, y) => {
|
|
@@ -3495,7 +3616,7 @@ function createChart(element, options = {}) {
|
|
|
3495
3616
|
setCrosshairPoint(null);
|
|
3496
3617
|
return;
|
|
3497
3618
|
}
|
|
3498
|
-
if (draftDrawing && activeDrawingTool === "trendline") {
|
|
3619
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3499
3620
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3500
3621
|
if (nextPoint) {
|
|
3501
3622
|
draftDrawing = {
|