fix(web): 小周期切换时对齐标记,避免 LWC Value is null
主周期笔/KLC 分型标记在切到 1m/2m 主图时未对齐 K 线 time;过滤均线无效点并钳制视窗恢复。顺带统一 BI 中枢计算路径。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,77 @@
|
||||
/* chart_tv_overlays.js — structure zones / wyckoff / BSP / FX / bollinger */
|
||||
|
||||
/** 标记 time 必须落在主 series 的 K 线 time 上,否则 LWC 会抛 Value is null */
|
||||
function alignMarkersToCandles(markers, candles) {
|
||||
if (!Array.isArray(markers) || !markers.length) return [];
|
||||
if (!Array.isArray(candles) || !candles.length) return [];
|
||||
var times = [];
|
||||
for (var i = 0; i < candles.length; i++) {
|
||||
var ct = candles[i] && candles[i].time;
|
||||
if (ct == null || !isFinite(Number(ct))) continue;
|
||||
times.push(Math.floor(Number(ct)));
|
||||
}
|
||||
if (!times.length) return [];
|
||||
var set = {};
|
||||
for (var j = 0; j < times.length; j++) set[times[j]] = true;
|
||||
var nearest = function (target) {
|
||||
var best = times[0];
|
||||
var bestDiff = Math.abs(best - target);
|
||||
// 两端夹逼:大数据量时比全扫略好
|
||||
var lo = 0, hi = times.length - 1;
|
||||
while (lo <= hi) {
|
||||
var mid = (lo + hi) >> 1;
|
||||
var t = times[mid];
|
||||
var d = Math.abs(t - target);
|
||||
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||
if (t < target) lo = mid + 1;
|
||||
else hi = mid - 1;
|
||||
}
|
||||
if (lo < times.length) {
|
||||
var d2 = Math.abs(times[lo] - target);
|
||||
if (d2 < bestDiff) best = times[lo];
|
||||
}
|
||||
if (hi >= 0) {
|
||||
var d3 = Math.abs(times[hi] - target);
|
||||
if (d3 < bestDiff) best = times[hi];
|
||||
}
|
||||
return best;
|
||||
};
|
||||
var out = [];
|
||||
for (var k = 0; k < markers.length; k++) {
|
||||
var m = markers[k];
|
||||
if (!m || m.time == null || !isFinite(Number(m.time))) continue;
|
||||
var t0 = Math.floor(Number(m.time));
|
||||
var aligned = set[t0] ? t0 : nearest(t0);
|
||||
var copy = Object.assign({}, m, { time: aligned });
|
||||
out.push(copy);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function safeOverlayLineSetData(series, points) {
|
||||
if (!series || typeof series.setData !== 'function' || !Array.isArray(points) || points.length < 2) return;
|
||||
try {
|
||||
var a = points[0], b = points[1];
|
||||
if (!a || !b || a.time == null || b.time == null) return;
|
||||
var t0 = Math.floor(Number(a.time));
|
||||
var t1 = Math.floor(Number(b.time));
|
||||
var v0 = Number(a.value);
|
||||
var v1 = Number(b.value);
|
||||
if (!isFinite(t0) || !isFinite(t1) || !isFinite(v0) || !isFinite(v1)) return;
|
||||
if (t0 === t1) {
|
||||
// 竖边:同 time 两点 LWC 不接受,跳过(横边仍保留)
|
||||
return;
|
||||
}
|
||||
if (t0 > t1) {
|
||||
series.setData([{ time: t1, value: v1 }, { time: t0, value: v0 }]);
|
||||
} else {
|
||||
series.setData([{ time: t0, value: v0 }, { time: t1, value: v1 }]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('叠层线 setData 跳过:', e && e.message ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
function chartTvRenderOverlays(ctx) {
|
||||
var symbol = ctx.symbol;
|
||||
var timeframe = ctx.timeframe;
|
||||
@@ -1642,7 +1714,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
topSeries.setData([{ time: startTs, value: boxHigh }, { time: endTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(topSeries, [{ time: startTs, value: boxHigh }, { time: endTs, value: boxHigh }]);
|
||||
|
||||
const bottomSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -1652,7 +1724,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
bottomSeries.setData([{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
safeOverlayLineSetData(bottomSeries, [{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
|
||||
const leftSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -1662,8 +1734,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
// 左边竖线:同一 time 上下两个点(和你已有ZS绘制写法保持一致)
|
||||
leftSeries.setData([{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(leftSeries, [{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
|
||||
const rightSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -1673,7 +1744,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
rightSeries.setData([{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(rightSeries, [{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
|
||||
if (!tvWidget.series.mainKlcFxBoxSeries) tvWidget.series.mainKlcFxBoxSeries = [];
|
||||
tvWidget.series.mainKlcFxBoxSeries.push(topSeries, bottomSeries, leftSeries, rightSeries);
|
||||
@@ -1849,7 +1920,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
topSeries.setData([{ time: startTs, value: boxHigh }, { time: endTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(topSeries, [{ time: startTs, value: boxHigh }, { time: endTs, value: boxHigh }]);
|
||||
|
||||
const bottomSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -1859,7 +1930,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
bottomSeries.setData([{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
safeOverlayLineSetData(bottomSeries, [{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
|
||||
const leftSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -1869,7 +1940,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
leftSeries.setData([{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(leftSeries, [{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
|
||||
const rightSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -1879,7 +1950,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
rightSeries.setData([{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(rightSeries, [{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
|
||||
if (!tvWidget.series.elementKlcFxBoxSeries) tvWidget.series.elementKlcFxBoxSeries = [];
|
||||
tvWidget.series.elementKlcFxBoxSeries.push(topSeries, bottomSeries, leftSeries, rightSeries);
|
||||
@@ -2002,7 +2073,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
topSeries.setData([{ time: startTs, value: boxHigh }, { time: endTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(topSeries, [{ time: startTs, value: boxHigh }, { time: endTs, value: boxHigh }]);
|
||||
|
||||
const bottomSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -2012,7 +2083,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
bottomSeries.setData([{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
safeOverlayLineSetData(bottomSeries, [{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
|
||||
const leftSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -2022,7 +2093,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
leftSeries.setData([{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(leftSeries, [{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
|
||||
const rightSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
@@ -2032,7 +2103,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
rightSeries.setData([{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
safeOverlayLineSetData(rightSeries, [{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
|
||||
if (!tvWidget.series.subSubKlcFxBoxSeries) tvWidget.series.subSubKlcFxBoxSeries = [];
|
||||
tvWidget.series.subSubKlcFxBoxSeries.push(topSeries, bottomSeries, leftSeries, rightSeries);
|
||||
@@ -2180,7 +2251,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
else if (klineType === 'klc') targetSeries = tvWidget.series.klcSeries;
|
||||
if (targetSeries) {
|
||||
try {
|
||||
targetSeries.setMarkers(combinedMarkers);
|
||||
targetSeries.setMarkers(alignMarkersToCandles(combinedMarkers, candles));
|
||||
} catch (e) {
|
||||
console.warn('设置主系列标记失败(可能series已释放):', e);
|
||||
}
|
||||
@@ -2309,7 +2380,7 @@ function chartTvRenderOverlays(ctx) {
|
||||
else if (klineType2 === 'klc') targetSeries2 = tvWidget.series.klcSeries;
|
||||
if (targetSeries2) {
|
||||
try {
|
||||
targetSeries2.setMarkers(onlyMainAndU);
|
||||
targetSeries2.setMarkers(alignMarkersToCandles(onlyMainAndU, candles));
|
||||
} catch (e) {
|
||||
console.warn('设置主系列标记失败(可能series已释放):', e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user