Fix hub market chart live K-line updates without manual reload.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
const CHART_WATCH_HEARTBEAT_MS = 25000;
|
const CHART_WATCH_HEARTBEAT_MS = 25000;
|
||||||
const CHART_SSE_FALLBACK_MS = 60000;
|
const CHART_SSE_FALLBACK_MS = 15000;
|
||||||
const DEFAULT_VISIBLE_BARS = 200;
|
const DEFAULT_VISIBLE_BARS = 200;
|
||||||
const CHART_LOAD_LEFT_THRESHOLD = 25;
|
const CHART_LOAD_LEFT_THRESHOLD = 25;
|
||||||
const CHART_INITIAL_LIMITS = {
|
const CHART_INITIAL_LIMITS = {
|
||||||
@@ -2312,7 +2312,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function viewKey(exKey, sym, tf) {
|
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) {
|
function chartInitialLimit(tf) {
|
||||||
@@ -2773,10 +2787,10 @@
|
|||||||
|
|
||||||
function currentViewSeriesKey() {
|
function currentViewSeriesKey() {
|
||||||
const exKey = (elExchange && elExchange.value) || "";
|
const exKey = (elExchange && elExchange.value) || "";
|
||||||
const sym = (elSymbol && elSymbol.value.trim().toUpperCase()) || "";
|
const sym = (elSymbol && elSymbol.value.trim()) || "";
|
||||||
const tf = (elTf && elTf.value) || "1d";
|
const tf = (elTf && elTf.value) || "1d";
|
||||||
if (!exKey || !sym) return "";
|
if (!exKey || !sym) return "";
|
||||||
return exKey + "|" + sym + "|" + tf;
|
return viewKey(exKey, sym, tf);
|
||||||
}
|
}
|
||||||
|
|
||||||
function postChartWatch() {
|
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() {
|
function connectChartStream() {
|
||||||
closeChartStream();
|
closeChartStream();
|
||||||
const page = document.getElementById("page-market");
|
const page = document.getElementById("page-market");
|
||||||
@@ -2819,34 +2872,7 @@
|
|||||||
chartEventSource = new EventSource("/api/chart/stream");
|
chartEventSource = new EventSource("/api/chart/stream");
|
||||||
chartEventSource.addEventListener("chart", function (ev) {
|
chartEventSource.addEventListener("chart", function (ev) {
|
||||||
try {
|
try {
|
||||||
const st = JSON.parse(ev.data || "{}");
|
handleChartStreamEvent(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;
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
chartEventSource.onerror = function () {
|
chartEventSource.onerror = function () {
|
||||||
@@ -2876,11 +2902,17 @@
|
|||||||
|
|
||||||
function startAutoRefresh() {
|
function startAutoRefresh() {
|
||||||
stopAutoRefresh();
|
stopAutoRefresh();
|
||||||
refreshTimer = setInterval(function () {
|
const tick = function () {
|
||||||
const page = document.getElementById("page-market");
|
const page = document.getElementById("page-market");
|
||||||
if (!page || page.classList.contains("hidden")) return;
|
if (!page || page.classList.contains("hidden")) return;
|
||||||
refreshChartTail();
|
if (lastCandles.length) {
|
||||||
}, CHART_SSE_FALLBACK_MS);
|
void refreshChartTail();
|
||||||
|
} else if (!chartDataLoading) {
|
||||||
|
void loadChart(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
refreshTimer = setInterval(tick, CHART_SSE_FALLBACK_MS);
|
||||||
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopAutoRefresh() {
|
function stopAutoRefresh() {
|
||||||
@@ -3189,6 +3221,9 @@
|
|||||||
elSymbol.addEventListener("keydown", function (e) {
|
elSymbol.addEventListener("keydown", function (e) {
|
||||||
if (e.key === "Enter") loadChart(false);
|
if (e.key === "Enter") loadChart(false);
|
||||||
});
|
});
|
||||||
|
elSymbol.addEventListener("change", function () {
|
||||||
|
loadChart(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const btnLoad = document.getElementById("market-load");
|
const btnLoad = document.getElementById("market-load");
|
||||||
if (btnLoad) {
|
if (btnLoad) {
|
||||||
|
|||||||
@@ -1046,7 +1046,7 @@
|
|||||||
<div id="toast"></div>
|
<div id="toast"></div>
|
||||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||||
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
|
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
|
||||||
<script src="/assets/chart.js?v=20260609-prev-day-lines"></script>
|
<script src="/assets/chart.js?v=20260614-market-live"></script>
|
||||||
<script src="/assets/plan.js?v=20260614-plan-refresh"></script>
|
<script src="/assets/plan.js?v=20260614-plan-refresh"></script>
|
||||||
<script src="/assets/calculator.js?v=3"></script>
|
<script src="/assets/calculator.js?v=3"></script>
|
||||||
<script src="/assets/archive.js?v=20260612-archive-ai-chat"></script>
|
<script src="/assets/archive.js?v=20260612-archive-ai-chat"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user