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.
- package/dist/hyperprop-charting-library.cjs +73 -0
- package/dist/hyperprop-charting-library.d.ts +17 -1
- package/dist/hyperprop-charting-library.js +73 -0
- package/dist/index.cjs +73 -0
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +73 -0
- package/docs/API.md +1 -0
- package/package.json +1 -1
|
@@ -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"`)
|