hyperprop-charting-library 0.1.34 → 0.1.36
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 +61 -11
- package/dist/hyperprop-charting-library.d.ts +2 -0
- package/dist/hyperprop-charting-library.js +61 -11
- package/dist/index.cjs +61 -11
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +61 -11
- package/package.json +1 -1
|
@@ -171,8 +171,6 @@ var DEFAULT_OPTIONS = {
|
|
|
171
171
|
visible: true,
|
|
172
172
|
style: "dotted",
|
|
173
173
|
thickness: 1,
|
|
174
|
-
color: "#38bdf8",
|
|
175
|
-
labelBackgroundColor: "#38bdf8",
|
|
176
174
|
labelTextColor: "#0b1220",
|
|
177
175
|
labelBorderRadius: 3
|
|
178
176
|
},
|
|
@@ -852,6 +850,41 @@ function createChart(element, options = {}) {
|
|
|
852
850
|
let crosshairPoint = null;
|
|
853
851
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
854
852
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
853
|
+
let smoothedTickerPrice = null;
|
|
854
|
+
let tickerPriceTarget = null;
|
|
855
|
+
let smoothingRafId = null;
|
|
856
|
+
const tickerSmoothingLoop = () => {
|
|
857
|
+
smoothingRafId = null;
|
|
858
|
+
if (smoothedTickerPrice === null || tickerPriceTarget === null) return;
|
|
859
|
+
const diff = tickerPriceTarget - smoothedTickerPrice;
|
|
860
|
+
if (Math.abs(diff) < 1e-9) {
|
|
861
|
+
smoothedTickerPrice = tickerPriceTarget;
|
|
862
|
+
draw();
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
866
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 8, 1, 60);
|
|
867
|
+
const dt = 1 / 60;
|
|
868
|
+
const lerp = 1 - Math.exp(-speed * dt);
|
|
869
|
+
smoothedTickerPrice += diff * lerp;
|
|
870
|
+
draw();
|
|
871
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
872
|
+
};
|
|
873
|
+
const pushSmoothedPrice = (target) => {
|
|
874
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
875
|
+
if (!tickerOpts.smoothing) {
|
|
876
|
+
smoothedTickerPrice = null;
|
|
877
|
+
tickerPriceTarget = null;
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
tickerPriceTarget = target;
|
|
881
|
+
if (smoothedTickerPrice === null) {
|
|
882
|
+
smoothedTickerPrice = target;
|
|
883
|
+
}
|
|
884
|
+
if (smoothingRafId === null) {
|
|
885
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
886
|
+
}
|
|
887
|
+
};
|
|
855
888
|
const canvas = document.createElement("canvas");
|
|
856
889
|
const ctx = canvas.getContext("2d");
|
|
857
890
|
if (!ctx) {
|
|
@@ -1812,28 +1845,37 @@ function createChart(element, options = {}) {
|
|
|
1812
1845
|
}
|
|
1813
1846
|
ctx.restore();
|
|
1814
1847
|
}
|
|
1848
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1849
|
+
const useSmoothedCandle = tickerOpts.smoothing && smoothedTickerPrice !== null;
|
|
1850
|
+
const lastDataIndex = data.length - 1;
|
|
1815
1851
|
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
1816
1852
|
const point = data[index];
|
|
1817
1853
|
if (!point) {
|
|
1818
1854
|
continue;
|
|
1819
1855
|
}
|
|
1856
|
+
const isLastCandle = useSmoothedCandle && index === lastDataIndex;
|
|
1857
|
+
const displayClose = isLastCandle ? smoothedTickerPrice : point.c;
|
|
1858
|
+
const displayHigh = isLastCandle ? Math.max(point.h, smoothedTickerPrice) : point.h;
|
|
1859
|
+
const displayLow = isLastCandle ? Math.min(point.l, smoothedTickerPrice) : point.l;
|
|
1820
1860
|
const centerX = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
1821
1861
|
const openY = yFromPrice(point.o);
|
|
1822
|
-
const closeY = yFromPrice(
|
|
1823
|
-
const highY = yFromPrice(
|
|
1824
|
-
const lowY = yFromPrice(
|
|
1825
|
-
const direction = getCandleDirectionByIndex(index);
|
|
1862
|
+
const closeY = yFromPrice(displayClose);
|
|
1863
|
+
const highY = yFromPrice(displayHigh);
|
|
1864
|
+
const lowY = yFromPrice(displayLow);
|
|
1865
|
+
const direction = isLastCandle ? displayClose >= point.o ? "up" : "down" : getCandleDirectionByIndex(index);
|
|
1826
1866
|
const candleColor = direction === "up" ? mergedOptions.upColor : mergedOptions.downColor;
|
|
1867
|
+
const roundedCenterX = Math.round(centerX);
|
|
1827
1868
|
ctx.strokeStyle = candleColor;
|
|
1828
1869
|
ctx.lineWidth = candleWickWidth;
|
|
1829
1870
|
ctx.beginPath();
|
|
1830
|
-
ctx.moveTo(
|
|
1831
|
-
ctx.lineTo(
|
|
1871
|
+
ctx.moveTo(roundedCenterX + 0.5, crisp(highY));
|
|
1872
|
+
ctx.lineTo(roundedCenterX + 0.5, crisp(lowY));
|
|
1832
1873
|
ctx.stroke();
|
|
1874
|
+
const bodyLeft = roundedCenterX - Math.floor(bodyWidth / 2);
|
|
1833
1875
|
const bodyTop = Math.min(openY, closeY);
|
|
1834
1876
|
const bodyHeight = Math.max(1, Math.abs(closeY - openY));
|
|
1835
1877
|
ctx.fillStyle = candleColor;
|
|
1836
|
-
ctx.fillRect(
|
|
1878
|
+
ctx.fillRect(bodyLeft, Math.round(bodyTop), bodyWidth, Math.max(1, Math.round(bodyHeight)));
|
|
1837
1879
|
}
|
|
1838
1880
|
const activeOverlayIndicators = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
1839
1881
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
@@ -1967,10 +2009,10 @@ function createChart(element, options = {}) {
|
|
|
1967
2009
|
const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1968
2010
|
const lastPoint = data[data.length - 1];
|
|
1969
2011
|
if ((ticker.visible ?? true) && lastPoint) {
|
|
1970
|
-
const tickerPrice = lastPoint.c;
|
|
2012
|
+
const tickerPrice = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice : lastPoint.c;
|
|
1971
2013
|
const tickerY = yFromPrice(tickerPrice);
|
|
1972
2014
|
const lineY = clamp(tickerY, chartTop + 1, chartBottom - 1);
|
|
1973
|
-
const lastDirection = getCandleDirectionByIndex(data.length - 1);
|
|
2015
|
+
const lastDirection = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice >= lastPoint.o ? "up" : "down" : getCandleDirectionByIndex(data.length - 1);
|
|
1974
2016
|
const tickerColor = ticker.color ?? (lastDirection === "up" ? mergedOptions.upColor : mergedOptions.downColor);
|
|
1975
2017
|
const tickerThickness = Math.max(1, ticker.thickness ?? 1);
|
|
1976
2018
|
const tickerStyle = ticker.style ?? "solid";
|
|
@@ -2802,6 +2844,10 @@ function createChart(element, options = {}) {
|
|
|
2802
2844
|
fitXViewport();
|
|
2803
2845
|
}
|
|
2804
2846
|
}
|
|
2847
|
+
const lastClose = data.length > 0 ? data[data.length - 1].c : null;
|
|
2848
|
+
if (lastClose !== null) {
|
|
2849
|
+
pushSmoothedPrice(lastClose);
|
|
2850
|
+
}
|
|
2805
2851
|
draw();
|
|
2806
2852
|
};
|
|
2807
2853
|
const setPriceLines = (lines) => {
|
|
@@ -2952,6 +2998,10 @@ function createChart(element, options = {}) {
|
|
|
2952
2998
|
draw();
|
|
2953
2999
|
};
|
|
2954
3000
|
const destroy = () => {
|
|
3001
|
+
if (smoothingRafId !== null) {
|
|
3002
|
+
cancelAnimationFrame(smoothingRafId);
|
|
3003
|
+
smoothingRafId = null;
|
|
3004
|
+
}
|
|
2955
3005
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
2956
3006
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
2957
3007
|
canvas.removeEventListener("pointerup", endPointerDrag);
|
|
@@ -259,6 +259,8 @@ interface TickerLineOptions {
|
|
|
259
259
|
labelBackgroundColor?: string;
|
|
260
260
|
labelTextColor?: string;
|
|
261
261
|
labelBorderRadius?: number;
|
|
262
|
+
smoothing?: boolean;
|
|
263
|
+
smoothingSpeed?: number;
|
|
262
264
|
}
|
|
263
265
|
interface ChartInstance {
|
|
264
266
|
setData: (data: OhlcDataPoint[]) => void;
|
|
@@ -147,8 +147,6 @@ var DEFAULT_OPTIONS = {
|
|
|
147
147
|
visible: true,
|
|
148
148
|
style: "dotted",
|
|
149
149
|
thickness: 1,
|
|
150
|
-
color: "#38bdf8",
|
|
151
|
-
labelBackgroundColor: "#38bdf8",
|
|
152
150
|
labelTextColor: "#0b1220",
|
|
153
151
|
labelBorderRadius: 3
|
|
154
152
|
},
|
|
@@ -828,6 +826,41 @@ function createChart(element, options = {}) {
|
|
|
828
826
|
let crosshairPoint = null;
|
|
829
827
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
830
828
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
829
|
+
let smoothedTickerPrice = null;
|
|
830
|
+
let tickerPriceTarget = null;
|
|
831
|
+
let smoothingRafId = null;
|
|
832
|
+
const tickerSmoothingLoop = () => {
|
|
833
|
+
smoothingRafId = null;
|
|
834
|
+
if (smoothedTickerPrice === null || tickerPriceTarget === null) return;
|
|
835
|
+
const diff = tickerPriceTarget - smoothedTickerPrice;
|
|
836
|
+
if (Math.abs(diff) < 1e-9) {
|
|
837
|
+
smoothedTickerPrice = tickerPriceTarget;
|
|
838
|
+
draw();
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
842
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 8, 1, 60);
|
|
843
|
+
const dt = 1 / 60;
|
|
844
|
+
const lerp = 1 - Math.exp(-speed * dt);
|
|
845
|
+
smoothedTickerPrice += diff * lerp;
|
|
846
|
+
draw();
|
|
847
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
848
|
+
};
|
|
849
|
+
const pushSmoothedPrice = (target) => {
|
|
850
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
851
|
+
if (!tickerOpts.smoothing) {
|
|
852
|
+
smoothedTickerPrice = null;
|
|
853
|
+
tickerPriceTarget = null;
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
tickerPriceTarget = target;
|
|
857
|
+
if (smoothedTickerPrice === null) {
|
|
858
|
+
smoothedTickerPrice = target;
|
|
859
|
+
}
|
|
860
|
+
if (smoothingRafId === null) {
|
|
861
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
831
864
|
const canvas = document.createElement("canvas");
|
|
832
865
|
const ctx = canvas.getContext("2d");
|
|
833
866
|
if (!ctx) {
|
|
@@ -1788,28 +1821,37 @@ function createChart(element, options = {}) {
|
|
|
1788
1821
|
}
|
|
1789
1822
|
ctx.restore();
|
|
1790
1823
|
}
|
|
1824
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1825
|
+
const useSmoothedCandle = tickerOpts.smoothing && smoothedTickerPrice !== null;
|
|
1826
|
+
const lastDataIndex = data.length - 1;
|
|
1791
1827
|
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
1792
1828
|
const point = data[index];
|
|
1793
1829
|
if (!point) {
|
|
1794
1830
|
continue;
|
|
1795
1831
|
}
|
|
1832
|
+
const isLastCandle = useSmoothedCandle && index === lastDataIndex;
|
|
1833
|
+
const displayClose = isLastCandle ? smoothedTickerPrice : point.c;
|
|
1834
|
+
const displayHigh = isLastCandle ? Math.max(point.h, smoothedTickerPrice) : point.h;
|
|
1835
|
+
const displayLow = isLastCandle ? Math.min(point.l, smoothedTickerPrice) : point.l;
|
|
1796
1836
|
const centerX = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
1797
1837
|
const openY = yFromPrice(point.o);
|
|
1798
|
-
const closeY = yFromPrice(
|
|
1799
|
-
const highY = yFromPrice(
|
|
1800
|
-
const lowY = yFromPrice(
|
|
1801
|
-
const direction = getCandleDirectionByIndex(index);
|
|
1838
|
+
const closeY = yFromPrice(displayClose);
|
|
1839
|
+
const highY = yFromPrice(displayHigh);
|
|
1840
|
+
const lowY = yFromPrice(displayLow);
|
|
1841
|
+
const direction = isLastCandle ? displayClose >= point.o ? "up" : "down" : getCandleDirectionByIndex(index);
|
|
1802
1842
|
const candleColor = direction === "up" ? mergedOptions.upColor : mergedOptions.downColor;
|
|
1843
|
+
const roundedCenterX = Math.round(centerX);
|
|
1803
1844
|
ctx.strokeStyle = candleColor;
|
|
1804
1845
|
ctx.lineWidth = candleWickWidth;
|
|
1805
1846
|
ctx.beginPath();
|
|
1806
|
-
ctx.moveTo(
|
|
1807
|
-
ctx.lineTo(
|
|
1847
|
+
ctx.moveTo(roundedCenterX + 0.5, crisp(highY));
|
|
1848
|
+
ctx.lineTo(roundedCenterX + 0.5, crisp(lowY));
|
|
1808
1849
|
ctx.stroke();
|
|
1850
|
+
const bodyLeft = roundedCenterX - Math.floor(bodyWidth / 2);
|
|
1809
1851
|
const bodyTop = Math.min(openY, closeY);
|
|
1810
1852
|
const bodyHeight = Math.max(1, Math.abs(closeY - openY));
|
|
1811
1853
|
ctx.fillStyle = candleColor;
|
|
1812
|
-
ctx.fillRect(
|
|
1854
|
+
ctx.fillRect(bodyLeft, Math.round(bodyTop), bodyWidth, Math.max(1, Math.round(bodyHeight)));
|
|
1813
1855
|
}
|
|
1814
1856
|
const activeOverlayIndicators = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
1815
1857
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
@@ -1943,10 +1985,10 @@ function createChart(element, options = {}) {
|
|
|
1943
1985
|
const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1944
1986
|
const lastPoint = data[data.length - 1];
|
|
1945
1987
|
if ((ticker.visible ?? true) && lastPoint) {
|
|
1946
|
-
const tickerPrice = lastPoint.c;
|
|
1988
|
+
const tickerPrice = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice : lastPoint.c;
|
|
1947
1989
|
const tickerY = yFromPrice(tickerPrice);
|
|
1948
1990
|
const lineY = clamp(tickerY, chartTop + 1, chartBottom - 1);
|
|
1949
|
-
const lastDirection = getCandleDirectionByIndex(data.length - 1);
|
|
1991
|
+
const lastDirection = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice >= lastPoint.o ? "up" : "down" : getCandleDirectionByIndex(data.length - 1);
|
|
1950
1992
|
const tickerColor = ticker.color ?? (lastDirection === "up" ? mergedOptions.upColor : mergedOptions.downColor);
|
|
1951
1993
|
const tickerThickness = Math.max(1, ticker.thickness ?? 1);
|
|
1952
1994
|
const tickerStyle = ticker.style ?? "solid";
|
|
@@ -2778,6 +2820,10 @@ function createChart(element, options = {}) {
|
|
|
2778
2820
|
fitXViewport();
|
|
2779
2821
|
}
|
|
2780
2822
|
}
|
|
2823
|
+
const lastClose = data.length > 0 ? data[data.length - 1].c : null;
|
|
2824
|
+
if (lastClose !== null) {
|
|
2825
|
+
pushSmoothedPrice(lastClose);
|
|
2826
|
+
}
|
|
2781
2827
|
draw();
|
|
2782
2828
|
};
|
|
2783
2829
|
const setPriceLines = (lines) => {
|
|
@@ -2928,6 +2974,10 @@ function createChart(element, options = {}) {
|
|
|
2928
2974
|
draw();
|
|
2929
2975
|
};
|
|
2930
2976
|
const destroy = () => {
|
|
2977
|
+
if (smoothingRafId !== null) {
|
|
2978
|
+
cancelAnimationFrame(smoothingRafId);
|
|
2979
|
+
smoothingRafId = null;
|
|
2980
|
+
}
|
|
2931
2981
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
2932
2982
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
2933
2983
|
canvas.removeEventListener("pointerup", endPointerDrag);
|
package/dist/index.cjs
CHANGED
|
@@ -171,8 +171,6 @@ var DEFAULT_OPTIONS = {
|
|
|
171
171
|
visible: true,
|
|
172
172
|
style: "dotted",
|
|
173
173
|
thickness: 1,
|
|
174
|
-
color: "#38bdf8",
|
|
175
|
-
labelBackgroundColor: "#38bdf8",
|
|
176
174
|
labelTextColor: "#0b1220",
|
|
177
175
|
labelBorderRadius: 3
|
|
178
176
|
},
|
|
@@ -852,6 +850,41 @@ function createChart(element, options = {}) {
|
|
|
852
850
|
let crosshairPoint = null;
|
|
853
851
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
854
852
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
853
|
+
let smoothedTickerPrice = null;
|
|
854
|
+
let tickerPriceTarget = null;
|
|
855
|
+
let smoothingRafId = null;
|
|
856
|
+
const tickerSmoothingLoop = () => {
|
|
857
|
+
smoothingRafId = null;
|
|
858
|
+
if (smoothedTickerPrice === null || tickerPriceTarget === null) return;
|
|
859
|
+
const diff = tickerPriceTarget - smoothedTickerPrice;
|
|
860
|
+
if (Math.abs(diff) < 1e-9) {
|
|
861
|
+
smoothedTickerPrice = tickerPriceTarget;
|
|
862
|
+
draw();
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
866
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 8, 1, 60);
|
|
867
|
+
const dt = 1 / 60;
|
|
868
|
+
const lerp = 1 - Math.exp(-speed * dt);
|
|
869
|
+
smoothedTickerPrice += diff * lerp;
|
|
870
|
+
draw();
|
|
871
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
872
|
+
};
|
|
873
|
+
const pushSmoothedPrice = (target) => {
|
|
874
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
875
|
+
if (!tickerOpts.smoothing) {
|
|
876
|
+
smoothedTickerPrice = null;
|
|
877
|
+
tickerPriceTarget = null;
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
tickerPriceTarget = target;
|
|
881
|
+
if (smoothedTickerPrice === null) {
|
|
882
|
+
smoothedTickerPrice = target;
|
|
883
|
+
}
|
|
884
|
+
if (smoothingRafId === null) {
|
|
885
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
886
|
+
}
|
|
887
|
+
};
|
|
855
888
|
const canvas = document.createElement("canvas");
|
|
856
889
|
const ctx = canvas.getContext("2d");
|
|
857
890
|
if (!ctx) {
|
|
@@ -1812,28 +1845,37 @@ function createChart(element, options = {}) {
|
|
|
1812
1845
|
}
|
|
1813
1846
|
ctx.restore();
|
|
1814
1847
|
}
|
|
1848
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1849
|
+
const useSmoothedCandle = tickerOpts.smoothing && smoothedTickerPrice !== null;
|
|
1850
|
+
const lastDataIndex = data.length - 1;
|
|
1815
1851
|
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
1816
1852
|
const point = data[index];
|
|
1817
1853
|
if (!point) {
|
|
1818
1854
|
continue;
|
|
1819
1855
|
}
|
|
1856
|
+
const isLastCandle = useSmoothedCandle && index === lastDataIndex;
|
|
1857
|
+
const displayClose = isLastCandle ? smoothedTickerPrice : point.c;
|
|
1858
|
+
const displayHigh = isLastCandle ? Math.max(point.h, smoothedTickerPrice) : point.h;
|
|
1859
|
+
const displayLow = isLastCandle ? Math.min(point.l, smoothedTickerPrice) : point.l;
|
|
1820
1860
|
const centerX = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
1821
1861
|
const openY = yFromPrice(point.o);
|
|
1822
|
-
const closeY = yFromPrice(
|
|
1823
|
-
const highY = yFromPrice(
|
|
1824
|
-
const lowY = yFromPrice(
|
|
1825
|
-
const direction = getCandleDirectionByIndex(index);
|
|
1862
|
+
const closeY = yFromPrice(displayClose);
|
|
1863
|
+
const highY = yFromPrice(displayHigh);
|
|
1864
|
+
const lowY = yFromPrice(displayLow);
|
|
1865
|
+
const direction = isLastCandle ? displayClose >= point.o ? "up" : "down" : getCandleDirectionByIndex(index);
|
|
1826
1866
|
const candleColor = direction === "up" ? mergedOptions.upColor : mergedOptions.downColor;
|
|
1867
|
+
const roundedCenterX = Math.round(centerX);
|
|
1827
1868
|
ctx.strokeStyle = candleColor;
|
|
1828
1869
|
ctx.lineWidth = candleWickWidth;
|
|
1829
1870
|
ctx.beginPath();
|
|
1830
|
-
ctx.moveTo(
|
|
1831
|
-
ctx.lineTo(
|
|
1871
|
+
ctx.moveTo(roundedCenterX + 0.5, crisp(highY));
|
|
1872
|
+
ctx.lineTo(roundedCenterX + 0.5, crisp(lowY));
|
|
1832
1873
|
ctx.stroke();
|
|
1874
|
+
const bodyLeft = roundedCenterX - Math.floor(bodyWidth / 2);
|
|
1833
1875
|
const bodyTop = Math.min(openY, closeY);
|
|
1834
1876
|
const bodyHeight = Math.max(1, Math.abs(closeY - openY));
|
|
1835
1877
|
ctx.fillStyle = candleColor;
|
|
1836
|
-
ctx.fillRect(
|
|
1878
|
+
ctx.fillRect(bodyLeft, Math.round(bodyTop), bodyWidth, Math.max(1, Math.round(bodyHeight)));
|
|
1837
1879
|
}
|
|
1838
1880
|
const activeOverlayIndicators = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
1839
1881
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
@@ -1967,10 +2009,10 @@ function createChart(element, options = {}) {
|
|
|
1967
2009
|
const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1968
2010
|
const lastPoint = data[data.length - 1];
|
|
1969
2011
|
if ((ticker.visible ?? true) && lastPoint) {
|
|
1970
|
-
const tickerPrice = lastPoint.c;
|
|
2012
|
+
const tickerPrice = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice : lastPoint.c;
|
|
1971
2013
|
const tickerY = yFromPrice(tickerPrice);
|
|
1972
2014
|
const lineY = clamp(tickerY, chartTop + 1, chartBottom - 1);
|
|
1973
|
-
const lastDirection = getCandleDirectionByIndex(data.length - 1);
|
|
2015
|
+
const lastDirection = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice >= lastPoint.o ? "up" : "down" : getCandleDirectionByIndex(data.length - 1);
|
|
1974
2016
|
const tickerColor = ticker.color ?? (lastDirection === "up" ? mergedOptions.upColor : mergedOptions.downColor);
|
|
1975
2017
|
const tickerThickness = Math.max(1, ticker.thickness ?? 1);
|
|
1976
2018
|
const tickerStyle = ticker.style ?? "solid";
|
|
@@ -2802,6 +2844,10 @@ function createChart(element, options = {}) {
|
|
|
2802
2844
|
fitXViewport();
|
|
2803
2845
|
}
|
|
2804
2846
|
}
|
|
2847
|
+
const lastClose = data.length > 0 ? data[data.length - 1].c : null;
|
|
2848
|
+
if (lastClose !== null) {
|
|
2849
|
+
pushSmoothedPrice(lastClose);
|
|
2850
|
+
}
|
|
2805
2851
|
draw();
|
|
2806
2852
|
};
|
|
2807
2853
|
const setPriceLines = (lines) => {
|
|
@@ -2952,6 +2998,10 @@ function createChart(element, options = {}) {
|
|
|
2952
2998
|
draw();
|
|
2953
2999
|
};
|
|
2954
3000
|
const destroy = () => {
|
|
3001
|
+
if (smoothingRafId !== null) {
|
|
3002
|
+
cancelAnimationFrame(smoothingRafId);
|
|
3003
|
+
smoothingRafId = null;
|
|
3004
|
+
}
|
|
2955
3005
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
2956
3006
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
2957
3007
|
canvas.removeEventListener("pointerup", endPointerDrag);
|
package/dist/index.d.cts
CHANGED
|
@@ -259,6 +259,8 @@ interface TickerLineOptions {
|
|
|
259
259
|
labelBackgroundColor?: string;
|
|
260
260
|
labelTextColor?: string;
|
|
261
261
|
labelBorderRadius?: number;
|
|
262
|
+
smoothing?: boolean;
|
|
263
|
+
smoothingSpeed?: number;
|
|
262
264
|
}
|
|
263
265
|
interface ChartInstance {
|
|
264
266
|
setData: (data: OhlcDataPoint[]) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -259,6 +259,8 @@ interface TickerLineOptions {
|
|
|
259
259
|
labelBackgroundColor?: string;
|
|
260
260
|
labelTextColor?: string;
|
|
261
261
|
labelBorderRadius?: number;
|
|
262
|
+
smoothing?: boolean;
|
|
263
|
+
smoothingSpeed?: number;
|
|
262
264
|
}
|
|
263
265
|
interface ChartInstance {
|
|
264
266
|
setData: (data: OhlcDataPoint[]) => void;
|
package/dist/index.js
CHANGED
|
@@ -147,8 +147,6 @@ var DEFAULT_OPTIONS = {
|
|
|
147
147
|
visible: true,
|
|
148
148
|
style: "dotted",
|
|
149
149
|
thickness: 1,
|
|
150
|
-
color: "#38bdf8",
|
|
151
|
-
labelBackgroundColor: "#38bdf8",
|
|
152
150
|
labelTextColor: "#0b1220",
|
|
153
151
|
labelBorderRadius: 3
|
|
154
152
|
},
|
|
@@ -828,6 +826,41 @@ function createChart(element, options = {}) {
|
|
|
828
826
|
let crosshairPoint = null;
|
|
829
827
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
830
828
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
829
|
+
let smoothedTickerPrice = null;
|
|
830
|
+
let tickerPriceTarget = null;
|
|
831
|
+
let smoothingRafId = null;
|
|
832
|
+
const tickerSmoothingLoop = () => {
|
|
833
|
+
smoothingRafId = null;
|
|
834
|
+
if (smoothedTickerPrice === null || tickerPriceTarget === null) return;
|
|
835
|
+
const diff = tickerPriceTarget - smoothedTickerPrice;
|
|
836
|
+
if (Math.abs(diff) < 1e-9) {
|
|
837
|
+
smoothedTickerPrice = tickerPriceTarget;
|
|
838
|
+
draw();
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
842
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 8, 1, 60);
|
|
843
|
+
const dt = 1 / 60;
|
|
844
|
+
const lerp = 1 - Math.exp(-speed * dt);
|
|
845
|
+
smoothedTickerPrice += diff * lerp;
|
|
846
|
+
draw();
|
|
847
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
848
|
+
};
|
|
849
|
+
const pushSmoothedPrice = (target) => {
|
|
850
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
851
|
+
if (!tickerOpts.smoothing) {
|
|
852
|
+
smoothedTickerPrice = null;
|
|
853
|
+
tickerPriceTarget = null;
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
tickerPriceTarget = target;
|
|
857
|
+
if (smoothedTickerPrice === null) {
|
|
858
|
+
smoothedTickerPrice = target;
|
|
859
|
+
}
|
|
860
|
+
if (smoothingRafId === null) {
|
|
861
|
+
smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
831
864
|
const canvas = document.createElement("canvas");
|
|
832
865
|
const ctx = canvas.getContext("2d");
|
|
833
866
|
if (!ctx) {
|
|
@@ -1788,28 +1821,37 @@ function createChart(element, options = {}) {
|
|
|
1788
1821
|
}
|
|
1789
1822
|
ctx.restore();
|
|
1790
1823
|
}
|
|
1824
|
+
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1825
|
+
const useSmoothedCandle = tickerOpts.smoothing && smoothedTickerPrice !== null;
|
|
1826
|
+
const lastDataIndex = data.length - 1;
|
|
1791
1827
|
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
1792
1828
|
const point = data[index];
|
|
1793
1829
|
if (!point) {
|
|
1794
1830
|
continue;
|
|
1795
1831
|
}
|
|
1832
|
+
const isLastCandle = useSmoothedCandle && index === lastDataIndex;
|
|
1833
|
+
const displayClose = isLastCandle ? smoothedTickerPrice : point.c;
|
|
1834
|
+
const displayHigh = isLastCandle ? Math.max(point.h, smoothedTickerPrice) : point.h;
|
|
1835
|
+
const displayLow = isLastCandle ? Math.min(point.l, smoothedTickerPrice) : point.l;
|
|
1796
1836
|
const centerX = chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
1797
1837
|
const openY = yFromPrice(point.o);
|
|
1798
|
-
const closeY = yFromPrice(
|
|
1799
|
-
const highY = yFromPrice(
|
|
1800
|
-
const lowY = yFromPrice(
|
|
1801
|
-
const direction = getCandleDirectionByIndex(index);
|
|
1838
|
+
const closeY = yFromPrice(displayClose);
|
|
1839
|
+
const highY = yFromPrice(displayHigh);
|
|
1840
|
+
const lowY = yFromPrice(displayLow);
|
|
1841
|
+
const direction = isLastCandle ? displayClose >= point.o ? "up" : "down" : getCandleDirectionByIndex(index);
|
|
1802
1842
|
const candleColor = direction === "up" ? mergedOptions.upColor : mergedOptions.downColor;
|
|
1843
|
+
const roundedCenterX = Math.round(centerX);
|
|
1803
1844
|
ctx.strokeStyle = candleColor;
|
|
1804
1845
|
ctx.lineWidth = candleWickWidth;
|
|
1805
1846
|
ctx.beginPath();
|
|
1806
|
-
ctx.moveTo(
|
|
1807
|
-
ctx.lineTo(
|
|
1847
|
+
ctx.moveTo(roundedCenterX + 0.5, crisp(highY));
|
|
1848
|
+
ctx.lineTo(roundedCenterX + 0.5, crisp(lowY));
|
|
1808
1849
|
ctx.stroke();
|
|
1850
|
+
const bodyLeft = roundedCenterX - Math.floor(bodyWidth / 2);
|
|
1809
1851
|
const bodyTop = Math.min(openY, closeY);
|
|
1810
1852
|
const bodyHeight = Math.max(1, Math.abs(closeY - openY));
|
|
1811
1853
|
ctx.fillStyle = candleColor;
|
|
1812
|
-
ctx.fillRect(
|
|
1854
|
+
ctx.fillRect(bodyLeft, Math.round(bodyTop), bodyWidth, Math.max(1, Math.round(bodyHeight)));
|
|
1813
1855
|
}
|
|
1814
1856
|
const activeOverlayIndicators = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
1815
1857
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
@@ -1943,10 +1985,10 @@ function createChart(element, options = {}) {
|
|
|
1943
1985
|
const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1944
1986
|
const lastPoint = data[data.length - 1];
|
|
1945
1987
|
if ((ticker.visible ?? true) && lastPoint) {
|
|
1946
|
-
const tickerPrice = lastPoint.c;
|
|
1988
|
+
const tickerPrice = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice : lastPoint.c;
|
|
1947
1989
|
const tickerY = yFromPrice(tickerPrice);
|
|
1948
1990
|
const lineY = clamp(tickerY, chartTop + 1, chartBottom - 1);
|
|
1949
|
-
const lastDirection = getCandleDirectionByIndex(data.length - 1);
|
|
1991
|
+
const lastDirection = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice >= lastPoint.o ? "up" : "down" : getCandleDirectionByIndex(data.length - 1);
|
|
1950
1992
|
const tickerColor = ticker.color ?? (lastDirection === "up" ? mergedOptions.upColor : mergedOptions.downColor);
|
|
1951
1993
|
const tickerThickness = Math.max(1, ticker.thickness ?? 1);
|
|
1952
1994
|
const tickerStyle = ticker.style ?? "solid";
|
|
@@ -2778,6 +2820,10 @@ function createChart(element, options = {}) {
|
|
|
2778
2820
|
fitXViewport();
|
|
2779
2821
|
}
|
|
2780
2822
|
}
|
|
2823
|
+
const lastClose = data.length > 0 ? data[data.length - 1].c : null;
|
|
2824
|
+
if (lastClose !== null) {
|
|
2825
|
+
pushSmoothedPrice(lastClose);
|
|
2826
|
+
}
|
|
2781
2827
|
draw();
|
|
2782
2828
|
};
|
|
2783
2829
|
const setPriceLines = (lines) => {
|
|
@@ -2928,6 +2974,10 @@ function createChart(element, options = {}) {
|
|
|
2928
2974
|
draw();
|
|
2929
2975
|
};
|
|
2930
2976
|
const destroy = () => {
|
|
2977
|
+
if (smoothingRafId !== null) {
|
|
2978
|
+
cancelAnimationFrame(smoothingRafId);
|
|
2979
|
+
smoothingRafId = null;
|
|
2980
|
+
}
|
|
2931
2981
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
2932
2982
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
2933
2983
|
canvas.removeEventListener("pointerup", endPointerDrag);
|