hyperprop-charting-library 0.1.100 → 0.1.102

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.
@@ -991,6 +991,7 @@ function createChart(element, options = {}) {
991
991
  fontSize: drawing.fontSize,
992
992
  ...drawing.label === void 0 ? {} : { label: drawing.label }
993
993
  });
994
+ let tradeMarkers = [];
994
995
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
995
996
  let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
996
997
  let activeDrawingTool = null;
@@ -2897,6 +2898,73 @@ function createChart(element, options = {}) {
2897
2898
  if (draftDrawing) {
2898
2899
  drawDrawing(draftDrawing, true);
2899
2900
  }
2901
+ if (tradeMarkers.length > 0 && data.length > 0) {
2902
+ const visibleStart = Math.floor(xStart) - 1;
2903
+ const visibleEnd = Math.ceil(xStart + xSpan) + 1;
2904
+ const indexForTime = (ms) => {
2905
+ let lo = 0;
2906
+ let hi = data.length - 1;
2907
+ if (ms < data[0].time.getTime()) return null;
2908
+ if (ms >= data[hi].time.getTime()) return hi;
2909
+ while (lo < hi) {
2910
+ const mid = lo + hi + 1 >> 1;
2911
+ if (data[mid].time.getTime() <= ms) lo = mid;
2912
+ else hi = mid - 1;
2913
+ }
2914
+ return lo;
2915
+ };
2916
+ const prevFont = ctx.font;
2917
+ const prevAlign = ctx.textAlign;
2918
+ const prevBaseline = ctx.textBaseline;
2919
+ ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2920
+ ctx.textAlign = "center";
2921
+ ctx.textBaseline = "middle";
2922
+ const arrowHalf = 5;
2923
+ const arrowH = 8;
2924
+ const gap = 5;
2925
+ const labelOffset = arrowH + 8;
2926
+ for (const marker of tradeMarkers) {
2927
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2928
+ if (!Number.isFinite(ms)) continue;
2929
+ const index = indexForTime(ms);
2930
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2931
+ const bar = data[index];
2932
+ if (!bar) continue;
2933
+ const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2934
+ if (x < chartLeft - 20 || x > chartRight + 20) continue;
2935
+ const isBuy = marker.side === "buy";
2936
+ const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2937
+ const textColor = marker.textColor ?? "#d1d4dc";
2938
+ const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2939
+ ctx.save();
2940
+ ctx.fillStyle = arrowColor;
2941
+ if (isBuy) {
2942
+ const tipY = yFromPrice(bar.l) + gap;
2943
+ ctx.beginPath();
2944
+ ctx.moveTo(x, tipY);
2945
+ ctx.lineTo(x - arrowHalf, tipY + arrowH);
2946
+ ctx.lineTo(x + arrowHalf, tipY + arrowH);
2947
+ ctx.closePath();
2948
+ ctx.fill();
2949
+ ctx.fillStyle = textColor;
2950
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset));
2951
+ } else {
2952
+ const tipY = yFromPrice(bar.h) - gap;
2953
+ ctx.beginPath();
2954
+ ctx.moveTo(x, tipY);
2955
+ ctx.lineTo(x - arrowHalf, tipY - arrowH);
2956
+ ctx.lineTo(x + arrowHalf, tipY - arrowH);
2957
+ ctx.closePath();
2958
+ ctx.fill();
2959
+ ctx.fillStyle = textColor;
2960
+ ctx.fillText(label, Math.round(x), Math.round(tipY - labelOffset));
2961
+ }
2962
+ ctx.restore();
2963
+ }
2964
+ ctx.font = prevFont;
2965
+ ctx.textAlign = prevAlign;
2966
+ ctx.textBaseline = prevBaseline;
2967
+ }
2900
2968
  const crosshair = { ...DEFAULT_CROSSHAIR_OPTIONS, ...mergedOptions.crosshair ?? {} };
2901
2969
  if (crosshair.visible && crosshairPoint) {
2902
2970
  const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
@@ -5158,6 +5226,10 @@ function createChart(element, options = {}) {
5158
5226
  }
5159
5227
  return false;
5160
5228
  };
5229
+ const setTradeMarkers = (markers) => {
5230
+ tradeMarkers = Array.isArray(markers) ? markers.slice() : [];
5231
+ draw();
5232
+ };
5161
5233
  const setMagnetMode = (mode) => {
5162
5234
  magnetMode = mode;
5163
5235
  };
@@ -5278,6 +5350,7 @@ function createChart(element, options = {}) {
5278
5350
  setMagnetMode,
5279
5351
  getMagnetMode,
5280
5352
  cancelDrawing,
5353
+ setTradeMarkers,
5281
5354
  setSelectedDrawing,
5282
5355
  onDrawingHover,
5283
5356
  setDrawingDefaults,
@@ -79,6 +79,21 @@ interface DrawingObjectOptions {
79
79
  declare const POSITION_DEFAULT_COLORS: string[];
80
80
  /** Default multi-color palette for fib-retracement levels. */
81
81
  declare const FIB_DEFAULT_PALETTE: string[];
82
+ /** A trade execution marker (arrow + label) drawn on the candle of its time. */
83
+ interface TradeMarkerOptions {
84
+ id?: string;
85
+ /** Execution time as epoch ms or ISO string; resolved to the bar it falls on. */
86
+ time: number | string;
87
+ price: number;
88
+ side: "buy" | "sell";
89
+ qty?: number;
90
+ /** Override label text (defaults to `${qty} @ ${price}`). */
91
+ text?: string;
92
+ /** Arrow color (defaults: buy #2962ff, sell #f23645). */
93
+ color?: string;
94
+ /** Label text color (default #d1d4dc). */
95
+ textColor?: string;
96
+ }
82
97
  interface DrawingSelectEvent {
83
98
  drawing: DrawingObjectOptions;
84
99
  target: "line" | "handle";
@@ -433,6 +448,7 @@ interface ChartInstance {
433
448
  setMagnetMode: (mode: "none" | "weak" | "strong") => void;
434
449
  getMagnetMode: () => "none" | "weak" | "strong";
435
450
  cancelDrawing: () => boolean;
451
+ setTradeMarkers: (markers: TradeMarkerOptions[]) => void;
436
452
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
437
453
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
438
454
  setSelectedDrawing: (id: string | null) => void;
@@ -471,4 +487,4 @@ interface ViewportState {
471
487
  }
472
488
  declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
473
489
 
474
- 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, FIB_DEFAULT_PALETTE, 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, POSITION_DEFAULT_COLORS, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
490
+ 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, FIB_DEFAULT_PALETTE, 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, POSITION_DEFAULT_COLORS, type PriceLineOptions, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, createChart };
@@ -965,6 +965,7 @@ function createChart(element, options = {}) {
965
965
  fontSize: drawing.fontSize,
966
966
  ...drawing.label === void 0 ? {} : { label: drawing.label }
967
967
  });
968
+ let tradeMarkers = [];
968
969
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
969
970
  let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
970
971
  let activeDrawingTool = null;
@@ -2871,6 +2872,73 @@ function createChart(element, options = {}) {
2871
2872
  if (draftDrawing) {
2872
2873
  drawDrawing(draftDrawing, true);
2873
2874
  }
2875
+ if (tradeMarkers.length > 0 && data.length > 0) {
2876
+ const visibleStart = Math.floor(xStart) - 1;
2877
+ const visibleEnd = Math.ceil(xStart + xSpan) + 1;
2878
+ const indexForTime = (ms) => {
2879
+ let lo = 0;
2880
+ let hi = data.length - 1;
2881
+ if (ms < data[0].time.getTime()) return null;
2882
+ if (ms >= data[hi].time.getTime()) return hi;
2883
+ while (lo < hi) {
2884
+ const mid = lo + hi + 1 >> 1;
2885
+ if (data[mid].time.getTime() <= ms) lo = mid;
2886
+ else hi = mid - 1;
2887
+ }
2888
+ return lo;
2889
+ };
2890
+ const prevFont = ctx.font;
2891
+ const prevAlign = ctx.textAlign;
2892
+ const prevBaseline = ctx.textBaseline;
2893
+ ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2894
+ ctx.textAlign = "center";
2895
+ ctx.textBaseline = "middle";
2896
+ const arrowHalf = 5;
2897
+ const arrowH = 8;
2898
+ const gap = 5;
2899
+ const labelOffset = arrowH + 8;
2900
+ for (const marker of tradeMarkers) {
2901
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2902
+ if (!Number.isFinite(ms)) continue;
2903
+ const index = indexForTime(ms);
2904
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2905
+ const bar = data[index];
2906
+ if (!bar) continue;
2907
+ const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2908
+ if (x < chartLeft - 20 || x > chartRight + 20) continue;
2909
+ const isBuy = marker.side === "buy";
2910
+ const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2911
+ const textColor = marker.textColor ?? "#d1d4dc";
2912
+ const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2913
+ ctx.save();
2914
+ ctx.fillStyle = arrowColor;
2915
+ if (isBuy) {
2916
+ const tipY = yFromPrice(bar.l) + gap;
2917
+ ctx.beginPath();
2918
+ ctx.moveTo(x, tipY);
2919
+ ctx.lineTo(x - arrowHalf, tipY + arrowH);
2920
+ ctx.lineTo(x + arrowHalf, tipY + arrowH);
2921
+ ctx.closePath();
2922
+ ctx.fill();
2923
+ ctx.fillStyle = textColor;
2924
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset));
2925
+ } else {
2926
+ const tipY = yFromPrice(bar.h) - gap;
2927
+ ctx.beginPath();
2928
+ ctx.moveTo(x, tipY);
2929
+ ctx.lineTo(x - arrowHalf, tipY - arrowH);
2930
+ ctx.lineTo(x + arrowHalf, tipY - arrowH);
2931
+ ctx.closePath();
2932
+ ctx.fill();
2933
+ ctx.fillStyle = textColor;
2934
+ ctx.fillText(label, Math.round(x), Math.round(tipY - labelOffset));
2935
+ }
2936
+ ctx.restore();
2937
+ }
2938
+ ctx.font = prevFont;
2939
+ ctx.textAlign = prevAlign;
2940
+ ctx.textBaseline = prevBaseline;
2941
+ }
2874
2942
  const crosshair = { ...DEFAULT_CROSSHAIR_OPTIONS, ...mergedOptions.crosshair ?? {} };
2875
2943
  if (crosshair.visible && crosshairPoint) {
2876
2944
  const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
@@ -5132,6 +5200,10 @@ function createChart(element, options = {}) {
5132
5200
  }
5133
5201
  return false;
5134
5202
  };
5203
+ const setTradeMarkers = (markers) => {
5204
+ tradeMarkers = Array.isArray(markers) ? markers.slice() : [];
5205
+ draw();
5206
+ };
5135
5207
  const setMagnetMode = (mode) => {
5136
5208
  magnetMode = mode;
5137
5209
  };
@@ -5252,6 +5324,7 @@ function createChart(element, options = {}) {
5252
5324
  setMagnetMode,
5253
5325
  getMagnetMode,
5254
5326
  cancelDrawing,
5327
+ setTradeMarkers,
5255
5328
  setSelectedDrawing,
5256
5329
  onDrawingHover,
5257
5330
  setDrawingDefaults,
package/dist/index.cjs CHANGED
@@ -991,6 +991,7 @@ function createChart(element, options = {}) {
991
991
  fontSize: drawing.fontSize,
992
992
  ...drawing.label === void 0 ? {} : { label: drawing.label }
993
993
  });
994
+ let tradeMarkers = [];
994
995
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
995
996
  let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
996
997
  let activeDrawingTool = null;
@@ -2897,6 +2898,73 @@ function createChart(element, options = {}) {
2897
2898
  if (draftDrawing) {
2898
2899
  drawDrawing(draftDrawing, true);
2899
2900
  }
2901
+ if (tradeMarkers.length > 0 && data.length > 0) {
2902
+ const visibleStart = Math.floor(xStart) - 1;
2903
+ const visibleEnd = Math.ceil(xStart + xSpan) + 1;
2904
+ const indexForTime = (ms) => {
2905
+ let lo = 0;
2906
+ let hi = data.length - 1;
2907
+ if (ms < data[0].time.getTime()) return null;
2908
+ if (ms >= data[hi].time.getTime()) return hi;
2909
+ while (lo < hi) {
2910
+ const mid = lo + hi + 1 >> 1;
2911
+ if (data[mid].time.getTime() <= ms) lo = mid;
2912
+ else hi = mid - 1;
2913
+ }
2914
+ return lo;
2915
+ };
2916
+ const prevFont = ctx.font;
2917
+ const prevAlign = ctx.textAlign;
2918
+ const prevBaseline = ctx.textBaseline;
2919
+ ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2920
+ ctx.textAlign = "center";
2921
+ ctx.textBaseline = "middle";
2922
+ const arrowHalf = 5;
2923
+ const arrowH = 8;
2924
+ const gap = 5;
2925
+ const labelOffset = arrowH + 8;
2926
+ for (const marker of tradeMarkers) {
2927
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2928
+ if (!Number.isFinite(ms)) continue;
2929
+ const index = indexForTime(ms);
2930
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2931
+ const bar = data[index];
2932
+ if (!bar) continue;
2933
+ const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2934
+ if (x < chartLeft - 20 || x > chartRight + 20) continue;
2935
+ const isBuy = marker.side === "buy";
2936
+ const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2937
+ const textColor = marker.textColor ?? "#d1d4dc";
2938
+ const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2939
+ ctx.save();
2940
+ ctx.fillStyle = arrowColor;
2941
+ if (isBuy) {
2942
+ const tipY = yFromPrice(bar.l) + gap;
2943
+ ctx.beginPath();
2944
+ ctx.moveTo(x, tipY);
2945
+ ctx.lineTo(x - arrowHalf, tipY + arrowH);
2946
+ ctx.lineTo(x + arrowHalf, tipY + arrowH);
2947
+ ctx.closePath();
2948
+ ctx.fill();
2949
+ ctx.fillStyle = textColor;
2950
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset));
2951
+ } else {
2952
+ const tipY = yFromPrice(bar.h) - gap;
2953
+ ctx.beginPath();
2954
+ ctx.moveTo(x, tipY);
2955
+ ctx.lineTo(x - arrowHalf, tipY - arrowH);
2956
+ ctx.lineTo(x + arrowHalf, tipY - arrowH);
2957
+ ctx.closePath();
2958
+ ctx.fill();
2959
+ ctx.fillStyle = textColor;
2960
+ ctx.fillText(label, Math.round(x), Math.round(tipY - labelOffset));
2961
+ }
2962
+ ctx.restore();
2963
+ }
2964
+ ctx.font = prevFont;
2965
+ ctx.textAlign = prevAlign;
2966
+ ctx.textBaseline = prevBaseline;
2967
+ }
2900
2968
  const crosshair = { ...DEFAULT_CROSSHAIR_OPTIONS, ...mergedOptions.crosshair ?? {} };
2901
2969
  if (crosshair.visible && crosshairPoint) {
2902
2970
  const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
@@ -5158,6 +5226,10 @@ function createChart(element, options = {}) {
5158
5226
  }
5159
5227
  return false;
5160
5228
  };
5229
+ const setTradeMarkers = (markers) => {
5230
+ tradeMarkers = Array.isArray(markers) ? markers.slice() : [];
5231
+ draw();
5232
+ };
5161
5233
  const setMagnetMode = (mode) => {
5162
5234
  magnetMode = mode;
5163
5235
  };
@@ -5278,6 +5350,7 @@ function createChart(element, options = {}) {
5278
5350
  setMagnetMode,
5279
5351
  getMagnetMode,
5280
5352
  cancelDrawing,
5353
+ setTradeMarkers,
5281
5354
  setSelectedDrawing,
5282
5355
  onDrawingHover,
5283
5356
  setDrawingDefaults,
package/dist/index.d.cts CHANGED
@@ -79,6 +79,21 @@ interface DrawingObjectOptions {
79
79
  declare const POSITION_DEFAULT_COLORS: string[];
80
80
  /** Default multi-color palette for fib-retracement levels. */
81
81
  declare const FIB_DEFAULT_PALETTE: string[];
82
+ /** A trade execution marker (arrow + label) drawn on the candle of its time. */
83
+ interface TradeMarkerOptions {
84
+ id?: string;
85
+ /** Execution time as epoch ms or ISO string; resolved to the bar it falls on. */
86
+ time: number | string;
87
+ price: number;
88
+ side: "buy" | "sell";
89
+ qty?: number;
90
+ /** Override label text (defaults to `${qty} @ ${price}`). */
91
+ text?: string;
92
+ /** Arrow color (defaults: buy #2962ff, sell #f23645). */
93
+ color?: string;
94
+ /** Label text color (default #d1d4dc). */
95
+ textColor?: string;
96
+ }
82
97
  interface DrawingSelectEvent {
83
98
  drawing: DrawingObjectOptions;
84
99
  target: "line" | "handle";
@@ -433,6 +448,7 @@ interface ChartInstance {
433
448
  setMagnetMode: (mode: "none" | "weak" | "strong") => void;
434
449
  getMagnetMode: () => "none" | "weak" | "strong";
435
450
  cancelDrawing: () => boolean;
451
+ setTradeMarkers: (markers: TradeMarkerOptions[]) => void;
436
452
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
437
453
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
438
454
  setSelectedDrawing: (id: string | null) => void;
@@ -471,4 +487,4 @@ interface ViewportState {
471
487
  }
472
488
  declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
473
489
 
474
- 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, FIB_DEFAULT_PALETTE, 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, POSITION_DEFAULT_COLORS, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
490
+ 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, FIB_DEFAULT_PALETTE, 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, POSITION_DEFAULT_COLORS, type PriceLineOptions, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, createChart };
package/dist/index.d.ts CHANGED
@@ -79,6 +79,21 @@ interface DrawingObjectOptions {
79
79
  declare const POSITION_DEFAULT_COLORS: string[];
80
80
  /** Default multi-color palette for fib-retracement levels. */
81
81
  declare const FIB_DEFAULT_PALETTE: string[];
82
+ /** A trade execution marker (arrow + label) drawn on the candle of its time. */
83
+ interface TradeMarkerOptions {
84
+ id?: string;
85
+ /** Execution time as epoch ms or ISO string; resolved to the bar it falls on. */
86
+ time: number | string;
87
+ price: number;
88
+ side: "buy" | "sell";
89
+ qty?: number;
90
+ /** Override label text (defaults to `${qty} @ ${price}`). */
91
+ text?: string;
92
+ /** Arrow color (defaults: buy #2962ff, sell #f23645). */
93
+ color?: string;
94
+ /** Label text color (default #d1d4dc). */
95
+ textColor?: string;
96
+ }
82
97
  interface DrawingSelectEvent {
83
98
  drawing: DrawingObjectOptions;
84
99
  target: "line" | "handle";
@@ -433,6 +448,7 @@ interface ChartInstance {
433
448
  setMagnetMode: (mode: "none" | "weak" | "strong") => void;
434
449
  getMagnetMode: () => "none" | "weak" | "strong";
435
450
  cancelDrawing: () => boolean;
451
+ setTradeMarkers: (markers: TradeMarkerOptions[]) => void;
436
452
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
437
453
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
438
454
  setSelectedDrawing: (id: string | null) => void;
@@ -471,4 +487,4 @@ interface ViewportState {
471
487
  }
472
488
  declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
473
489
 
474
- 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, FIB_DEFAULT_PALETTE, 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, POSITION_DEFAULT_COLORS, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
490
+ 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, FIB_DEFAULT_PALETTE, 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, POSITION_DEFAULT_COLORS, type PriceLineOptions, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, createChart };
package/dist/index.js CHANGED
@@ -965,6 +965,7 @@ function createChart(element, options = {}) {
965
965
  fontSize: drawing.fontSize,
966
966
  ...drawing.label === void 0 ? {} : { label: drawing.label }
967
967
  });
968
+ let tradeMarkers = [];
968
969
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
969
970
  let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
970
971
  let activeDrawingTool = null;
@@ -2871,6 +2872,73 @@ function createChart(element, options = {}) {
2871
2872
  if (draftDrawing) {
2872
2873
  drawDrawing(draftDrawing, true);
2873
2874
  }
2875
+ if (tradeMarkers.length > 0 && data.length > 0) {
2876
+ const visibleStart = Math.floor(xStart) - 1;
2877
+ const visibleEnd = Math.ceil(xStart + xSpan) + 1;
2878
+ const indexForTime = (ms) => {
2879
+ let lo = 0;
2880
+ let hi = data.length - 1;
2881
+ if (ms < data[0].time.getTime()) return null;
2882
+ if (ms >= data[hi].time.getTime()) return hi;
2883
+ while (lo < hi) {
2884
+ const mid = lo + hi + 1 >> 1;
2885
+ if (data[mid].time.getTime() <= ms) lo = mid;
2886
+ else hi = mid - 1;
2887
+ }
2888
+ return lo;
2889
+ };
2890
+ const prevFont = ctx.font;
2891
+ const prevAlign = ctx.textAlign;
2892
+ const prevBaseline = ctx.textBaseline;
2893
+ ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2894
+ ctx.textAlign = "center";
2895
+ ctx.textBaseline = "middle";
2896
+ const arrowHalf = 5;
2897
+ const arrowH = 8;
2898
+ const gap = 5;
2899
+ const labelOffset = arrowH + 8;
2900
+ for (const marker of tradeMarkers) {
2901
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2902
+ if (!Number.isFinite(ms)) continue;
2903
+ const index = indexForTime(ms);
2904
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2905
+ const bar = data[index];
2906
+ if (!bar) continue;
2907
+ const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2908
+ if (x < chartLeft - 20 || x > chartRight + 20) continue;
2909
+ const isBuy = marker.side === "buy";
2910
+ const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2911
+ const textColor = marker.textColor ?? "#d1d4dc";
2912
+ const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2913
+ ctx.save();
2914
+ ctx.fillStyle = arrowColor;
2915
+ if (isBuy) {
2916
+ const tipY = yFromPrice(bar.l) + gap;
2917
+ ctx.beginPath();
2918
+ ctx.moveTo(x, tipY);
2919
+ ctx.lineTo(x - arrowHalf, tipY + arrowH);
2920
+ ctx.lineTo(x + arrowHalf, tipY + arrowH);
2921
+ ctx.closePath();
2922
+ ctx.fill();
2923
+ ctx.fillStyle = textColor;
2924
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset));
2925
+ } else {
2926
+ const tipY = yFromPrice(bar.h) - gap;
2927
+ ctx.beginPath();
2928
+ ctx.moveTo(x, tipY);
2929
+ ctx.lineTo(x - arrowHalf, tipY - arrowH);
2930
+ ctx.lineTo(x + arrowHalf, tipY - arrowH);
2931
+ ctx.closePath();
2932
+ ctx.fill();
2933
+ ctx.fillStyle = textColor;
2934
+ ctx.fillText(label, Math.round(x), Math.round(tipY - labelOffset));
2935
+ }
2936
+ ctx.restore();
2937
+ }
2938
+ ctx.font = prevFont;
2939
+ ctx.textAlign = prevAlign;
2940
+ ctx.textBaseline = prevBaseline;
2941
+ }
2874
2942
  const crosshair = { ...DEFAULT_CROSSHAIR_OPTIONS, ...mergedOptions.crosshair ?? {} };
2875
2943
  if (crosshair.visible && crosshairPoint) {
2876
2944
  const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
@@ -5132,6 +5200,10 @@ function createChart(element, options = {}) {
5132
5200
  }
5133
5201
  return false;
5134
5202
  };
5203
+ const setTradeMarkers = (markers) => {
5204
+ tradeMarkers = Array.isArray(markers) ? markers.slice() : [];
5205
+ draw();
5206
+ };
5135
5207
  const setMagnetMode = (mode) => {
5136
5208
  magnetMode = mode;
5137
5209
  };
@@ -5252,6 +5324,7 @@ function createChart(element, options = {}) {
5252
5324
  setMagnetMode,
5253
5325
  getMagnetMode,
5254
5326
  cancelDrawing,
5327
+ setTradeMarkers,
5255
5328
  setSelectedDrawing,
5256
5329
  onDrawingHover,
5257
5330
  setDrawingDefaults,
package/docs/API.md CHANGED
@@ -484,6 +484,7 @@ Use `getDrawings()` / `setDrawings()` for persistence.
484
484
  - `setSelectedDrawing(id: string | null): void` (marks a drawing selected; only the selected/drafted drawing renders its handles, and position tools render their lines/labels only while selected)
485
485
  - `onDrawingDoubleClick(handler): void` (fires when a drawing is double-clicked; use it to open a settings dialog, e.g. for position tools)
486
486
  - `onDrawingEditText(handler): void` (fires when a `text`/`note` tool is placed or double-clicked; use it to show an inline text editor at `{x, y}` and write the result back via `updateDrawing(id, { label })`). `text`/`note` drawings store their content in `label` and size in `fontSize`.
487
+ - `setTradeMarkers(markers: TradeMarkerOptions[]): void` — draw trade execution markers (arrow + `qty @ price` label) on the candle of each fill's `time`. Buy = arrow below the low pointing up; sell = arrow above the high pointing down. Each marker: `{ time (ms|ISO), price, side: "buy"|"sell", qty?, text?, color?, textColor? }` (defaults: buy `#2962ff`, sell `#f23645`, text `#d1d4dc`). Markers resolve to bars by time, so they persist across timeframe switches and scrolling. Pass `[]` to clear.
487
488
  - `cancelDrawing(): boolean` — abort an in-progress multi-click drawing (between the first click and the final point, e.g. a half-drawn trendline/ray/fib). Returns `true` if a draft was cancelled. Wire it to Escape.
488
489
  - `setMagnetMode(mode): void` / `getMagnetMode()` — magnet/snap for all drawing tools. `"none"` off, `"weak"` snaps to a candle's OHLC only when the cursor is near a value, `"strong"` always snaps to the nearest OHLC of the bar under the cursor. Holding Cmd/Ctrl while drawing/dragging forces strong snapping regardless of the mode.
489
490
  - `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.100",
3
+ "version": "0.1.102",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",