hyperprop-charting-library 0.1.101 → 0.1.103

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.
@@ -2913,52 +2913,65 @@ function createChart(element, options = {}) {
2913
2913
  }
2914
2914
  return lo;
2915
2915
  };
2916
+ const groups = /* @__PURE__ */ new Map();
2917
+ for (const marker of tradeMarkers) {
2918
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2919
+ if (!Number.isFinite(ms)) continue;
2920
+ const index = indexForTime(ms);
2921
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2922
+ const side = marker.side === "buy" ? "buy" : "sell";
2923
+ const qty = Math.max(0, marker.qty ?? 0);
2924
+ const weight = qty > 0 ? qty : 1;
2925
+ const key = `${index}:${side}`;
2926
+ const existing = groups.get(key);
2927
+ if (existing) {
2928
+ existing.qty += qty;
2929
+ existing.priceQty += marker.price * weight;
2930
+ } else {
2931
+ groups.set(key, {
2932
+ index,
2933
+ side,
2934
+ qty,
2935
+ priceQty: marker.price * weight,
2936
+ ...marker.color === void 0 ? {} : { color: marker.color },
2937
+ ...marker.textColor === void 0 ? {} : { textColor: marker.textColor },
2938
+ ...marker.text === void 0 ? {} : { text: marker.text }
2939
+ });
2940
+ }
2941
+ }
2916
2942
  const prevFont = ctx.font;
2917
2943
  const prevAlign = ctx.textAlign;
2918
2944
  const prevBaseline = ctx.textBaseline;
2919
2945
  ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2920
2946
  ctx.textAlign = "center";
2947
+ ctx.textBaseline = "middle";
2921
2948
  const arrowHalf = 5;
2922
2949
  const arrowH = 8;
2923
2950
  const gap = 5;
2924
- for (const marker of tradeMarkers) {
2925
- const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2926
- if (!Number.isFinite(ms)) continue;
2927
- const index = indexForTime(ms);
2928
- if (index === null || index < visibleStart || index > visibleEnd) continue;
2929
- const bar = data[index];
2951
+ const labelOffset = arrowH + 8;
2952
+ for (const group of groups.values()) {
2953
+ const bar = data[group.index];
2930
2954
  if (!bar) continue;
2931
- const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2955
+ const x = chartLeft + (group.index + 0.5 - xStart) / xSpan * chartWidth;
2932
2956
  if (x < chartLeft - 20 || x > chartRight + 20) continue;
2933
- const isBuy = marker.side === "buy";
2934
- const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2935
- const textColor = marker.textColor ?? "#d1d4dc";
2936
- const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2957
+ const isBuy = group.side === "buy";
2958
+ const arrowColor = group.color ?? (isBuy ? "#2962ff" : "#f23645");
2959
+ const textColor = group.textColor ?? "#d1d4dc";
2960
+ const weightTotal = group.qty > 0 ? group.qty : 1;
2961
+ const avgPrice = group.priceQty / weightTotal;
2962
+ const label = group.text ?? `${group.qty > 0 ? `${group.qty} @ ` : ""}${formatPrice(avgPrice)}`.trim();
2937
2963
  ctx.save();
2938
2964
  ctx.fillStyle = arrowColor;
2939
- if (isBuy) {
2940
- const tipY = yFromPrice(bar.l) + gap;
2941
- ctx.beginPath();
2942
- ctx.moveTo(x, tipY);
2943
- ctx.lineTo(x - arrowHalf, tipY + arrowH);
2944
- ctx.lineTo(x + arrowHalf, tipY + arrowH);
2945
- ctx.closePath();
2946
- ctx.fill();
2947
- ctx.fillStyle = textColor;
2948
- ctx.textBaseline = "top";
2949
- ctx.fillText(label, x, tipY + arrowH + 2);
2950
- } else {
2951
- const tipY = yFromPrice(bar.h) - gap;
2952
- ctx.beginPath();
2953
- ctx.moveTo(x, tipY);
2954
- ctx.lineTo(x - arrowHalf, tipY - arrowH);
2955
- ctx.lineTo(x + arrowHalf, tipY - arrowH);
2956
- ctx.closePath();
2957
- ctx.fill();
2958
- ctx.fillStyle = textColor;
2959
- ctx.textBaseline = "bottom";
2960
- ctx.fillText(label, x, tipY - arrowH - 2);
2961
- }
2965
+ const tipY = isBuy ? yFromPrice(bar.l) + gap : yFromPrice(bar.h) - gap;
2966
+ const dir = isBuy ? 1 : -1;
2967
+ ctx.beginPath();
2968
+ ctx.moveTo(x, tipY);
2969
+ ctx.lineTo(x - arrowHalf, tipY + arrowH * dir);
2970
+ ctx.lineTo(x + arrowHalf, tipY + arrowH * dir);
2971
+ ctx.closePath();
2972
+ ctx.fill();
2973
+ ctx.fillStyle = textColor;
2974
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset * dir));
2962
2975
  ctx.restore();
2963
2976
  }
2964
2977
  ctx.font = prevFont;
@@ -2887,52 +2887,65 @@ function createChart(element, options = {}) {
2887
2887
  }
2888
2888
  return lo;
2889
2889
  };
2890
+ const groups = /* @__PURE__ */ new Map();
2891
+ for (const marker of tradeMarkers) {
2892
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2893
+ if (!Number.isFinite(ms)) continue;
2894
+ const index = indexForTime(ms);
2895
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2896
+ const side = marker.side === "buy" ? "buy" : "sell";
2897
+ const qty = Math.max(0, marker.qty ?? 0);
2898
+ const weight = qty > 0 ? qty : 1;
2899
+ const key = `${index}:${side}`;
2900
+ const existing = groups.get(key);
2901
+ if (existing) {
2902
+ existing.qty += qty;
2903
+ existing.priceQty += marker.price * weight;
2904
+ } else {
2905
+ groups.set(key, {
2906
+ index,
2907
+ side,
2908
+ qty,
2909
+ priceQty: marker.price * weight,
2910
+ ...marker.color === void 0 ? {} : { color: marker.color },
2911
+ ...marker.textColor === void 0 ? {} : { textColor: marker.textColor },
2912
+ ...marker.text === void 0 ? {} : { text: marker.text }
2913
+ });
2914
+ }
2915
+ }
2890
2916
  const prevFont = ctx.font;
2891
2917
  const prevAlign = ctx.textAlign;
2892
2918
  const prevBaseline = ctx.textBaseline;
2893
2919
  ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2894
2920
  ctx.textAlign = "center";
2921
+ ctx.textBaseline = "middle";
2895
2922
  const arrowHalf = 5;
2896
2923
  const arrowH = 8;
2897
2924
  const gap = 5;
2898
- for (const marker of tradeMarkers) {
2899
- const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2900
- if (!Number.isFinite(ms)) continue;
2901
- const index = indexForTime(ms);
2902
- if (index === null || index < visibleStart || index > visibleEnd) continue;
2903
- const bar = data[index];
2925
+ const labelOffset = arrowH + 8;
2926
+ for (const group of groups.values()) {
2927
+ const bar = data[group.index];
2904
2928
  if (!bar) continue;
2905
- const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2929
+ const x = chartLeft + (group.index + 0.5 - xStart) / xSpan * chartWidth;
2906
2930
  if (x < chartLeft - 20 || x > chartRight + 20) continue;
2907
- const isBuy = marker.side === "buy";
2908
- const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2909
- const textColor = marker.textColor ?? "#d1d4dc";
2910
- const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2931
+ const isBuy = group.side === "buy";
2932
+ const arrowColor = group.color ?? (isBuy ? "#2962ff" : "#f23645");
2933
+ const textColor = group.textColor ?? "#d1d4dc";
2934
+ const weightTotal = group.qty > 0 ? group.qty : 1;
2935
+ const avgPrice = group.priceQty / weightTotal;
2936
+ const label = group.text ?? `${group.qty > 0 ? `${group.qty} @ ` : ""}${formatPrice(avgPrice)}`.trim();
2911
2937
  ctx.save();
2912
2938
  ctx.fillStyle = arrowColor;
2913
- if (isBuy) {
2914
- const tipY = yFromPrice(bar.l) + gap;
2915
- ctx.beginPath();
2916
- ctx.moveTo(x, tipY);
2917
- ctx.lineTo(x - arrowHalf, tipY + arrowH);
2918
- ctx.lineTo(x + arrowHalf, tipY + arrowH);
2919
- ctx.closePath();
2920
- ctx.fill();
2921
- ctx.fillStyle = textColor;
2922
- ctx.textBaseline = "top";
2923
- ctx.fillText(label, x, tipY + arrowH + 2);
2924
- } else {
2925
- const tipY = yFromPrice(bar.h) - gap;
2926
- ctx.beginPath();
2927
- ctx.moveTo(x, tipY);
2928
- ctx.lineTo(x - arrowHalf, tipY - arrowH);
2929
- ctx.lineTo(x + arrowHalf, tipY - arrowH);
2930
- ctx.closePath();
2931
- ctx.fill();
2932
- ctx.fillStyle = textColor;
2933
- ctx.textBaseline = "bottom";
2934
- ctx.fillText(label, x, tipY - arrowH - 2);
2935
- }
2939
+ const tipY = isBuy ? yFromPrice(bar.l) + gap : yFromPrice(bar.h) - gap;
2940
+ const dir = isBuy ? 1 : -1;
2941
+ ctx.beginPath();
2942
+ ctx.moveTo(x, tipY);
2943
+ ctx.lineTo(x - arrowHalf, tipY + arrowH * dir);
2944
+ ctx.lineTo(x + arrowHalf, tipY + arrowH * dir);
2945
+ ctx.closePath();
2946
+ ctx.fill();
2947
+ ctx.fillStyle = textColor;
2948
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset * dir));
2936
2949
  ctx.restore();
2937
2950
  }
2938
2951
  ctx.font = prevFont;
package/dist/index.cjs CHANGED
@@ -2913,52 +2913,65 @@ function createChart(element, options = {}) {
2913
2913
  }
2914
2914
  return lo;
2915
2915
  };
2916
+ const groups = /* @__PURE__ */ new Map();
2917
+ for (const marker of tradeMarkers) {
2918
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2919
+ if (!Number.isFinite(ms)) continue;
2920
+ const index = indexForTime(ms);
2921
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2922
+ const side = marker.side === "buy" ? "buy" : "sell";
2923
+ const qty = Math.max(0, marker.qty ?? 0);
2924
+ const weight = qty > 0 ? qty : 1;
2925
+ const key = `${index}:${side}`;
2926
+ const existing = groups.get(key);
2927
+ if (existing) {
2928
+ existing.qty += qty;
2929
+ existing.priceQty += marker.price * weight;
2930
+ } else {
2931
+ groups.set(key, {
2932
+ index,
2933
+ side,
2934
+ qty,
2935
+ priceQty: marker.price * weight,
2936
+ ...marker.color === void 0 ? {} : { color: marker.color },
2937
+ ...marker.textColor === void 0 ? {} : { textColor: marker.textColor },
2938
+ ...marker.text === void 0 ? {} : { text: marker.text }
2939
+ });
2940
+ }
2941
+ }
2916
2942
  const prevFont = ctx.font;
2917
2943
  const prevAlign = ctx.textAlign;
2918
2944
  const prevBaseline = ctx.textBaseline;
2919
2945
  ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2920
2946
  ctx.textAlign = "center";
2947
+ ctx.textBaseline = "middle";
2921
2948
  const arrowHalf = 5;
2922
2949
  const arrowH = 8;
2923
2950
  const gap = 5;
2924
- for (const marker of tradeMarkers) {
2925
- const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2926
- if (!Number.isFinite(ms)) continue;
2927
- const index = indexForTime(ms);
2928
- if (index === null || index < visibleStart || index > visibleEnd) continue;
2929
- const bar = data[index];
2951
+ const labelOffset = arrowH + 8;
2952
+ for (const group of groups.values()) {
2953
+ const bar = data[group.index];
2930
2954
  if (!bar) continue;
2931
- const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2955
+ const x = chartLeft + (group.index + 0.5 - xStart) / xSpan * chartWidth;
2932
2956
  if (x < chartLeft - 20 || x > chartRight + 20) continue;
2933
- const isBuy = marker.side === "buy";
2934
- const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2935
- const textColor = marker.textColor ?? "#d1d4dc";
2936
- const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2957
+ const isBuy = group.side === "buy";
2958
+ const arrowColor = group.color ?? (isBuy ? "#2962ff" : "#f23645");
2959
+ const textColor = group.textColor ?? "#d1d4dc";
2960
+ const weightTotal = group.qty > 0 ? group.qty : 1;
2961
+ const avgPrice = group.priceQty / weightTotal;
2962
+ const label = group.text ?? `${group.qty > 0 ? `${group.qty} @ ` : ""}${formatPrice(avgPrice)}`.trim();
2937
2963
  ctx.save();
2938
2964
  ctx.fillStyle = arrowColor;
2939
- if (isBuy) {
2940
- const tipY = yFromPrice(bar.l) + gap;
2941
- ctx.beginPath();
2942
- ctx.moveTo(x, tipY);
2943
- ctx.lineTo(x - arrowHalf, tipY + arrowH);
2944
- ctx.lineTo(x + arrowHalf, tipY + arrowH);
2945
- ctx.closePath();
2946
- ctx.fill();
2947
- ctx.fillStyle = textColor;
2948
- ctx.textBaseline = "top";
2949
- ctx.fillText(label, x, tipY + arrowH + 2);
2950
- } else {
2951
- const tipY = yFromPrice(bar.h) - gap;
2952
- ctx.beginPath();
2953
- ctx.moveTo(x, tipY);
2954
- ctx.lineTo(x - arrowHalf, tipY - arrowH);
2955
- ctx.lineTo(x + arrowHalf, tipY - arrowH);
2956
- ctx.closePath();
2957
- ctx.fill();
2958
- ctx.fillStyle = textColor;
2959
- ctx.textBaseline = "bottom";
2960
- ctx.fillText(label, x, tipY - arrowH - 2);
2961
- }
2965
+ const tipY = isBuy ? yFromPrice(bar.l) + gap : yFromPrice(bar.h) - gap;
2966
+ const dir = isBuy ? 1 : -1;
2967
+ ctx.beginPath();
2968
+ ctx.moveTo(x, tipY);
2969
+ ctx.lineTo(x - arrowHalf, tipY + arrowH * dir);
2970
+ ctx.lineTo(x + arrowHalf, tipY + arrowH * dir);
2971
+ ctx.closePath();
2972
+ ctx.fill();
2973
+ ctx.fillStyle = textColor;
2974
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset * dir));
2962
2975
  ctx.restore();
2963
2976
  }
2964
2977
  ctx.font = prevFont;
package/dist/index.js CHANGED
@@ -2887,52 +2887,65 @@ function createChart(element, options = {}) {
2887
2887
  }
2888
2888
  return lo;
2889
2889
  };
2890
+ const groups = /* @__PURE__ */ new Map();
2891
+ for (const marker of tradeMarkers) {
2892
+ const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2893
+ if (!Number.isFinite(ms)) continue;
2894
+ const index = indexForTime(ms);
2895
+ if (index === null || index < visibleStart || index > visibleEnd) continue;
2896
+ const side = marker.side === "buy" ? "buy" : "sell";
2897
+ const qty = Math.max(0, marker.qty ?? 0);
2898
+ const weight = qty > 0 ? qty : 1;
2899
+ const key = `${index}:${side}`;
2900
+ const existing = groups.get(key);
2901
+ if (existing) {
2902
+ existing.qty += qty;
2903
+ existing.priceQty += marker.price * weight;
2904
+ } else {
2905
+ groups.set(key, {
2906
+ index,
2907
+ side,
2908
+ qty,
2909
+ priceQty: marker.price * weight,
2910
+ ...marker.color === void 0 ? {} : { color: marker.color },
2911
+ ...marker.textColor === void 0 ? {} : { textColor: marker.textColor },
2912
+ ...marker.text === void 0 ? {} : { text: marker.text }
2913
+ });
2914
+ }
2915
+ }
2890
2916
  const prevFont = ctx.font;
2891
2917
  const prevAlign = ctx.textAlign;
2892
2918
  const prevBaseline = ctx.textBaseline;
2893
2919
  ctx.font = `500 10px ${mergedOptions.fontFamily}`;
2894
2920
  ctx.textAlign = "center";
2921
+ ctx.textBaseline = "middle";
2895
2922
  const arrowHalf = 5;
2896
2923
  const arrowH = 8;
2897
2924
  const gap = 5;
2898
- for (const marker of tradeMarkers) {
2899
- const ms = typeof marker.time === "number" ? marker.time : Date.parse(String(marker.time));
2900
- if (!Number.isFinite(ms)) continue;
2901
- const index = indexForTime(ms);
2902
- if (index === null || index < visibleStart || index > visibleEnd) continue;
2903
- const bar = data[index];
2925
+ const labelOffset = arrowH + 8;
2926
+ for (const group of groups.values()) {
2927
+ const bar = data[group.index];
2904
2928
  if (!bar) continue;
2905
- const x = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
2929
+ const x = chartLeft + (group.index + 0.5 - xStart) / xSpan * chartWidth;
2906
2930
  if (x < chartLeft - 20 || x > chartRight + 20) continue;
2907
- const isBuy = marker.side === "buy";
2908
- const arrowColor = marker.color ?? (isBuy ? "#2962ff" : "#f23645");
2909
- const textColor = marker.textColor ?? "#d1d4dc";
2910
- const label = marker.text ?? `${marker.qty ?? ""}${marker.qty != null ? " @ " : ""}${formatPrice(marker.price)}`.trim();
2931
+ const isBuy = group.side === "buy";
2932
+ const arrowColor = group.color ?? (isBuy ? "#2962ff" : "#f23645");
2933
+ const textColor = group.textColor ?? "#d1d4dc";
2934
+ const weightTotal = group.qty > 0 ? group.qty : 1;
2935
+ const avgPrice = group.priceQty / weightTotal;
2936
+ const label = group.text ?? `${group.qty > 0 ? `${group.qty} @ ` : ""}${formatPrice(avgPrice)}`.trim();
2911
2937
  ctx.save();
2912
2938
  ctx.fillStyle = arrowColor;
2913
- if (isBuy) {
2914
- const tipY = yFromPrice(bar.l) + gap;
2915
- ctx.beginPath();
2916
- ctx.moveTo(x, tipY);
2917
- ctx.lineTo(x - arrowHalf, tipY + arrowH);
2918
- ctx.lineTo(x + arrowHalf, tipY + arrowH);
2919
- ctx.closePath();
2920
- ctx.fill();
2921
- ctx.fillStyle = textColor;
2922
- ctx.textBaseline = "top";
2923
- ctx.fillText(label, x, tipY + arrowH + 2);
2924
- } else {
2925
- const tipY = yFromPrice(bar.h) - gap;
2926
- ctx.beginPath();
2927
- ctx.moveTo(x, tipY);
2928
- ctx.lineTo(x - arrowHalf, tipY - arrowH);
2929
- ctx.lineTo(x + arrowHalf, tipY - arrowH);
2930
- ctx.closePath();
2931
- ctx.fill();
2932
- ctx.fillStyle = textColor;
2933
- ctx.textBaseline = "bottom";
2934
- ctx.fillText(label, x, tipY - arrowH - 2);
2935
- }
2939
+ const tipY = isBuy ? yFromPrice(bar.l) + gap : yFromPrice(bar.h) - gap;
2940
+ const dir = isBuy ? 1 : -1;
2941
+ ctx.beginPath();
2942
+ ctx.moveTo(x, tipY);
2943
+ ctx.lineTo(x - arrowHalf, tipY + arrowH * dir);
2944
+ ctx.lineTo(x + arrowHalf, tipY + arrowH * dir);
2945
+ ctx.closePath();
2946
+ ctx.fill();
2947
+ ctx.fillStyle = textColor;
2948
+ ctx.fillText(label, Math.round(x), Math.round(tipY + labelOffset * dir));
2936
2949
  ctx.restore();
2937
2950
  }
2938
2951
  ctx.font = prevFont;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.101",
3
+ "version": "0.1.103",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",