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