diff --git a/manual_trading_hub/static/chart.js b/manual_trading_hub/static/chart.js index a563eb2..1230d0a 100644 --- a/manual_trading_hub/static/chart.js +++ b/manual_trading_hub/static/chart.js @@ -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) { diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index d1203ef..8f317c8 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -1046,7 +1046,7 @@
- +