fix(options): soft-poll chain quotes so list asks stay fresh

SSE only refreshed positions; chain asks were one-shot until manual reload, which could mislead open decisions.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-11 11:21:30 +08:00
parent c514a75026
commit 24bb8532c4
2 changed files with 47 additions and 4 deletions
+46 -3
View File
@@ -37,9 +37,14 @@
let selectSeq = 0; let selectSeq = 0;
let refreshAllTimer = null; let refreshAllTimer = null;
let pendingRefreshTimer = null; let pendingRefreshTimer = null;
let chainSoftTimer = null;
let lastChainSoftAt = 0;
let chainQuotedAt = 0;
let pendingTtlSeconds = 600; let pendingTtlSeconds = 600;
const POSITIONS_STALE_MS = 45000; const POSITIONS_STALE_MS = 45000;
const PENDING_POLL_MS = 8000; const PENDING_POLL_MS = 8000;
/** 链卖一/买一静默刷新节流:无推送,靠拉;过密会撞 OKX 50011 */
const CHAIN_SOFT_POLL_MS = 15000;
const orderPanelHome = (function () { const orderPanelHome = (function () {
const host = document.getElementById("opt-order-panel-host"); const host = document.getElementById("opt-order-panel-host");
return host ? host.parentElement : null; return host ? host.parentElement : null;
@@ -632,6 +637,15 @@
if (el) el.textContent = fmt(buf, 2); if (el) el.textContent = fmt(buf, 2);
} }
function fmtChainQuotedAt() {
if (!chainQuotedAt) return "";
const d = new Date(chainQuotedAt);
const pad = function (n) {
return n < 10 ? "0" + n : String(n);
};
return pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds());
}
function renderIndexLine() { function renderIndexLine() {
const idx = state.chain && state.chain.index_px; const idx = state.chain && state.chain.index_px;
const dte = state.chain && state.chain.chain_max_dte_days; const dte = state.chain && state.chain.chain_max_dte_days;
@@ -645,12 +659,36 @@
const line = document.getElementById("opt-index-line"); const line = document.getElementById("opt-index-line");
if (line) { if (line) {
const liqHint = askLiqFilterOn() ? "仅显示卖一深度≥1张" : "显示全部卖一(含估算~)"; const liqHint = askLiqFilterOn() ? "仅显示卖一深度≥1张" : "显示全部卖一(含估算~)";
const ageHint = chainQuotedAt ? " · 链报价 " + fmtChainQuotedAt() + "(约每15s静默刷新)" : "";
line.textContent = line.textContent =
"指数 " + state.underlying + " ≈ " + fmt(idx, 2) + "指数 " + state.underlying + " ≈ " + fmt(idx, 2) +
" · 默认最近一期 · " + liqHint + " · 实值含平值 · 虚值=价外"; " · 默认最近一期 · " + liqHint + " · 实值含平值 · 虚值=价外" + ageHint;
} }
} }
function softRefreshChainThrottled(force) {
if (document.hidden) return;
if (!document.getElementById("options-root")) return;
const now = Date.now();
if (!force && now - lastChainSoftAt < CHAIN_SOFT_POLL_MS) return;
lastChainSoftAt = now;
void loadChain({ soft: true });
}
function startChainSoftPoll() {
if (chainSoftTimer) return;
chainSoftTimer = setInterval(function () {
if (!document.getElementById("options-root")) {
if (chainSoftTimer) {
clearInterval(chainSoftTimer);
chainSoftTimer = null;
}
return;
}
softRefreshChainThrottled(false);
}, CHAIN_SOFT_POLL_MS);
}
function pickNearestExpiry(exps) { function pickNearestExpiry(exps) {
if (!exps || !exps.length) return ""; if (!exps || !exps.length) return "";
const now = Date.now(); const now = Date.now();
@@ -1269,6 +1307,8 @@
panelCache.chain = d; panelCache.chain = d;
panelCache.underlying = uly; panelCache.underlying = uly;
panelCache.optType = state.optType; panelCache.optType = state.optType;
chainQuotedAt = Date.now();
lastChainSoftAt = chainQuotedAt;
syncAskLiqFilterFromChain(d); syncAskLiqFilterFromChain(d);
if (!soft) { if (!soft) {
state.selectedInst = null; state.selectedInst = null;
@@ -2206,6 +2246,7 @@
updateUnderlyingLabel(); updateUnderlyingLabel();
refreshPendingOrders(); refreshPendingOrders();
startPendingOrdersPoll(); startPendingOrdersPoll();
startChainSoftPoll();
const hasCache = const hasCache =
chainHasExpiries(panelCache.chain) && chainHasExpiries(panelCache.chain) &&
panelCache.underlying === state.underlying && panelCache.underlying === state.underlying &&
@@ -2215,8 +2256,8 @@
renderExpiries(); renderExpiries();
renderStrikes(); renderStrikes();
refreshAllPositions(); refreshAllPositions();
// 后台静默刷新,避免缓存过期后到期日变空 // 后台静默刷新,避免缓存过期后到期日变空 / 卖一过期
loadChain({ soft: true }); softRefreshChainThrottled(true);
return; return;
} }
requestAnimationFrame(function () { requestAnimationFrame(function () {
@@ -2340,6 +2381,8 @@
window.OptionsPanelLive = { window.OptionsPanelLive = {
refreshSoft: function () { refreshSoft: function () {
refreshAllPositions(); refreshAllPositions();
// embed SSE 只通知「该拉了」,不推送链报价;这里节流拉新鲜卖一/买一
softRefreshChainThrottled(false);
}, },
refreshChain: loadChain, refreshChain: loadChain,
}; };
+1 -1
View File
@@ -322,4 +322,4 @@
</div> </div>
</div> </div>
<script src="/static/options_expiry_countdown.js?v=1"></script> <script src="/static/options_expiry_countdown.js?v=1"></script>
<script src="/static/options_panel.js?v=56"></script> <script src="/static/options_panel.js?v=57"></script>