fix(options): speed up chain refresh with fast path and non-blocking UI

Skip full REST tickers when WS is warm, seed subscriptions off-request, and keep the old chain visible while refreshing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-11 12:06:16 +08:00
parent 14a7adae1f
commit 6fad68f7b1
5 changed files with 153 additions and 19 deletions
+52 -1
View File
@@ -158,7 +158,58 @@ class OptionsQuoteLive:
args = [{"channel": "tickers", "instId": iid} for iid in merged]
for iid in sorted(index_insts):
args.append({"channel": "index-tickers", "instId": iid})
self._ws.set_subscriptions(args)
# 订阅可能分片 sleep,不能堵 Flask 请求线程
threading.Thread(
target=self._ws.set_subscriptions,
args=(args,),
name="okx-options-quote-sub",
daemon=True,
).start()
def as_okx_tickers(self, underlying: str | None = None) -> dict[str, dict[str, Any]]:
"""转成 build_option_chain 可用的 OKX ticker 字段."""
u = (underlying or "").upper()
out: dict[str, dict[str, Any]] = {}
with self._lock:
for inst_id, q in self._tickers.items():
if u and str(q.get("underlying") or "").upper() not in ("", u):
continue
row: dict[str, Any] = {"instId": inst_id}
if q.get("ask") is not None and not q.get("ask_estimated"):
row["askPx"] = q.get("ask")
row["askSz"] = q.get("ask_sz")
if q.get("bid") is not None:
row["bidPx"] = q.get("bid")
row["bidSz"] = q.get("bid_sz")
if q.get("mark_px") is not None:
row["markPx"] = q.get("mark_px")
out[inst_id] = row
return out
def index_px_for(self, underlying: str) -> float | None:
u = (underlying or "").upper()
with self._lock:
return self._index_by_uly.get(u)
def is_ws_fresh(self, *, max_age_sec: float = 15.0) -> bool:
if not self._ws.connected:
return False
last = float(self._ws.last_msg_at or 0)
return last > 0 and (time.time() - last) <= max_age_sec
def schedule_seed_from_chain(
self,
chain: dict[str, Any],
*,
exp_time: str | int | None = None,
watcher_id: str | None = None,
) -> None:
threading.Thread(
target=self.seed_from_chain,
kwargs={"chain": chain, "exp_time": exp_time, "watcher_id": watcher_id},
name="options-quote-seed",
daemon=True,
).start()
def seed_from_chain(
self,