Patch tail candles without resetting chart viewport.
When auto-follow is off, refresh only updates the latest bars via series.update instead of setData so zoom and pan stay fixed during background polls. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1868,6 +1868,16 @@
|
||||
});
|
||||
}
|
||||
|
||||
function buildVolumeBar(candle) {
|
||||
const p = chartThemePalette();
|
||||
const up = Number(candle.close) >= Number(candle.open);
|
||||
return {
|
||||
time: candle.time,
|
||||
value: Number(candle.volume) || 0,
|
||||
color: up ? p.volUp : p.volDown,
|
||||
};
|
||||
}
|
||||
|
||||
function ensureChart() {
|
||||
if (chart && candleSeries && volumeSeries) return true;
|
||||
if (!window.LightweightCharts) {
|
||||
@@ -2141,6 +2151,45 @@
|
||||
return merged;
|
||||
}
|
||||
|
||||
/** 尾部静默刷新:仅 update 变更 K 线,不 setData,避免视口跳动 */
|
||||
function applyTailCandlePatch(incoming) {
|
||||
if (!candleSeries || !volumeSeries || !incoming || !incoming.length) return false;
|
||||
const aligned = alignCandlesToTick(incoming);
|
||||
const prevLen = lastCandles.length;
|
||||
const oldestTime = prevLen ? lastCandles[0].time : null;
|
||||
const merged = mergeCandles(lastCandles, aligned, { prepend: false });
|
||||
if (
|
||||
prevLen > 0 &&
|
||||
merged.length > 0 &&
|
||||
merged[0].time !== oldestTime &&
|
||||
merged.length <= prevLen
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
aligned.forEach(function (bar) {
|
||||
candleSeries.update(bar);
|
||||
volumeSeries.update(buildVolumeBar(bar));
|
||||
});
|
||||
if (merged.length > prevLen) {
|
||||
for (let i = prevLen; i < merged.length; i++) {
|
||||
const bar = merged[i];
|
||||
candleSeries.update(bar);
|
||||
volumeSeries.update(buildVolumeBar(bar));
|
||||
}
|
||||
}
|
||||
lastCandles = merged;
|
||||
indexCandles(lastCandles);
|
||||
readIndicatorState();
|
||||
if (indicatorState.ema || indicatorState.macd || indicatorState.rsi) {
|
||||
try {
|
||||
updateIndicators();
|
||||
} catch (indErr) {}
|
||||
}
|
||||
updateVisibleRangeMarkers();
|
||||
showLatestOhlcv();
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyCandlesToChart(candles, rangeShift, opts) {
|
||||
opts = opts || {};
|
||||
let savedRange = null;
|
||||
@@ -2151,7 +2200,9 @@
|
||||
indexCandles(lastCandles);
|
||||
candleSeries.setData(lastCandles);
|
||||
volumeSeries.setData(buildVolumeData(lastCandles));
|
||||
applyChartRightGap();
|
||||
if (!opts.skipRightGap) {
|
||||
applyChartRightGap();
|
||||
}
|
||||
if (rangeShift && chart) {
|
||||
const range = chart.timeScale().getVisibleLogicalRange();
|
||||
if (range) {
|
||||
@@ -2270,21 +2321,25 @@
|
||||
applyChartPriceFormat();
|
||||
}
|
||||
}
|
||||
applyCandlesToChart(
|
||||
mergeCandles(lastCandles, alignCandlesToTick(data.candles), { prepend: false }),
|
||||
0,
|
||||
{
|
||||
const incoming = alignCandlesToTick(data.candles);
|
||||
if (!autoFollow && applyTailCandlePatch(incoming)) {
|
||||
/* 手动模式:增量更新,不触碰时间轴 */
|
||||
} else {
|
||||
const merged = mergeCandles(lastCandles, incoming, { prepend: false });
|
||||
applyCandlesToChart(merged, 0, {
|
||||
preserveRange: false,
|
||||
skipAutoScale: !autoFollow,
|
||||
skipRightGap: !autoFollow,
|
||||
});
|
||||
if (epochAtStart !== chartViewEpoch) return;
|
||||
const n = lastCandles.length;
|
||||
if (autoFollow) {
|
||||
applyDefaultVisibleRange();
|
||||
} else if (savedRange) {
|
||||
applyPreservedVisibleRange(savedRange, n);
|
||||
}
|
||||
);
|
||||
if (epochAtStart !== chartViewEpoch) return;
|
||||
const n = lastCandles.length;
|
||||
if (autoFollow) {
|
||||
applyDefaultVisibleRange();
|
||||
} else if (savedRange) {
|
||||
applyPreservedVisibleRange(savedRange, n);
|
||||
}
|
||||
if (epochAtStart !== chartViewEpoch) return;
|
||||
scheduleRangeUiUpdate();
|
||||
if (posContext) {
|
||||
updateLivePosPnl();
|
||||
|
||||
@@ -349,7 +349,7 @@
|
||||
|
||||
<div id="toast"></div>
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/assets/chart.js?v=20260608-auto-viewport"></script>
|
||||
<script src="/assets/chart.js?v=20260608-tail-patch"></script>
|
||||
<script src="/assets/archive.js?v=20260607-hub-archive-v6"></script>
|
||||
<script src="/assets/ai_review_render.js?v=2"></script>
|
||||
<script src="/assets/app.js?v=20260607-hub-archive-v1"></script>
|
||||
|
||||
Reference in New Issue
Block a user