hyperprop-charting-library 0.1.122 → 0.1.124
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.
|
@@ -222,6 +222,7 @@ var DEFAULT_OPTIONS = {
|
|
|
222
222
|
autoScaleIgnoreLatestCandle: true,
|
|
223
223
|
kineticScroll: { touch: true, mouse: false },
|
|
224
224
|
timeStepMs: 0,
|
|
225
|
+
clockOffsetMs: 0,
|
|
225
226
|
pinOutOfRangeLines: false,
|
|
226
227
|
doubleClickEnabled: true,
|
|
227
228
|
doubleClickAction: "reset",
|
|
@@ -1363,7 +1364,7 @@ function createChart(element, options = {}) {
|
|
|
1363
1364
|
return;
|
|
1364
1365
|
}
|
|
1365
1366
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1366
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1367
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1367
1368
|
const dt = 1 / 60;
|
|
1368
1369
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1369
1370
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1800,6 +1801,56 @@ function createChart(element, options = {}) {
|
|
|
1800
1801
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1801
1802
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1802
1803
|
};
|
|
1804
|
+
const indexForTimeMs = (timeMs) => {
|
|
1805
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
const firstMs = data[0].time.getTime();
|
|
1809
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1810
|
+
if (timeMs < firstMs) {
|
|
1811
|
+
const stepMs = getTimeStepMs();
|
|
1812
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1813
|
+
}
|
|
1814
|
+
if (timeMs > lastMs) {
|
|
1815
|
+
const stepMs = getTimeStepMs();
|
|
1816
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1817
|
+
}
|
|
1818
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1819
|
+
};
|
|
1820
|
+
const reanchorDrawingPoint = (point) => {
|
|
1821
|
+
if (!point.time) {
|
|
1822
|
+
return point;
|
|
1823
|
+
}
|
|
1824
|
+
const timeMs = Date.parse(point.time);
|
|
1825
|
+
if (!Number.isFinite(timeMs)) {
|
|
1826
|
+
return point;
|
|
1827
|
+
}
|
|
1828
|
+
const rounded = Math.round(point.index);
|
|
1829
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1830
|
+
return point;
|
|
1831
|
+
}
|
|
1832
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1833
|
+
if (mapped === null) {
|
|
1834
|
+
return point;
|
|
1835
|
+
}
|
|
1836
|
+
const fraction = point.index - rounded;
|
|
1837
|
+
return { ...point, index: mapped + fraction };
|
|
1838
|
+
};
|
|
1839
|
+
const reanchorDrawingsToData = () => {
|
|
1840
|
+
if (data.length === 0) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
drawings = drawings.map((drawing) => ({
|
|
1844
|
+
...drawing,
|
|
1845
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1846
|
+
}));
|
|
1847
|
+
if (draftDrawing) {
|
|
1848
|
+
draftDrawing = {
|
|
1849
|
+
...draftDrawing,
|
|
1850
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1803
1854
|
const getCandleDirectionByIndex = (index) => {
|
|
1804
1855
|
const point = data[index];
|
|
1805
1856
|
if (!point) {
|
|
@@ -3639,7 +3690,8 @@ function createChart(element, options = {}) {
|
|
|
3639
3690
|
return null;
|
|
3640
3691
|
}
|
|
3641
3692
|
const stepMs = getTimeStepMs();
|
|
3642
|
-
const
|
|
3693
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3694
|
+
const elapsedMs = nowMs - last.time.getTime();
|
|
3643
3695
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3644
3696
|
return formatDuration(countdownMs);
|
|
3645
3697
|
};
|
|
@@ -3893,7 +3945,8 @@ function createChart(element, options = {}) {
|
|
|
3893
3945
|
}
|
|
3894
3946
|
if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
|
|
3895
3947
|
const stepMs = getTimeStepMs();
|
|
3896
|
-
const
|
|
3948
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3949
|
+
const elapsedMs = nowMs - lastPoint.time.getTime();
|
|
3897
3950
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3898
3951
|
const countdownText = formatDuration(countdownMs);
|
|
3899
3952
|
const prevFont = ctx.font;
|
|
@@ -5782,6 +5835,7 @@ function createChart(element, options = {}) {
|
|
|
5782
5835
|
fitXViewport();
|
|
5783
5836
|
}
|
|
5784
5837
|
}
|
|
5838
|
+
reanchorDrawingsToData();
|
|
5785
5839
|
const lastPoint = data[data.length - 1];
|
|
5786
5840
|
if (lastPoint) {
|
|
5787
5841
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5828,6 +5882,7 @@ function createChart(element, options = {}) {
|
|
|
5828
5882
|
}
|
|
5829
5883
|
merged.set(timeMs, parsed);
|
|
5830
5884
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5885
|
+
reanchorDrawingsToData();
|
|
5831
5886
|
}
|
|
5832
5887
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5833
5888
|
scheduleDraw();
|
|
@@ -6010,11 +6065,13 @@ function createChart(element, options = {}) {
|
|
|
6010
6065
|
const setDrawings = (nextDrawings) => {
|
|
6011
6066
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
6012
6067
|
draftDrawing = null;
|
|
6068
|
+
reanchorDrawingsToData();
|
|
6013
6069
|
emitDrawingsChange();
|
|
6014
6070
|
scheduleDraw();
|
|
6015
6071
|
};
|
|
6016
6072
|
const addDrawing = (drawing) => {
|
|
6017
6073
|
const next = normalizeDrawingState(drawing);
|
|
6074
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
6018
6075
|
drawings.push(next);
|
|
6019
6076
|
emitDrawingsChange();
|
|
6020
6077
|
scheduleDraw();
|
|
@@ -6026,7 +6083,7 @@ function createChart(element, options = {}) {
|
|
|
6026
6083
|
...serializeDrawing(drawing),
|
|
6027
6084
|
...patch,
|
|
6028
6085
|
id,
|
|
6029
|
-
points: patch.points ?? drawing.points
|
|
6086
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6030
6087
|
}) : drawing
|
|
6031
6088
|
);
|
|
6032
6089
|
emitDrawingsChange();
|
|
@@ -45,6 +45,14 @@ interface ChartOptions {
|
|
|
45
45
|
* futures data with sparse back-month eras). 0/undefined = infer.
|
|
46
46
|
*/
|
|
47
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;
|
|
48
56
|
pinOutOfRangeLines?: boolean;
|
|
49
57
|
doubleClickEnabled?: boolean;
|
|
50
58
|
doubleClickAction?: "reset" | "placeLimitOrder";
|
|
@@ -196,6 +196,7 @@ var DEFAULT_OPTIONS = {
|
|
|
196
196
|
autoScaleIgnoreLatestCandle: true,
|
|
197
197
|
kineticScroll: { touch: true, mouse: false },
|
|
198
198
|
timeStepMs: 0,
|
|
199
|
+
clockOffsetMs: 0,
|
|
199
200
|
pinOutOfRangeLines: false,
|
|
200
201
|
doubleClickEnabled: true,
|
|
201
202
|
doubleClickAction: "reset",
|
|
@@ -1337,7 +1338,7 @@ function createChart(element, options = {}) {
|
|
|
1337
1338
|
return;
|
|
1338
1339
|
}
|
|
1339
1340
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1340
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1341
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1341
1342
|
const dt = 1 / 60;
|
|
1342
1343
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1343
1344
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1774,6 +1775,56 @@ function createChart(element, options = {}) {
|
|
|
1774
1775
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1775
1776
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1776
1777
|
};
|
|
1778
|
+
const indexForTimeMs = (timeMs) => {
|
|
1779
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1780
|
+
return null;
|
|
1781
|
+
}
|
|
1782
|
+
const firstMs = data[0].time.getTime();
|
|
1783
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1784
|
+
if (timeMs < firstMs) {
|
|
1785
|
+
const stepMs = getTimeStepMs();
|
|
1786
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1787
|
+
}
|
|
1788
|
+
if (timeMs > lastMs) {
|
|
1789
|
+
const stepMs = getTimeStepMs();
|
|
1790
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1791
|
+
}
|
|
1792
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1793
|
+
};
|
|
1794
|
+
const reanchorDrawingPoint = (point) => {
|
|
1795
|
+
if (!point.time) {
|
|
1796
|
+
return point;
|
|
1797
|
+
}
|
|
1798
|
+
const timeMs = Date.parse(point.time);
|
|
1799
|
+
if (!Number.isFinite(timeMs)) {
|
|
1800
|
+
return point;
|
|
1801
|
+
}
|
|
1802
|
+
const rounded = Math.round(point.index);
|
|
1803
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1804
|
+
return point;
|
|
1805
|
+
}
|
|
1806
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1807
|
+
if (mapped === null) {
|
|
1808
|
+
return point;
|
|
1809
|
+
}
|
|
1810
|
+
const fraction = point.index - rounded;
|
|
1811
|
+
return { ...point, index: mapped + fraction };
|
|
1812
|
+
};
|
|
1813
|
+
const reanchorDrawingsToData = () => {
|
|
1814
|
+
if (data.length === 0) {
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
drawings = drawings.map((drawing) => ({
|
|
1818
|
+
...drawing,
|
|
1819
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1820
|
+
}));
|
|
1821
|
+
if (draftDrawing) {
|
|
1822
|
+
draftDrawing = {
|
|
1823
|
+
...draftDrawing,
|
|
1824
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1777
1828
|
const getCandleDirectionByIndex = (index) => {
|
|
1778
1829
|
const point = data[index];
|
|
1779
1830
|
if (!point) {
|
|
@@ -3613,7 +3664,8 @@ function createChart(element, options = {}) {
|
|
|
3613
3664
|
return null;
|
|
3614
3665
|
}
|
|
3615
3666
|
const stepMs = getTimeStepMs();
|
|
3616
|
-
const
|
|
3667
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3668
|
+
const elapsedMs = nowMs - last.time.getTime();
|
|
3617
3669
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3618
3670
|
return formatDuration(countdownMs);
|
|
3619
3671
|
};
|
|
@@ -3867,7 +3919,8 @@ function createChart(element, options = {}) {
|
|
|
3867
3919
|
}
|
|
3868
3920
|
if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
|
|
3869
3921
|
const stepMs = getTimeStepMs();
|
|
3870
|
-
const
|
|
3922
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3923
|
+
const elapsedMs = nowMs - lastPoint.time.getTime();
|
|
3871
3924
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3872
3925
|
const countdownText = formatDuration(countdownMs);
|
|
3873
3926
|
const prevFont = ctx.font;
|
|
@@ -5756,6 +5809,7 @@ function createChart(element, options = {}) {
|
|
|
5756
5809
|
fitXViewport();
|
|
5757
5810
|
}
|
|
5758
5811
|
}
|
|
5812
|
+
reanchorDrawingsToData();
|
|
5759
5813
|
const lastPoint = data[data.length - 1];
|
|
5760
5814
|
if (lastPoint) {
|
|
5761
5815
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5802,6 +5856,7 @@ function createChart(element, options = {}) {
|
|
|
5802
5856
|
}
|
|
5803
5857
|
merged.set(timeMs, parsed);
|
|
5804
5858
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5859
|
+
reanchorDrawingsToData();
|
|
5805
5860
|
}
|
|
5806
5861
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5807
5862
|
scheduleDraw();
|
|
@@ -5984,11 +6039,13 @@ function createChart(element, options = {}) {
|
|
|
5984
6039
|
const setDrawings = (nextDrawings) => {
|
|
5985
6040
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
5986
6041
|
draftDrawing = null;
|
|
6042
|
+
reanchorDrawingsToData();
|
|
5987
6043
|
emitDrawingsChange();
|
|
5988
6044
|
scheduleDraw();
|
|
5989
6045
|
};
|
|
5990
6046
|
const addDrawing = (drawing) => {
|
|
5991
6047
|
const next = normalizeDrawingState(drawing);
|
|
6048
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
5992
6049
|
drawings.push(next);
|
|
5993
6050
|
emitDrawingsChange();
|
|
5994
6051
|
scheduleDraw();
|
|
@@ -6000,7 +6057,7 @@ function createChart(element, options = {}) {
|
|
|
6000
6057
|
...serializeDrawing(drawing),
|
|
6001
6058
|
...patch,
|
|
6002
6059
|
id,
|
|
6003
|
-
points: patch.points ?? drawing.points
|
|
6060
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6004
6061
|
}) : drawing
|
|
6005
6062
|
);
|
|
6006
6063
|
emitDrawingsChange();
|
package/dist/index.cjs
CHANGED
|
@@ -222,6 +222,7 @@ var DEFAULT_OPTIONS = {
|
|
|
222
222
|
autoScaleIgnoreLatestCandle: true,
|
|
223
223
|
kineticScroll: { touch: true, mouse: false },
|
|
224
224
|
timeStepMs: 0,
|
|
225
|
+
clockOffsetMs: 0,
|
|
225
226
|
pinOutOfRangeLines: false,
|
|
226
227
|
doubleClickEnabled: true,
|
|
227
228
|
doubleClickAction: "reset",
|
|
@@ -1363,7 +1364,7 @@ function createChart(element, options = {}) {
|
|
|
1363
1364
|
return;
|
|
1364
1365
|
}
|
|
1365
1366
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1366
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1367
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1367
1368
|
const dt = 1 / 60;
|
|
1368
1369
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1369
1370
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1800,6 +1801,56 @@ function createChart(element, options = {}) {
|
|
|
1800
1801
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1801
1802
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1802
1803
|
};
|
|
1804
|
+
const indexForTimeMs = (timeMs) => {
|
|
1805
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
const firstMs = data[0].time.getTime();
|
|
1809
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1810
|
+
if (timeMs < firstMs) {
|
|
1811
|
+
const stepMs = getTimeStepMs();
|
|
1812
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1813
|
+
}
|
|
1814
|
+
if (timeMs > lastMs) {
|
|
1815
|
+
const stepMs = getTimeStepMs();
|
|
1816
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1817
|
+
}
|
|
1818
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1819
|
+
};
|
|
1820
|
+
const reanchorDrawingPoint = (point) => {
|
|
1821
|
+
if (!point.time) {
|
|
1822
|
+
return point;
|
|
1823
|
+
}
|
|
1824
|
+
const timeMs = Date.parse(point.time);
|
|
1825
|
+
if (!Number.isFinite(timeMs)) {
|
|
1826
|
+
return point;
|
|
1827
|
+
}
|
|
1828
|
+
const rounded = Math.round(point.index);
|
|
1829
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1830
|
+
return point;
|
|
1831
|
+
}
|
|
1832
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1833
|
+
if (mapped === null) {
|
|
1834
|
+
return point;
|
|
1835
|
+
}
|
|
1836
|
+
const fraction = point.index - rounded;
|
|
1837
|
+
return { ...point, index: mapped + fraction };
|
|
1838
|
+
};
|
|
1839
|
+
const reanchorDrawingsToData = () => {
|
|
1840
|
+
if (data.length === 0) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
drawings = drawings.map((drawing) => ({
|
|
1844
|
+
...drawing,
|
|
1845
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1846
|
+
}));
|
|
1847
|
+
if (draftDrawing) {
|
|
1848
|
+
draftDrawing = {
|
|
1849
|
+
...draftDrawing,
|
|
1850
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1803
1854
|
const getCandleDirectionByIndex = (index) => {
|
|
1804
1855
|
const point = data[index];
|
|
1805
1856
|
if (!point) {
|
|
@@ -3639,7 +3690,8 @@ function createChart(element, options = {}) {
|
|
|
3639
3690
|
return null;
|
|
3640
3691
|
}
|
|
3641
3692
|
const stepMs = getTimeStepMs();
|
|
3642
|
-
const
|
|
3693
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3694
|
+
const elapsedMs = nowMs - last.time.getTime();
|
|
3643
3695
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3644
3696
|
return formatDuration(countdownMs);
|
|
3645
3697
|
};
|
|
@@ -3893,7 +3945,8 @@ function createChart(element, options = {}) {
|
|
|
3893
3945
|
}
|
|
3894
3946
|
if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
|
|
3895
3947
|
const stepMs = getTimeStepMs();
|
|
3896
|
-
const
|
|
3948
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3949
|
+
const elapsedMs = nowMs - lastPoint.time.getTime();
|
|
3897
3950
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3898
3951
|
const countdownText = formatDuration(countdownMs);
|
|
3899
3952
|
const prevFont = ctx.font;
|
|
@@ -5782,6 +5835,7 @@ function createChart(element, options = {}) {
|
|
|
5782
5835
|
fitXViewport();
|
|
5783
5836
|
}
|
|
5784
5837
|
}
|
|
5838
|
+
reanchorDrawingsToData();
|
|
5785
5839
|
const lastPoint = data[data.length - 1];
|
|
5786
5840
|
if (lastPoint) {
|
|
5787
5841
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5828,6 +5882,7 @@ function createChart(element, options = {}) {
|
|
|
5828
5882
|
}
|
|
5829
5883
|
merged.set(timeMs, parsed);
|
|
5830
5884
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5885
|
+
reanchorDrawingsToData();
|
|
5831
5886
|
}
|
|
5832
5887
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5833
5888
|
scheduleDraw();
|
|
@@ -6010,11 +6065,13 @@ function createChart(element, options = {}) {
|
|
|
6010
6065
|
const setDrawings = (nextDrawings) => {
|
|
6011
6066
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
6012
6067
|
draftDrawing = null;
|
|
6068
|
+
reanchorDrawingsToData();
|
|
6013
6069
|
emitDrawingsChange();
|
|
6014
6070
|
scheduleDraw();
|
|
6015
6071
|
};
|
|
6016
6072
|
const addDrawing = (drawing) => {
|
|
6017
6073
|
const next = normalizeDrawingState(drawing);
|
|
6074
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
6018
6075
|
drawings.push(next);
|
|
6019
6076
|
emitDrawingsChange();
|
|
6020
6077
|
scheduleDraw();
|
|
@@ -6026,7 +6083,7 @@ function createChart(element, options = {}) {
|
|
|
6026
6083
|
...serializeDrawing(drawing),
|
|
6027
6084
|
...patch,
|
|
6028
6085
|
id,
|
|
6029
|
-
points: patch.points ?? drawing.points
|
|
6086
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6030
6087
|
}) : drawing
|
|
6031
6088
|
);
|
|
6032
6089
|
emitDrawingsChange();
|
package/dist/index.d.cts
CHANGED
|
@@ -45,6 +45,14 @@ interface ChartOptions {
|
|
|
45
45
|
* futures data with sparse back-month eras). 0/undefined = infer.
|
|
46
46
|
*/
|
|
47
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;
|
|
48
56
|
pinOutOfRangeLines?: boolean;
|
|
49
57
|
doubleClickEnabled?: boolean;
|
|
50
58
|
doubleClickAction?: "reset" | "placeLimitOrder";
|
package/dist/index.d.ts
CHANGED
|
@@ -45,6 +45,14 @@ interface ChartOptions {
|
|
|
45
45
|
* futures data with sparse back-month eras). 0/undefined = infer.
|
|
46
46
|
*/
|
|
47
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;
|
|
48
56
|
pinOutOfRangeLines?: boolean;
|
|
49
57
|
doubleClickEnabled?: boolean;
|
|
50
58
|
doubleClickAction?: "reset" | "placeLimitOrder";
|
package/dist/index.js
CHANGED
|
@@ -196,6 +196,7 @@ var DEFAULT_OPTIONS = {
|
|
|
196
196
|
autoScaleIgnoreLatestCandle: true,
|
|
197
197
|
kineticScroll: { touch: true, mouse: false },
|
|
198
198
|
timeStepMs: 0,
|
|
199
|
+
clockOffsetMs: 0,
|
|
199
200
|
pinOutOfRangeLines: false,
|
|
200
201
|
doubleClickEnabled: true,
|
|
201
202
|
doubleClickAction: "reset",
|
|
@@ -1337,7 +1338,7 @@ function createChart(element, options = {}) {
|
|
|
1337
1338
|
return;
|
|
1338
1339
|
}
|
|
1339
1340
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1340
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1341
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1341
1342
|
const dt = 1 / 60;
|
|
1342
1343
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1343
1344
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1774,6 +1775,56 @@ function createChart(element, options = {}) {
|
|
|
1774
1775
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1775
1776
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1776
1777
|
};
|
|
1778
|
+
const indexForTimeMs = (timeMs) => {
|
|
1779
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1780
|
+
return null;
|
|
1781
|
+
}
|
|
1782
|
+
const firstMs = data[0].time.getTime();
|
|
1783
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1784
|
+
if (timeMs < firstMs) {
|
|
1785
|
+
const stepMs = getTimeStepMs();
|
|
1786
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1787
|
+
}
|
|
1788
|
+
if (timeMs > lastMs) {
|
|
1789
|
+
const stepMs = getTimeStepMs();
|
|
1790
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1791
|
+
}
|
|
1792
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1793
|
+
};
|
|
1794
|
+
const reanchorDrawingPoint = (point) => {
|
|
1795
|
+
if (!point.time) {
|
|
1796
|
+
return point;
|
|
1797
|
+
}
|
|
1798
|
+
const timeMs = Date.parse(point.time);
|
|
1799
|
+
if (!Number.isFinite(timeMs)) {
|
|
1800
|
+
return point;
|
|
1801
|
+
}
|
|
1802
|
+
const rounded = Math.round(point.index);
|
|
1803
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1804
|
+
return point;
|
|
1805
|
+
}
|
|
1806
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1807
|
+
if (mapped === null) {
|
|
1808
|
+
return point;
|
|
1809
|
+
}
|
|
1810
|
+
const fraction = point.index - rounded;
|
|
1811
|
+
return { ...point, index: mapped + fraction };
|
|
1812
|
+
};
|
|
1813
|
+
const reanchorDrawingsToData = () => {
|
|
1814
|
+
if (data.length === 0) {
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
drawings = drawings.map((drawing) => ({
|
|
1818
|
+
...drawing,
|
|
1819
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1820
|
+
}));
|
|
1821
|
+
if (draftDrawing) {
|
|
1822
|
+
draftDrawing = {
|
|
1823
|
+
...draftDrawing,
|
|
1824
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1777
1828
|
const getCandleDirectionByIndex = (index) => {
|
|
1778
1829
|
const point = data[index];
|
|
1779
1830
|
if (!point) {
|
|
@@ -3613,7 +3664,8 @@ function createChart(element, options = {}) {
|
|
|
3613
3664
|
return null;
|
|
3614
3665
|
}
|
|
3615
3666
|
const stepMs = getTimeStepMs();
|
|
3616
|
-
const
|
|
3667
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3668
|
+
const elapsedMs = nowMs - last.time.getTime();
|
|
3617
3669
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3618
3670
|
return formatDuration(countdownMs);
|
|
3619
3671
|
};
|
|
@@ -3867,7 +3919,8 @@ function createChart(element, options = {}) {
|
|
|
3867
3919
|
}
|
|
3868
3920
|
if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
|
|
3869
3921
|
const stepMs = getTimeStepMs();
|
|
3870
|
-
const
|
|
3922
|
+
const nowMs = Date.now() + (Number(mergedOptions.clockOffsetMs) || 0);
|
|
3923
|
+
const elapsedMs = nowMs - lastPoint.time.getTime();
|
|
3871
3924
|
const countdownMs = elapsedMs >= 0 ? stepMs - elapsedMs % stepMs : Math.min(stepMs, stepMs - elapsedMs);
|
|
3872
3925
|
const countdownText = formatDuration(countdownMs);
|
|
3873
3926
|
const prevFont = ctx.font;
|
|
@@ -5756,6 +5809,7 @@ function createChart(element, options = {}) {
|
|
|
5756
5809
|
fitXViewport();
|
|
5757
5810
|
}
|
|
5758
5811
|
}
|
|
5812
|
+
reanchorDrawingsToData();
|
|
5759
5813
|
const lastPoint = data[data.length - 1];
|
|
5760
5814
|
if (lastPoint) {
|
|
5761
5815
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5802,6 +5856,7 @@ function createChart(element, options = {}) {
|
|
|
5802
5856
|
}
|
|
5803
5857
|
merged.set(timeMs, parsed);
|
|
5804
5858
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5859
|
+
reanchorDrawingsToData();
|
|
5805
5860
|
}
|
|
5806
5861
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5807
5862
|
scheduleDraw();
|
|
@@ -5984,11 +6039,13 @@ function createChart(element, options = {}) {
|
|
|
5984
6039
|
const setDrawings = (nextDrawings) => {
|
|
5985
6040
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
5986
6041
|
draftDrawing = null;
|
|
6042
|
+
reanchorDrawingsToData();
|
|
5987
6043
|
emitDrawingsChange();
|
|
5988
6044
|
scheduleDraw();
|
|
5989
6045
|
};
|
|
5990
6046
|
const addDrawing = (drawing) => {
|
|
5991
6047
|
const next = normalizeDrawingState(drawing);
|
|
6048
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
5992
6049
|
drawings.push(next);
|
|
5993
6050
|
emitDrawingsChange();
|
|
5994
6051
|
scheduleDraw();
|
|
@@ -6000,7 +6057,7 @@ function createChart(element, options = {}) {
|
|
|
6000
6057
|
...serializeDrawing(drawing),
|
|
6001
6058
|
...patch,
|
|
6002
6059
|
id,
|
|
6003
|
-
points: patch.points ?? drawing.points
|
|
6060
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6004
6061
|
}) : drawing
|
|
6005
6062
|
);
|
|
6006
6063
|
emitDrawingsChange();
|