hyperprop-charting-library 0.1.59 → 0.1.61

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.
@@ -964,6 +964,8 @@ function createChart(element, options = {}) {
964
964
  let drawingSelectHandler = null;
965
965
  let drawingHoverHandler = null;
966
966
  let lastHoveredDrawingId = null;
967
+ const drawingToolDefaults = /* @__PURE__ */ new Map();
968
+ const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
967
969
  const emitDrawingsChange = () => {
968
970
  drawingsChangeHandler?.(drawings.map((drawing) => serializeDrawing(drawing)));
969
971
  };
@@ -2021,6 +2023,16 @@ function createChart(element, options = {}) {
2021
2023
  return chartBottom - (price - yMin) / yRange * chartHeight;
2022
2024
  };
2023
2025
  const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
2026
+ const FIB_LEVELS = [
2027
+ { ratio: 0, color: "#787b86" },
2028
+ { ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
2029
+ { ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
2030
+ { ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
2031
+ { ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
2032
+ { ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
2033
+ { ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
2034
+ { ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
2035
+ ];
2024
2036
  const drawDrawingHandle = (x, y, color) => {
2025
2037
  ctx.save();
2026
2038
  ctx.setLineDash([]);
@@ -2097,6 +2109,72 @@ function createChart(element, options = {}) {
2097
2109
  drawDrawingLabel(drawing.label, midX, midY, drawing.color);
2098
2110
  }
2099
2111
  }
2112
+ } else if (drawing.type === "fib-retracement") {
2113
+ const first = drawing.points[0];
2114
+ const second = drawing.points[1];
2115
+ if (first && second) {
2116
+ const firstX = xFromDrawingPoint(first);
2117
+ const secondX = xFromDrawingPoint(second);
2118
+ const firstY = yFromPrice(first.price);
2119
+ const secondY = yFromPrice(second.price);
2120
+ const lineLeft = chartLeft;
2121
+ const lineRight = chartRight;
2122
+ const levelLines = FIB_LEVELS.map((level) => {
2123
+ const price = first.price + (second.price - first.price) * (1 - level.ratio);
2124
+ return { ...level, price, y: yFromPrice(price) };
2125
+ });
2126
+ for (let index = 0; index < levelLines.length - 1; index += 1) {
2127
+ const top = levelLines[index];
2128
+ const bottom = levelLines[index + 1];
2129
+ const fillColor = bottom.band ?? top.band;
2130
+ if (!fillColor) continue;
2131
+ const bandTop = Math.min(top.y, bottom.y);
2132
+ const bandBottom = Math.max(top.y, bottom.y);
2133
+ ctx.save();
2134
+ ctx.fillStyle = fillColor;
2135
+ ctx.globalAlpha = draft ? 0.5 : 1;
2136
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
2137
+ ctx.restore();
2138
+ }
2139
+ ctx.save();
2140
+ ctx.lineWidth = drawing.width;
2141
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2142
+ for (const level of levelLines) {
2143
+ ctx.strokeStyle = level.color;
2144
+ ctx.beginPath();
2145
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
2146
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
2147
+ ctx.stroke();
2148
+ }
2149
+ ctx.restore();
2150
+ ctx.save();
2151
+ ctx.strokeStyle = "rgba(148,163,184,0.7)";
2152
+ ctx.setLineDash([4, 4]);
2153
+ ctx.lineWidth = 1;
2154
+ ctx.beginPath();
2155
+ ctx.moveTo(firstX, firstY);
2156
+ ctx.lineTo(secondX, secondY);
2157
+ ctx.stroke();
2158
+ ctx.restore();
2159
+ drawDrawingHandle(firstX, firstY, drawing.color);
2160
+ drawDrawingHandle(secondX, secondY, drawing.color);
2161
+ const prevFont = ctx.font;
2162
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2163
+ for (const level of levelLines) {
2164
+ const labelText = `${level.ratio} (${formatPrice(level.price)})`;
2165
+ const textWidth = ctx.measureText(labelText).width;
2166
+ const padding = 4;
2167
+ const bgX = lineLeft + 4;
2168
+ const bgY = level.y - 9;
2169
+ ctx.fillStyle = mergedOptions.backgroundColor;
2170
+ fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
2171
+ ctx.fillStyle = level.color;
2172
+ ctx.textAlign = "left";
2173
+ ctx.textBaseline = "middle";
2174
+ ctx.fillText(labelText, bgX + padding, bgY + 8);
2175
+ }
2176
+ ctx.font = prevFont;
2177
+ }
2100
2178
  }
2101
2179
  ctx.restore();
2102
2180
  };
@@ -3183,6 +3261,28 @@ function createChart(element, options = {}) {
3183
3261
  if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
3184
3262
  return { drawing, target: "line" };
3185
3263
  }
3264
+ } else if (drawing.type === "fib-retracement") {
3265
+ const first = drawing.points[0];
3266
+ const second = drawing.points[1];
3267
+ if (!first || !second) continue;
3268
+ const x1 = canvasXFromDrawingPoint(first);
3269
+ const y1 = canvasYFromDrawingPrice(first.price);
3270
+ const x2 = canvasXFromDrawingPoint(second);
3271
+ const y2 = canvasYFromDrawingPrice(second.price);
3272
+ if (Math.hypot(x - x1, y - y1) <= 9) {
3273
+ return { drawing, target: "handle", pointIndex: 0 };
3274
+ }
3275
+ if (Math.hypot(x - x2, y - y2) <= 9) {
3276
+ return { drawing, target: "handle", pointIndex: 1 };
3277
+ }
3278
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
3279
+ for (const ratio of fibRatios) {
3280
+ const price = first.price + (second.price - first.price) * (1 - ratio);
3281
+ const lineY = canvasYFromDrawingPrice(price);
3282
+ if (Math.abs(y - lineY) <= 5) {
3283
+ return { drawing, target: "line" };
3284
+ }
3285
+ }
3186
3286
  }
3187
3287
  }
3188
3288
  return null;
@@ -3248,13 +3348,14 @@ function createChart(element, options = {}) {
3248
3348
  return false;
3249
3349
  }
3250
3350
  if (activeDrawingTool === "horizontal-line") {
3351
+ const defaults = getDrawingToolDefaults("horizontal-line");
3251
3352
  drawings.push(
3252
3353
  normalizeDrawingState({
3253
3354
  type: "horizontal-line",
3254
3355
  points: [point],
3255
- color: "#38bdf8",
3256
- style: "dotted",
3257
- width: 1
3356
+ color: defaults.color ?? "#38bdf8",
3357
+ style: defaults.style ?? "solid",
3358
+ width: defaults.width ?? 1
3258
3359
  })
3259
3360
  );
3260
3361
  emitDrawingsChange();
@@ -3274,18 +3375,50 @@ function createChart(element, options = {}) {
3274
3375
  draw();
3275
3376
  return true;
3276
3377
  }
3378
+ const defaults = getDrawingToolDefaults("trendline");
3277
3379
  draftDrawing = normalizeDrawingState({
3278
3380
  type: "trendline",
3279
3381
  points: [point, point],
3280
- color: "#2563eb",
3281
- style: "solid",
3282
- width: 2
3382
+ color: defaults.color ?? "#2563eb",
3383
+ style: defaults.style ?? "solid",
3384
+ width: defaults.width ?? 2
3385
+ });
3386
+ draw();
3387
+ return true;
3388
+ }
3389
+ if (activeDrawingTool === "fib-retracement") {
3390
+ if (draftDrawing?.type === "fib-retracement") {
3391
+ const completed = normalizeDrawingState({
3392
+ ...serializeDrawing(draftDrawing),
3393
+ points: [draftDrawing.points[0], point]
3394
+ });
3395
+ drawings.push(completed);
3396
+ draftDrawing = null;
3397
+ activeDrawingTool = null;
3398
+ emitDrawingsChange();
3399
+ draw();
3400
+ return true;
3401
+ }
3402
+ const defaults = getDrawingToolDefaults("fib-retracement");
3403
+ draftDrawing = normalizeDrawingState({
3404
+ type: "fib-retracement",
3405
+ points: [point, point],
3406
+ color: defaults.color ?? "#2563eb",
3407
+ style: defaults.style ?? "solid",
3408
+ width: defaults.width ?? 1
3283
3409
  });
3284
3410
  draw();
3285
3411
  return true;
3286
3412
  }
3287
3413
  return false;
3288
3414
  };
3415
+ const setDrawingDefaults = (tool, defaults) => {
3416
+ if (defaults === null) {
3417
+ drawingToolDefaults.delete(tool);
3418
+ return;
3419
+ }
3420
+ drawingToolDefaults.set(tool, { ...defaults });
3421
+ };
3289
3422
  const updateDrawingDrag = (x, y) => {
3290
3423
  if (!drawingDragState) {
3291
3424
  return false;
@@ -3519,7 +3652,7 @@ function createChart(element, options = {}) {
3519
3652
  setCrosshairPoint(null);
3520
3653
  return;
3521
3654
  }
3522
- if (draftDrawing && activeDrawingTool === "trendline") {
3655
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
3523
3656
  const nextPoint = drawingPointFromCanvas(point.x, point.y);
3524
3657
  if (nextPoint) {
3525
3658
  draftDrawing = {
@@ -4134,6 +4267,7 @@ function createChart(element, options = {}) {
4134
4267
  onDrawingsChange,
4135
4268
  onDrawingSelect,
4136
4269
  onDrawingHover,
4270
+ setDrawingDefaults,
4137
4271
  setDoubleClickEnabled,
4138
4272
  setDoubleClickAction,
4139
4273
  registerIndicator,
@@ -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;
@@ -73,6 +73,7 @@ interface DrawingHoverEvent {
73
73
  x: number;
74
74
  y: number;
75
75
  }
76
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "style" | "width">>;
76
77
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
77
78
  id?: string;
78
79
  type: string;
@@ -405,6 +406,7 @@ interface ChartInstance {
405
406
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
406
407
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
407
408
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
409
+ setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
408
410
  setDoubleClickEnabled: (enabled: boolean) => void;
409
411
  setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
410
412
  registerIndicator: (plugin: IndicatorPlugin<any>) => void;
@@ -440,4 +442,4 @@ interface ViewportState {
440
442
  }
441
443
  declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
442
444
 
443
- export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
445
+ export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
@@ -940,6 +940,8 @@ function createChart(element, options = {}) {
940
940
  let drawingSelectHandler = null;
941
941
  let drawingHoverHandler = null;
942
942
  let lastHoveredDrawingId = null;
943
+ const drawingToolDefaults = /* @__PURE__ */ new Map();
944
+ const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
943
945
  const emitDrawingsChange = () => {
944
946
  drawingsChangeHandler?.(drawings.map((drawing) => serializeDrawing(drawing)));
945
947
  };
@@ -1997,6 +1999,16 @@ function createChart(element, options = {}) {
1997
1999
  return chartBottom - (price - yMin) / yRange * chartHeight;
1998
2000
  };
1999
2001
  const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
2002
+ const FIB_LEVELS = [
2003
+ { ratio: 0, color: "#787b86" },
2004
+ { ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
2005
+ { ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
2006
+ { ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
2007
+ { ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
2008
+ { ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
2009
+ { ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
2010
+ { ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
2011
+ ];
2000
2012
  const drawDrawingHandle = (x, y, color) => {
2001
2013
  ctx.save();
2002
2014
  ctx.setLineDash([]);
@@ -2073,6 +2085,72 @@ function createChart(element, options = {}) {
2073
2085
  drawDrawingLabel(drawing.label, midX, midY, drawing.color);
2074
2086
  }
2075
2087
  }
2088
+ } else if (drawing.type === "fib-retracement") {
2089
+ const first = drawing.points[0];
2090
+ const second = drawing.points[1];
2091
+ if (first && second) {
2092
+ const firstX = xFromDrawingPoint(first);
2093
+ const secondX = xFromDrawingPoint(second);
2094
+ const firstY = yFromPrice(first.price);
2095
+ const secondY = yFromPrice(second.price);
2096
+ const lineLeft = chartLeft;
2097
+ const lineRight = chartRight;
2098
+ const levelLines = FIB_LEVELS.map((level) => {
2099
+ const price = first.price + (second.price - first.price) * (1 - level.ratio);
2100
+ return { ...level, price, y: yFromPrice(price) };
2101
+ });
2102
+ for (let index = 0; index < levelLines.length - 1; index += 1) {
2103
+ const top = levelLines[index];
2104
+ const bottom = levelLines[index + 1];
2105
+ const fillColor = bottom.band ?? top.band;
2106
+ if (!fillColor) continue;
2107
+ const bandTop = Math.min(top.y, bottom.y);
2108
+ const bandBottom = Math.max(top.y, bottom.y);
2109
+ ctx.save();
2110
+ ctx.fillStyle = fillColor;
2111
+ ctx.globalAlpha = draft ? 0.5 : 1;
2112
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
2113
+ ctx.restore();
2114
+ }
2115
+ ctx.save();
2116
+ ctx.lineWidth = drawing.width;
2117
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2118
+ for (const level of levelLines) {
2119
+ ctx.strokeStyle = level.color;
2120
+ ctx.beginPath();
2121
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
2122
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
2123
+ ctx.stroke();
2124
+ }
2125
+ ctx.restore();
2126
+ ctx.save();
2127
+ ctx.strokeStyle = "rgba(148,163,184,0.7)";
2128
+ ctx.setLineDash([4, 4]);
2129
+ ctx.lineWidth = 1;
2130
+ ctx.beginPath();
2131
+ ctx.moveTo(firstX, firstY);
2132
+ ctx.lineTo(secondX, secondY);
2133
+ ctx.stroke();
2134
+ ctx.restore();
2135
+ drawDrawingHandle(firstX, firstY, drawing.color);
2136
+ drawDrawingHandle(secondX, secondY, drawing.color);
2137
+ const prevFont = ctx.font;
2138
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2139
+ for (const level of levelLines) {
2140
+ const labelText = `${level.ratio} (${formatPrice(level.price)})`;
2141
+ const textWidth = ctx.measureText(labelText).width;
2142
+ const padding = 4;
2143
+ const bgX = lineLeft + 4;
2144
+ const bgY = level.y - 9;
2145
+ ctx.fillStyle = mergedOptions.backgroundColor;
2146
+ fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
2147
+ ctx.fillStyle = level.color;
2148
+ ctx.textAlign = "left";
2149
+ ctx.textBaseline = "middle";
2150
+ ctx.fillText(labelText, bgX + padding, bgY + 8);
2151
+ }
2152
+ ctx.font = prevFont;
2153
+ }
2076
2154
  }
2077
2155
  ctx.restore();
2078
2156
  };
@@ -3159,6 +3237,28 @@ function createChart(element, options = {}) {
3159
3237
  if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
3160
3238
  return { drawing, target: "line" };
3161
3239
  }
3240
+ } else if (drawing.type === "fib-retracement") {
3241
+ const first = drawing.points[0];
3242
+ const second = drawing.points[1];
3243
+ if (!first || !second) continue;
3244
+ const x1 = canvasXFromDrawingPoint(first);
3245
+ const y1 = canvasYFromDrawingPrice(first.price);
3246
+ const x2 = canvasXFromDrawingPoint(second);
3247
+ const y2 = canvasYFromDrawingPrice(second.price);
3248
+ if (Math.hypot(x - x1, y - y1) <= 9) {
3249
+ return { drawing, target: "handle", pointIndex: 0 };
3250
+ }
3251
+ if (Math.hypot(x - x2, y - y2) <= 9) {
3252
+ return { drawing, target: "handle", pointIndex: 1 };
3253
+ }
3254
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
3255
+ for (const ratio of fibRatios) {
3256
+ const price = first.price + (second.price - first.price) * (1 - ratio);
3257
+ const lineY = canvasYFromDrawingPrice(price);
3258
+ if (Math.abs(y - lineY) <= 5) {
3259
+ return { drawing, target: "line" };
3260
+ }
3261
+ }
3162
3262
  }
3163
3263
  }
3164
3264
  return null;
@@ -3224,13 +3324,14 @@ function createChart(element, options = {}) {
3224
3324
  return false;
3225
3325
  }
3226
3326
  if (activeDrawingTool === "horizontal-line") {
3327
+ const defaults = getDrawingToolDefaults("horizontal-line");
3227
3328
  drawings.push(
3228
3329
  normalizeDrawingState({
3229
3330
  type: "horizontal-line",
3230
3331
  points: [point],
3231
- color: "#38bdf8",
3232
- style: "dotted",
3233
- width: 1
3332
+ color: defaults.color ?? "#38bdf8",
3333
+ style: defaults.style ?? "solid",
3334
+ width: defaults.width ?? 1
3234
3335
  })
3235
3336
  );
3236
3337
  emitDrawingsChange();
@@ -3250,18 +3351,50 @@ function createChart(element, options = {}) {
3250
3351
  draw();
3251
3352
  return true;
3252
3353
  }
3354
+ const defaults = getDrawingToolDefaults("trendline");
3253
3355
  draftDrawing = normalizeDrawingState({
3254
3356
  type: "trendline",
3255
3357
  points: [point, point],
3256
- color: "#2563eb",
3257
- style: "solid",
3258
- width: 2
3358
+ color: defaults.color ?? "#2563eb",
3359
+ style: defaults.style ?? "solid",
3360
+ width: defaults.width ?? 2
3361
+ });
3362
+ draw();
3363
+ return true;
3364
+ }
3365
+ if (activeDrawingTool === "fib-retracement") {
3366
+ if (draftDrawing?.type === "fib-retracement") {
3367
+ const completed = normalizeDrawingState({
3368
+ ...serializeDrawing(draftDrawing),
3369
+ points: [draftDrawing.points[0], point]
3370
+ });
3371
+ drawings.push(completed);
3372
+ draftDrawing = null;
3373
+ activeDrawingTool = null;
3374
+ emitDrawingsChange();
3375
+ draw();
3376
+ return true;
3377
+ }
3378
+ const defaults = getDrawingToolDefaults("fib-retracement");
3379
+ draftDrawing = normalizeDrawingState({
3380
+ type: "fib-retracement",
3381
+ points: [point, point],
3382
+ color: defaults.color ?? "#2563eb",
3383
+ style: defaults.style ?? "solid",
3384
+ width: defaults.width ?? 1
3259
3385
  });
3260
3386
  draw();
3261
3387
  return true;
3262
3388
  }
3263
3389
  return false;
3264
3390
  };
3391
+ const setDrawingDefaults = (tool, defaults) => {
3392
+ if (defaults === null) {
3393
+ drawingToolDefaults.delete(tool);
3394
+ return;
3395
+ }
3396
+ drawingToolDefaults.set(tool, { ...defaults });
3397
+ };
3265
3398
  const updateDrawingDrag = (x, y) => {
3266
3399
  if (!drawingDragState) {
3267
3400
  return false;
@@ -3495,7 +3628,7 @@ function createChart(element, options = {}) {
3495
3628
  setCrosshairPoint(null);
3496
3629
  return;
3497
3630
  }
3498
- if (draftDrawing && activeDrawingTool === "trendline") {
3631
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
3499
3632
  const nextPoint = drawingPointFromCanvas(point.x, point.y);
3500
3633
  if (nextPoint) {
3501
3634
  draftDrawing = {
@@ -4110,6 +4243,7 @@ function createChart(element, options = {}) {
4110
4243
  onDrawingsChange,
4111
4244
  onDrawingSelect,
4112
4245
  onDrawingHover,
4246
+ setDrawingDefaults,
4113
4247
  setDoubleClickEnabled,
4114
4248
  setDoubleClickAction,
4115
4249
  registerIndicator,
package/dist/index.cjs CHANGED
@@ -964,6 +964,8 @@ function createChart(element, options = {}) {
964
964
  let drawingSelectHandler = null;
965
965
  let drawingHoverHandler = null;
966
966
  let lastHoveredDrawingId = null;
967
+ const drawingToolDefaults = /* @__PURE__ */ new Map();
968
+ const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
967
969
  const emitDrawingsChange = () => {
968
970
  drawingsChangeHandler?.(drawings.map((drawing) => serializeDrawing(drawing)));
969
971
  };
@@ -2021,6 +2023,16 @@ function createChart(element, options = {}) {
2021
2023
  return chartBottom - (price - yMin) / yRange * chartHeight;
2022
2024
  };
2023
2025
  const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
2026
+ const FIB_LEVELS = [
2027
+ { ratio: 0, color: "#787b86" },
2028
+ { ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
2029
+ { ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
2030
+ { ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
2031
+ { ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
2032
+ { ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
2033
+ { ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
2034
+ { ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
2035
+ ];
2024
2036
  const drawDrawingHandle = (x, y, color) => {
2025
2037
  ctx.save();
2026
2038
  ctx.setLineDash([]);
@@ -2097,6 +2109,72 @@ function createChart(element, options = {}) {
2097
2109
  drawDrawingLabel(drawing.label, midX, midY, drawing.color);
2098
2110
  }
2099
2111
  }
2112
+ } else if (drawing.type === "fib-retracement") {
2113
+ const first = drawing.points[0];
2114
+ const second = drawing.points[1];
2115
+ if (first && second) {
2116
+ const firstX = xFromDrawingPoint(first);
2117
+ const secondX = xFromDrawingPoint(second);
2118
+ const firstY = yFromPrice(first.price);
2119
+ const secondY = yFromPrice(second.price);
2120
+ const lineLeft = chartLeft;
2121
+ const lineRight = chartRight;
2122
+ const levelLines = FIB_LEVELS.map((level) => {
2123
+ const price = first.price + (second.price - first.price) * (1 - level.ratio);
2124
+ return { ...level, price, y: yFromPrice(price) };
2125
+ });
2126
+ for (let index = 0; index < levelLines.length - 1; index += 1) {
2127
+ const top = levelLines[index];
2128
+ const bottom = levelLines[index + 1];
2129
+ const fillColor = bottom.band ?? top.band;
2130
+ if (!fillColor) continue;
2131
+ const bandTop = Math.min(top.y, bottom.y);
2132
+ const bandBottom = Math.max(top.y, bottom.y);
2133
+ ctx.save();
2134
+ ctx.fillStyle = fillColor;
2135
+ ctx.globalAlpha = draft ? 0.5 : 1;
2136
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
2137
+ ctx.restore();
2138
+ }
2139
+ ctx.save();
2140
+ ctx.lineWidth = drawing.width;
2141
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2142
+ for (const level of levelLines) {
2143
+ ctx.strokeStyle = level.color;
2144
+ ctx.beginPath();
2145
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
2146
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
2147
+ ctx.stroke();
2148
+ }
2149
+ ctx.restore();
2150
+ ctx.save();
2151
+ ctx.strokeStyle = "rgba(148,163,184,0.7)";
2152
+ ctx.setLineDash([4, 4]);
2153
+ ctx.lineWidth = 1;
2154
+ ctx.beginPath();
2155
+ ctx.moveTo(firstX, firstY);
2156
+ ctx.lineTo(secondX, secondY);
2157
+ ctx.stroke();
2158
+ ctx.restore();
2159
+ drawDrawingHandle(firstX, firstY, drawing.color);
2160
+ drawDrawingHandle(secondX, secondY, drawing.color);
2161
+ const prevFont = ctx.font;
2162
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2163
+ for (const level of levelLines) {
2164
+ const labelText = `${level.ratio} (${formatPrice(level.price)})`;
2165
+ const textWidth = ctx.measureText(labelText).width;
2166
+ const padding = 4;
2167
+ const bgX = lineLeft + 4;
2168
+ const bgY = level.y - 9;
2169
+ ctx.fillStyle = mergedOptions.backgroundColor;
2170
+ fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
2171
+ ctx.fillStyle = level.color;
2172
+ ctx.textAlign = "left";
2173
+ ctx.textBaseline = "middle";
2174
+ ctx.fillText(labelText, bgX + padding, bgY + 8);
2175
+ }
2176
+ ctx.font = prevFont;
2177
+ }
2100
2178
  }
2101
2179
  ctx.restore();
2102
2180
  };
@@ -3183,6 +3261,28 @@ function createChart(element, options = {}) {
3183
3261
  if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
3184
3262
  return { drawing, target: "line" };
3185
3263
  }
3264
+ } else if (drawing.type === "fib-retracement") {
3265
+ const first = drawing.points[0];
3266
+ const second = drawing.points[1];
3267
+ if (!first || !second) continue;
3268
+ const x1 = canvasXFromDrawingPoint(first);
3269
+ const y1 = canvasYFromDrawingPrice(first.price);
3270
+ const x2 = canvasXFromDrawingPoint(second);
3271
+ const y2 = canvasYFromDrawingPrice(second.price);
3272
+ if (Math.hypot(x - x1, y - y1) <= 9) {
3273
+ return { drawing, target: "handle", pointIndex: 0 };
3274
+ }
3275
+ if (Math.hypot(x - x2, y - y2) <= 9) {
3276
+ return { drawing, target: "handle", pointIndex: 1 };
3277
+ }
3278
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
3279
+ for (const ratio of fibRatios) {
3280
+ const price = first.price + (second.price - first.price) * (1 - ratio);
3281
+ const lineY = canvasYFromDrawingPrice(price);
3282
+ if (Math.abs(y - lineY) <= 5) {
3283
+ return { drawing, target: "line" };
3284
+ }
3285
+ }
3186
3286
  }
3187
3287
  }
3188
3288
  return null;
@@ -3248,13 +3348,14 @@ function createChart(element, options = {}) {
3248
3348
  return false;
3249
3349
  }
3250
3350
  if (activeDrawingTool === "horizontal-line") {
3351
+ const defaults = getDrawingToolDefaults("horizontal-line");
3251
3352
  drawings.push(
3252
3353
  normalizeDrawingState({
3253
3354
  type: "horizontal-line",
3254
3355
  points: [point],
3255
- color: "#38bdf8",
3256
- style: "dotted",
3257
- width: 1
3356
+ color: defaults.color ?? "#38bdf8",
3357
+ style: defaults.style ?? "solid",
3358
+ width: defaults.width ?? 1
3258
3359
  })
3259
3360
  );
3260
3361
  emitDrawingsChange();
@@ -3274,18 +3375,50 @@ function createChart(element, options = {}) {
3274
3375
  draw();
3275
3376
  return true;
3276
3377
  }
3378
+ const defaults = getDrawingToolDefaults("trendline");
3277
3379
  draftDrawing = normalizeDrawingState({
3278
3380
  type: "trendline",
3279
3381
  points: [point, point],
3280
- color: "#2563eb",
3281
- style: "solid",
3282
- width: 2
3382
+ color: defaults.color ?? "#2563eb",
3383
+ style: defaults.style ?? "solid",
3384
+ width: defaults.width ?? 2
3385
+ });
3386
+ draw();
3387
+ return true;
3388
+ }
3389
+ if (activeDrawingTool === "fib-retracement") {
3390
+ if (draftDrawing?.type === "fib-retracement") {
3391
+ const completed = normalizeDrawingState({
3392
+ ...serializeDrawing(draftDrawing),
3393
+ points: [draftDrawing.points[0], point]
3394
+ });
3395
+ drawings.push(completed);
3396
+ draftDrawing = null;
3397
+ activeDrawingTool = null;
3398
+ emitDrawingsChange();
3399
+ draw();
3400
+ return true;
3401
+ }
3402
+ const defaults = getDrawingToolDefaults("fib-retracement");
3403
+ draftDrawing = normalizeDrawingState({
3404
+ type: "fib-retracement",
3405
+ points: [point, point],
3406
+ color: defaults.color ?? "#2563eb",
3407
+ style: defaults.style ?? "solid",
3408
+ width: defaults.width ?? 1
3283
3409
  });
3284
3410
  draw();
3285
3411
  return true;
3286
3412
  }
3287
3413
  return false;
3288
3414
  };
3415
+ const setDrawingDefaults = (tool, defaults) => {
3416
+ if (defaults === null) {
3417
+ drawingToolDefaults.delete(tool);
3418
+ return;
3419
+ }
3420
+ drawingToolDefaults.set(tool, { ...defaults });
3421
+ };
3289
3422
  const updateDrawingDrag = (x, y) => {
3290
3423
  if (!drawingDragState) {
3291
3424
  return false;
@@ -3519,7 +3652,7 @@ function createChart(element, options = {}) {
3519
3652
  setCrosshairPoint(null);
3520
3653
  return;
3521
3654
  }
3522
- if (draftDrawing && activeDrawingTool === "trendline") {
3655
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
3523
3656
  const nextPoint = drawingPointFromCanvas(point.x, point.y);
3524
3657
  if (nextPoint) {
3525
3658
  draftDrawing = {
@@ -4134,6 +4267,7 @@ function createChart(element, options = {}) {
4134
4267
  onDrawingsChange,
4135
4268
  onDrawingSelect,
4136
4269
  onDrawingHover,
4270
+ setDrawingDefaults,
4137
4271
  setDoubleClickEnabled,
4138
4272
  setDoubleClickAction,
4139
4273
  registerIndicator,
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;
@@ -73,6 +73,7 @@ interface DrawingHoverEvent {
73
73
  x: number;
74
74
  y: number;
75
75
  }
76
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "style" | "width">>;
76
77
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
77
78
  id?: string;
78
79
  type: string;
@@ -405,6 +406,7 @@ interface ChartInstance {
405
406
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
406
407
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
407
408
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
409
+ setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
408
410
  setDoubleClickEnabled: (enabled: boolean) => void;
409
411
  setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
410
412
  registerIndicator: (plugin: IndicatorPlugin<any>) => void;
@@ -440,4 +442,4 @@ interface ViewportState {
440
442
  }
441
443
  declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
442
444
 
443
- export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
445
+ export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
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;
@@ -73,6 +73,7 @@ interface DrawingHoverEvent {
73
73
  x: number;
74
74
  y: number;
75
75
  }
76
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "style" | "width">>;
76
77
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
77
78
  id?: string;
78
79
  type: string;
@@ -405,6 +406,7 @@ interface ChartInstance {
405
406
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
406
407
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
407
408
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
409
+ setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
408
410
  setDoubleClickEnabled: (enabled: boolean) => void;
409
411
  setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
410
412
  registerIndicator: (plugin: IndicatorPlugin<any>) => void;
@@ -440,4 +442,4 @@ interface ViewportState {
440
442
  }
441
443
  declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
442
444
 
443
- export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
445
+ export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
package/dist/index.js CHANGED
@@ -940,6 +940,8 @@ function createChart(element, options = {}) {
940
940
  let drawingSelectHandler = null;
941
941
  let drawingHoverHandler = null;
942
942
  let lastHoveredDrawingId = null;
943
+ const drawingToolDefaults = /* @__PURE__ */ new Map();
944
+ const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
943
945
  const emitDrawingsChange = () => {
944
946
  drawingsChangeHandler?.(drawings.map((drawing) => serializeDrawing(drawing)));
945
947
  };
@@ -1997,6 +1999,16 @@ function createChart(element, options = {}) {
1997
1999
  return chartBottom - (price - yMin) / yRange * chartHeight;
1998
2000
  };
1999
2001
  const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
2002
+ const FIB_LEVELS = [
2003
+ { ratio: 0, color: "#787b86" },
2004
+ { ratio: 0.236, color: "#f23645", band: "rgba(242,54,69,0.10)" },
2005
+ { ratio: 0.382, color: "#ff9800", band: "rgba(255,152,0,0.10)" },
2006
+ { ratio: 0.5, color: "#4caf50", band: "rgba(76,175,80,0.10)" },
2007
+ { ratio: 0.618, color: "#089981", band: "rgba(8,153,129,0.10)" },
2008
+ { ratio: 0.786, color: "#00bcd4", band: "rgba(0,188,212,0.10)" },
2009
+ { ratio: 1, color: "#787b86", band: "rgba(120,123,134,0.10)" },
2010
+ { ratio: 1.618, color: "#2962ff", band: "rgba(41,98,255,0.08)" }
2011
+ ];
2000
2012
  const drawDrawingHandle = (x, y, color) => {
2001
2013
  ctx.save();
2002
2014
  ctx.setLineDash([]);
@@ -2073,6 +2085,72 @@ function createChart(element, options = {}) {
2073
2085
  drawDrawingLabel(drawing.label, midX, midY, drawing.color);
2074
2086
  }
2075
2087
  }
2088
+ } else if (drawing.type === "fib-retracement") {
2089
+ const first = drawing.points[0];
2090
+ const second = drawing.points[1];
2091
+ if (first && second) {
2092
+ const firstX = xFromDrawingPoint(first);
2093
+ const secondX = xFromDrawingPoint(second);
2094
+ const firstY = yFromPrice(first.price);
2095
+ const secondY = yFromPrice(second.price);
2096
+ const lineLeft = chartLeft;
2097
+ const lineRight = chartRight;
2098
+ const levelLines = FIB_LEVELS.map((level) => {
2099
+ const price = first.price + (second.price - first.price) * (1 - level.ratio);
2100
+ return { ...level, price, y: yFromPrice(price) };
2101
+ });
2102
+ for (let index = 0; index < levelLines.length - 1; index += 1) {
2103
+ const top = levelLines[index];
2104
+ const bottom = levelLines[index + 1];
2105
+ const fillColor = bottom.band ?? top.band;
2106
+ if (!fillColor) continue;
2107
+ const bandTop = Math.min(top.y, bottom.y);
2108
+ const bandBottom = Math.max(top.y, bottom.y);
2109
+ ctx.save();
2110
+ ctx.fillStyle = fillColor;
2111
+ ctx.globalAlpha = draft ? 0.5 : 1;
2112
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
2113
+ ctx.restore();
2114
+ }
2115
+ ctx.save();
2116
+ ctx.lineWidth = drawing.width;
2117
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2118
+ for (const level of levelLines) {
2119
+ ctx.strokeStyle = level.color;
2120
+ ctx.beginPath();
2121
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
2122
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
2123
+ ctx.stroke();
2124
+ }
2125
+ ctx.restore();
2126
+ ctx.save();
2127
+ ctx.strokeStyle = "rgba(148,163,184,0.7)";
2128
+ ctx.setLineDash([4, 4]);
2129
+ ctx.lineWidth = 1;
2130
+ ctx.beginPath();
2131
+ ctx.moveTo(firstX, firstY);
2132
+ ctx.lineTo(secondX, secondY);
2133
+ ctx.stroke();
2134
+ ctx.restore();
2135
+ drawDrawingHandle(firstX, firstY, drawing.color);
2136
+ drawDrawingHandle(secondX, secondY, drawing.color);
2137
+ const prevFont = ctx.font;
2138
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2139
+ for (const level of levelLines) {
2140
+ const labelText = `${level.ratio} (${formatPrice(level.price)})`;
2141
+ const textWidth = ctx.measureText(labelText).width;
2142
+ const padding = 4;
2143
+ const bgX = lineLeft + 4;
2144
+ const bgY = level.y - 9;
2145
+ ctx.fillStyle = mergedOptions.backgroundColor;
2146
+ fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
2147
+ ctx.fillStyle = level.color;
2148
+ ctx.textAlign = "left";
2149
+ ctx.textBaseline = "middle";
2150
+ ctx.fillText(labelText, bgX + padding, bgY + 8);
2151
+ }
2152
+ ctx.font = prevFont;
2153
+ }
2076
2154
  }
2077
2155
  ctx.restore();
2078
2156
  };
@@ -3159,6 +3237,28 @@ function createChart(element, options = {}) {
3159
3237
  if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
3160
3238
  return { drawing, target: "line" };
3161
3239
  }
3240
+ } else if (drawing.type === "fib-retracement") {
3241
+ const first = drawing.points[0];
3242
+ const second = drawing.points[1];
3243
+ if (!first || !second) continue;
3244
+ const x1 = canvasXFromDrawingPoint(first);
3245
+ const y1 = canvasYFromDrawingPrice(first.price);
3246
+ const x2 = canvasXFromDrawingPoint(second);
3247
+ const y2 = canvasYFromDrawingPrice(second.price);
3248
+ if (Math.hypot(x - x1, y - y1) <= 9) {
3249
+ return { drawing, target: "handle", pointIndex: 0 };
3250
+ }
3251
+ if (Math.hypot(x - x2, y - y2) <= 9) {
3252
+ return { drawing, target: "handle", pointIndex: 1 };
3253
+ }
3254
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
3255
+ for (const ratio of fibRatios) {
3256
+ const price = first.price + (second.price - first.price) * (1 - ratio);
3257
+ const lineY = canvasYFromDrawingPrice(price);
3258
+ if (Math.abs(y - lineY) <= 5) {
3259
+ return { drawing, target: "line" };
3260
+ }
3261
+ }
3162
3262
  }
3163
3263
  }
3164
3264
  return null;
@@ -3224,13 +3324,14 @@ function createChart(element, options = {}) {
3224
3324
  return false;
3225
3325
  }
3226
3326
  if (activeDrawingTool === "horizontal-line") {
3327
+ const defaults = getDrawingToolDefaults("horizontal-line");
3227
3328
  drawings.push(
3228
3329
  normalizeDrawingState({
3229
3330
  type: "horizontal-line",
3230
3331
  points: [point],
3231
- color: "#38bdf8",
3232
- style: "dotted",
3233
- width: 1
3332
+ color: defaults.color ?? "#38bdf8",
3333
+ style: defaults.style ?? "solid",
3334
+ width: defaults.width ?? 1
3234
3335
  })
3235
3336
  );
3236
3337
  emitDrawingsChange();
@@ -3250,18 +3351,50 @@ function createChart(element, options = {}) {
3250
3351
  draw();
3251
3352
  return true;
3252
3353
  }
3354
+ const defaults = getDrawingToolDefaults("trendline");
3253
3355
  draftDrawing = normalizeDrawingState({
3254
3356
  type: "trendline",
3255
3357
  points: [point, point],
3256
- color: "#2563eb",
3257
- style: "solid",
3258
- width: 2
3358
+ color: defaults.color ?? "#2563eb",
3359
+ style: defaults.style ?? "solid",
3360
+ width: defaults.width ?? 2
3361
+ });
3362
+ draw();
3363
+ return true;
3364
+ }
3365
+ if (activeDrawingTool === "fib-retracement") {
3366
+ if (draftDrawing?.type === "fib-retracement") {
3367
+ const completed = normalizeDrawingState({
3368
+ ...serializeDrawing(draftDrawing),
3369
+ points: [draftDrawing.points[0], point]
3370
+ });
3371
+ drawings.push(completed);
3372
+ draftDrawing = null;
3373
+ activeDrawingTool = null;
3374
+ emitDrawingsChange();
3375
+ draw();
3376
+ return true;
3377
+ }
3378
+ const defaults = getDrawingToolDefaults("fib-retracement");
3379
+ draftDrawing = normalizeDrawingState({
3380
+ type: "fib-retracement",
3381
+ points: [point, point],
3382
+ color: defaults.color ?? "#2563eb",
3383
+ style: defaults.style ?? "solid",
3384
+ width: defaults.width ?? 1
3259
3385
  });
3260
3386
  draw();
3261
3387
  return true;
3262
3388
  }
3263
3389
  return false;
3264
3390
  };
3391
+ const setDrawingDefaults = (tool, defaults) => {
3392
+ if (defaults === null) {
3393
+ drawingToolDefaults.delete(tool);
3394
+ return;
3395
+ }
3396
+ drawingToolDefaults.set(tool, { ...defaults });
3397
+ };
3265
3398
  const updateDrawingDrag = (x, y) => {
3266
3399
  if (!drawingDragState) {
3267
3400
  return false;
@@ -3495,7 +3628,7 @@ function createChart(element, options = {}) {
3495
3628
  setCrosshairPoint(null);
3496
3629
  return;
3497
3630
  }
3498
- if (draftDrawing && activeDrawingTool === "trendline") {
3631
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
3499
3632
  const nextPoint = drawingPointFromCanvas(point.x, point.y);
3500
3633
  if (nextPoint) {
3501
3634
  draftDrawing = {
@@ -4110,6 +4243,7 @@ function createChart(element, options = {}) {
4110
4243
  onDrawingsChange,
4111
4244
  onDrawingSelect,
4112
4245
  onDrawingHover,
4246
+ setDrawingDefaults,
4113
4247
  setDoubleClickEnabled,
4114
4248
  setDoubleClickAction,
4115
4249
  registerIndicator,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",