Fix hub market chart live K-line updates without manual reload.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 14:48:01 +08:00
parent 6ffae02d30
commit 61d79c4de1
2 changed files with 71 additions and 36 deletions
+70 -35
View File
@@ -3,7 +3,7 @@
*/
(function () {
const CHART_WATCH_HEARTBEAT_MS = 25000;
const CHART_SSE_FALLBACK_MS = 60000;
const CHART_SSE_FALLBACK_MS = 15000;
const DEFAULT_VISIBLE_BARS = 200;
const CHART_LOAD_LEFT_THRESHOLD = 25;
const CHART_INITIAL_LIMITS = {
@@ -2312,7 +2312,21 @@
}
function viewKey(exKey, sym, tf) {
return (exKey || "") + "|" + (sym || "") + "|" + (tf || "");
const ex = String(exKey || "").trim().toLowerCase();
const s = normalizeMarketSymbol(sym);
const t = String(tf || "").trim();
return ex + "|" + s + "|" + t;
}
function lookupSeriesMapEntry(map, vKey) {
if (!map || !vKey) return null;
if (map[vKey]) return map[vKey];
const parts = String(vKey).split("|");
if (parts.length === 3) {
const norm = viewKey(parts[0], parts[1], parts[2]);
if (norm !== vKey && map[norm]) return map[norm];
}
return null;
}
function chartInitialLimit(tf) {
@@ -2773,10 +2787,10 @@
function currentViewSeriesKey() {
const exKey = (elExchange && elExchange.value) || "";
const sym = (elSymbol && elSymbol.value.trim().toUpperCase()) || "";
const sym = (elSymbol && elSymbol.value.trim()) || "";
const tf = (elTf && elTf.value) || "1d";
if (!exKey || !sym) return "";
return exKey + "|" + sym + "|" + tf;
return viewKey(exKey, sym, tf);
}
function postChartWatch() {
@@ -2812,6 +2826,45 @@
}
}
function handleChartStreamEvent(st) {
if (!st || st.polling) return;
const vKey = currentViewSeriesKey();
if (!vKey) return;
const tails = st.tails || {};
const series = st.series || {};
const tailPack = lookupSeriesMapEntry(tails, vKey);
if (tailPack && tailPack.candles && tailPack.candles.length) {
if (
applyIncomingTailCandles(tailPack.candles, {
price_tick: tailPack.price_tick,
series_version: tailPack.series_version,
chart_version: st.chart_version,
updated_at: tailPack.updated_at || st.updated_at,
})
) {
return;
}
}
const seriesEntry = lookupSeriesMapEntry(series, vKey);
const sVer = seriesEntry ? Number(seriesEntry.series_version) || 0 : 0;
const seriesChanged = sVer > 0 && sVer !== localSeriesVersion;
if (seriesChanged) {
if (lastCandles.length && vKey === lastViewKey) {
void refreshChartTail();
} else if (!lastCandles.length && !chartDataLoading) {
void loadChart(false);
}
return;
}
if (tailPack && lastCandles.length && vKey === lastViewKey && !chartDataLoading) {
void refreshChartTail();
return;
}
if (posContext) updateLivePosPnl();
const ver = Number(st.chart_version) || 0;
if (ver && ver !== localChartVersion) localChartVersion = ver;
}
function connectChartStream() {
closeChartStream();
const page = document.getElementById("page-market");
@@ -2819,34 +2872,7 @@
chartEventSource = new EventSource("/api/chart/stream");
chartEventSource.addEventListener("chart", function (ev) {
try {
const st = JSON.parse(ev.data || "{}");
if (st.polling) return;
const ver = Number(st.chart_version) || 0;
const series = st.series || {};
const vKey = currentViewSeriesKey();
const tails = st.tails || {};
const tailPack = vKey && tails[vKey] ? tails[vKey] : null;
if (tailPack && tailPack.candles && tailPack.candles.length) {
if (
applyIncomingTailCandles(tailPack.candles, {
price_tick: tailPack.price_tick,
series_version: tailPack.series_version,
chart_version: ver,
updated_at: tailPack.updated_at || st.updated_at,
})
) {
return;
}
}
const sVer = vKey && series[vKey] ? Number(series[vKey].series_version) || 0 : 0;
const seriesChanged = vKey && sVer > 0 && sVer !== localSeriesVersion;
if (seriesChanged) {
refreshChartTail();
} else if (posContext) {
updateLivePosPnl();
} else if (ver !== localChartVersion) {
localChartVersion = ver;
}
handleChartStreamEvent(JSON.parse(ev.data || "{}"));
} catch (_) {}
});
chartEventSource.onerror = function () {
@@ -2876,11 +2902,17 @@
function startAutoRefresh() {
stopAutoRefresh();
refreshTimer = setInterval(function () {
const tick = function () {
const page = document.getElementById("page-market");
if (!page || page.classList.contains("hidden")) return;
refreshChartTail();
}, CHART_SSE_FALLBACK_MS);
if (lastCandles.length) {
void refreshChartTail();
} else if (!chartDataLoading) {
void loadChart(false);
}
};
refreshTimer = setInterval(tick, CHART_SSE_FALLBACK_MS);
tick();
}
function stopAutoRefresh() {
@@ -3189,6 +3221,9 @@
elSymbol.addEventListener("keydown", function (e) {
if (e.key === "Enter") loadChart(false);
});
elSymbol.addEventListener("change", function () {
loadChart(false);
});
}
const btnLoad = document.getElementById("market-load");
if (btnLoad) {