hyperprop-charting-library 0.1.121 → 0.1.123

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.
@@ -221,6 +221,8 @@ var DEFAULT_OPTIONS = {
221
221
  autoScaleSmoothing: 0,
222
222
  autoScaleIgnoreLatestCandle: true,
223
223
  kineticScroll: { touch: true, mouse: false },
224
+ timeStepMs: 0,
225
+ clockOffsetMs: 0,
224
226
  pinOutOfRangeLines: false,
225
227
  doubleClickEnabled: true,
226
228
  doubleClickAction: "reset",
@@ -1720,11 +1722,16 @@ function createChart(element, options = {}) {
1720
1722
  return Array.from(dedupedByTime.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
1721
1723
  };
1722
1724
  const getTimeStepMs = () => {
1725
+ const configured = Number(mergedOptions.timeStepMs);
1726
+ if (Number.isFinite(configured) && configured > 0) {
1727
+ return configured;
1728
+ }
1723
1729
  if (data.length < 2) {
1724
1730
  return 24 * 60 * 60 * 1e3;
1725
1731
  }
1726
1732
  const deltas = [];
1727
- for (let index = 1; index < Math.min(data.length, 40); index += 1) {
1733
+ const first = Math.max(1, data.length - 40);
1734
+ for (let index = first; index < data.length; index += 1) {
1728
1735
  const previous = data[index - 1];
1729
1736
  const current = data[index];
1730
1737
  if (!previous || !current) {
@@ -3633,8 +3640,9 @@ function createChart(element, options = {}) {
3633
3640
  return null;
3634
3641
  }
3635
3642
  const stepMs = getTimeStepMs();
3636
- const rawRemainingMs = last.time.getTime() + stepMs - Date.now();
3637
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3643
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3644
+ const elapsedMs = nowMs - last.time.getTime();
3645
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3638
3646
  return formatDuration(countdownMs);
3639
3647
  };
3640
3648
  const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
@@ -3887,8 +3895,9 @@ function createChart(element, options = {}) {
3887
3895
  }
3888
3896
  if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
3889
3897
  const stepMs = getTimeStepMs();
3890
- const rawRemainingMs = lastPoint.time.getTime() + stepMs - Date.now();
3891
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3898
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3899
+ const elapsedMs = nowMs - lastPoint.time.getTime();
3900
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3892
3901
  const countdownText = formatDuration(countdownMs);
3893
3902
  const prevFont = ctx.font;
3894
3903
  ctx.font = `${xAxisFontSize}px ${mergedOptions.fontFamily}`;
@@ -37,6 +37,22 @@ interface ChartOptions {
37
37
  touch?: boolean;
38
38
  mouse?: boolean;
39
39
  };
40
+ /**
41
+ * Authoritative bar interval in milliseconds. When set, the bar-close
42
+ * countdown, time extrapolation beyond the last bar, and axis density use
43
+ * this exact step instead of inferring it from bar spacing (inference can
44
+ * mis-read series whose deep history has gaps, e.g. session-aligned 4h
45
+ * futures data with sparse back-month eras). 0/undefined = infer.
46
+ */
47
+ timeStepMs?: number;
48
+ /**
49
+ * Correction added to the machine's clock for time-sensitive rendering
50
+ * (bar-close countdowns): exchangeNow ≈ Date.now() + clockOffsetMs. Hosts
51
+ * should derive it from exchange-stamped ticks; without it a skewed local
52
+ * clock freezes the countdown at one full interval (bars arrive stamped
53
+ * "in the future") or makes it run visibly early/late.
54
+ */
55
+ clockOffsetMs?: number;
40
56
  pinOutOfRangeLines?: boolean;
41
57
  doubleClickEnabled?: boolean;
42
58
  doubleClickAction?: "reset" | "placeLimitOrder";
@@ -195,6 +195,8 @@ var DEFAULT_OPTIONS = {
195
195
  autoScaleSmoothing: 0,
196
196
  autoScaleIgnoreLatestCandle: true,
197
197
  kineticScroll: { touch: true, mouse: false },
198
+ timeStepMs: 0,
199
+ clockOffsetMs: 0,
198
200
  pinOutOfRangeLines: false,
199
201
  doubleClickEnabled: true,
200
202
  doubleClickAction: "reset",
@@ -1694,11 +1696,16 @@ function createChart(element, options = {}) {
1694
1696
  return Array.from(dedupedByTime.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
1695
1697
  };
1696
1698
  const getTimeStepMs = () => {
1699
+ const configured = Number(mergedOptions.timeStepMs);
1700
+ if (Number.isFinite(configured) && configured > 0) {
1701
+ return configured;
1702
+ }
1697
1703
  if (data.length < 2) {
1698
1704
  return 24 * 60 * 60 * 1e3;
1699
1705
  }
1700
1706
  const deltas = [];
1701
- for (let index = 1; index < Math.min(data.length, 40); index += 1) {
1707
+ const first = Math.max(1, data.length - 40);
1708
+ for (let index = first; index < data.length; index += 1) {
1702
1709
  const previous = data[index - 1];
1703
1710
  const current = data[index];
1704
1711
  if (!previous || !current) {
@@ -3607,8 +3614,9 @@ function createChart(element, options = {}) {
3607
3614
  return null;
3608
3615
  }
3609
3616
  const stepMs = getTimeStepMs();
3610
- const rawRemainingMs = last.time.getTime() + stepMs - Date.now();
3611
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3617
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3618
+ const elapsedMs = nowMs - last.time.getTime();
3619
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3612
3620
  return formatDuration(countdownMs);
3613
3621
  };
3614
3622
  const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
@@ -3861,8 +3869,9 @@ function createChart(element, options = {}) {
3861
3869
  }
3862
3870
  if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
3863
3871
  const stepMs = getTimeStepMs();
3864
- const rawRemainingMs = lastPoint.time.getTime() + stepMs - Date.now();
3865
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3872
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3873
+ const elapsedMs = nowMs - lastPoint.time.getTime();
3874
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3866
3875
  const countdownText = formatDuration(countdownMs);
3867
3876
  const prevFont = ctx.font;
3868
3877
  ctx.font = `${xAxisFontSize}px ${mergedOptions.fontFamily}`;
package/dist/index.cjs CHANGED
@@ -221,6 +221,8 @@ var DEFAULT_OPTIONS = {
221
221
  autoScaleSmoothing: 0,
222
222
  autoScaleIgnoreLatestCandle: true,
223
223
  kineticScroll: { touch: true, mouse: false },
224
+ timeStepMs: 0,
225
+ clockOffsetMs: 0,
224
226
  pinOutOfRangeLines: false,
225
227
  doubleClickEnabled: true,
226
228
  doubleClickAction: "reset",
@@ -1720,11 +1722,16 @@ function createChart(element, options = {}) {
1720
1722
  return Array.from(dedupedByTime.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
1721
1723
  };
1722
1724
  const getTimeStepMs = () => {
1725
+ const configured = Number(mergedOptions.timeStepMs);
1726
+ if (Number.isFinite(configured) && configured > 0) {
1727
+ return configured;
1728
+ }
1723
1729
  if (data.length < 2) {
1724
1730
  return 24 * 60 * 60 * 1e3;
1725
1731
  }
1726
1732
  const deltas = [];
1727
- for (let index = 1; index < Math.min(data.length, 40); index += 1) {
1733
+ const first = Math.max(1, data.length - 40);
1734
+ for (let index = first; index < data.length; index += 1) {
1728
1735
  const previous = data[index - 1];
1729
1736
  const current = data[index];
1730
1737
  if (!previous || !current) {
@@ -3633,8 +3640,9 @@ function createChart(element, options = {}) {
3633
3640
  return null;
3634
3641
  }
3635
3642
  const stepMs = getTimeStepMs();
3636
- const rawRemainingMs = last.time.getTime() + stepMs - Date.now();
3637
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3643
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3644
+ const elapsedMs = nowMs - last.time.getTime();
3645
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3638
3646
  return formatDuration(countdownMs);
3639
3647
  };
3640
3648
  const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
@@ -3887,8 +3895,9 @@ function createChart(element, options = {}) {
3887
3895
  }
3888
3896
  if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
3889
3897
  const stepMs = getTimeStepMs();
3890
- const rawRemainingMs = lastPoint.time.getTime() + stepMs - Date.now();
3891
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3898
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3899
+ const elapsedMs = nowMs - lastPoint.time.getTime();
3900
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3892
3901
  const countdownText = formatDuration(countdownMs);
3893
3902
  const prevFont = ctx.font;
3894
3903
  ctx.font = `${xAxisFontSize}px ${mergedOptions.fontFamily}`;
package/dist/index.d.cts CHANGED
@@ -37,6 +37,22 @@ interface ChartOptions {
37
37
  touch?: boolean;
38
38
  mouse?: boolean;
39
39
  };
40
+ /**
41
+ * Authoritative bar interval in milliseconds. When set, the bar-close
42
+ * countdown, time extrapolation beyond the last bar, and axis density use
43
+ * this exact step instead of inferring it from bar spacing (inference can
44
+ * mis-read series whose deep history has gaps, e.g. session-aligned 4h
45
+ * futures data with sparse back-month eras). 0/undefined = infer.
46
+ */
47
+ timeStepMs?: number;
48
+ /**
49
+ * Correction added to the machine's clock for time-sensitive rendering
50
+ * (bar-close countdowns): exchangeNow ≈ Date.now() + clockOffsetMs. Hosts
51
+ * should derive it from exchange-stamped ticks; without it a skewed local
52
+ * clock freezes the countdown at one full interval (bars arrive stamped
53
+ * "in the future") or makes it run visibly early/late.
54
+ */
55
+ clockOffsetMs?: number;
40
56
  pinOutOfRangeLines?: boolean;
41
57
  doubleClickEnabled?: boolean;
42
58
  doubleClickAction?: "reset" | "placeLimitOrder";
package/dist/index.d.ts CHANGED
@@ -37,6 +37,22 @@ interface ChartOptions {
37
37
  touch?: boolean;
38
38
  mouse?: boolean;
39
39
  };
40
+ /**
41
+ * Authoritative bar interval in milliseconds. When set, the bar-close
42
+ * countdown, time extrapolation beyond the last bar, and axis density use
43
+ * this exact step instead of inferring it from bar spacing (inference can
44
+ * mis-read series whose deep history has gaps, e.g. session-aligned 4h
45
+ * futures data with sparse back-month eras). 0/undefined = infer.
46
+ */
47
+ timeStepMs?: number;
48
+ /**
49
+ * Correction added to the machine's clock for time-sensitive rendering
50
+ * (bar-close countdowns): exchangeNow ≈ Date.now() + clockOffsetMs. Hosts
51
+ * should derive it from exchange-stamped ticks; without it a skewed local
52
+ * clock freezes the countdown at one full interval (bars arrive stamped
53
+ * "in the future") or makes it run visibly early/late.
54
+ */
55
+ clockOffsetMs?: number;
40
56
  pinOutOfRangeLines?: boolean;
41
57
  doubleClickEnabled?: boolean;
42
58
  doubleClickAction?: "reset" | "placeLimitOrder";
package/dist/index.js CHANGED
@@ -195,6 +195,8 @@ var DEFAULT_OPTIONS = {
195
195
  autoScaleSmoothing: 0,
196
196
  autoScaleIgnoreLatestCandle: true,
197
197
  kineticScroll: { touch: true, mouse: false },
198
+ timeStepMs: 0,
199
+ clockOffsetMs: 0,
198
200
  pinOutOfRangeLines: false,
199
201
  doubleClickEnabled: true,
200
202
  doubleClickAction: "reset",
@@ -1694,11 +1696,16 @@ function createChart(element, options = {}) {
1694
1696
  return Array.from(dedupedByTime.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
1695
1697
  };
1696
1698
  const getTimeStepMs = () => {
1699
+ const configured = Number(mergedOptions.timeStepMs);
1700
+ if (Number.isFinite(configured) && configured > 0) {
1701
+ return configured;
1702
+ }
1697
1703
  if (data.length < 2) {
1698
1704
  return 24 * 60 * 60 * 1e3;
1699
1705
  }
1700
1706
  const deltas = [];
1701
- for (let index = 1; index < Math.min(data.length, 40); index += 1) {
1707
+ const first = Math.max(1, data.length - 40);
1708
+ for (let index = first; index < data.length; index += 1) {
1702
1709
  const previous = data[index - 1];
1703
1710
  const current = data[index];
1704
1711
  if (!previous || !current) {
@@ -3607,8 +3614,9 @@ function createChart(element, options = {}) {
3607
3614
  return null;
3608
3615
  }
3609
3616
  const stepMs = getTimeStepMs();
3610
- const rawRemainingMs = last.time.getTime() + stepMs - Date.now();
3611
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3617
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3618
+ const elapsedMs = nowMs - last.time.getTime();
3619
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3612
3620
  return formatDuration(countdownMs);
3613
3621
  };
3614
3622
  const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
@@ -3861,8 +3869,9 @@ function createChart(element, options = {}) {
3861
3869
  }
3862
3870
  if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
3863
3871
  const stepMs = getTimeStepMs();
3864
- const rawRemainingMs = lastPoint.time.getTime() + stepMs - Date.now();
3865
- const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
3872
+ const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
3873
+ const elapsedMs = nowMs - lastPoint.time.getTime();
3874
+ const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
3866
3875
  const countdownText = formatDuration(countdownMs);
3867
3876
  const prevFont = ctx.font;
3868
3877
  ctx.font = `${xAxisFontSize}px ${mergedOptions.fontFamily}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.121",
3
+ "version": "0.1.123",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",