From 09abb8d35b53e62654acf9539188b8dd46872ea0 Mon Sep 17 00:00:00 2001 From: dekun Date: Mon, 3 Aug 2026 07:43:27 +0800 Subject: [PATCH] Show residual bid as price/size and recovery percent from bid premium. Co-authored-by: Cursor --- backend/app/sim/matcher.py | 15 +++++++++- backend/tests/test_residual_premium_close.py | 6 +++- control/frontend/src/pages/Monitor.tsx | 31 ++++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/backend/app/sim/matcher.py b/backend/app/sim/matcher.py index dcc75d1..15ba1a4 100644 --- a/backend/app/sim/matcher.py +++ b/backend/app/sim/matcher.py @@ -909,7 +909,7 @@ class Matcher: return (None, close_bid, oq) def list_residual_options_enriched(self) -> list[dict[str, Any]]: - """pending 残留 + 买一权利金/回收占比/流动性是否可手动平。""" + """pending 残留 + 买一价/量、买一权利金、回收占比、流动性是否可手动平。""" out: list[dict[str, Any]] = [] for row in self.list_residual_options(pending_only=True): d = dict(row) @@ -918,6 +918,17 @@ class Matcher: init = float(d.get("initial_premium") or 0) oq = self._quote_held_option(option_inst_id) if option_inst_id else None bid = float(oq.bid) if oq is not None and oq.bid is not None else None + bid_sz = None + bid_sz_eth = None + if oq is not None and getattr(oq, "bid_sz", None) is not None: + try: + bid_sz = float(oq.bid_sz) + except (TypeError, ValueError): + bid_sz = None + if bid_sz is not None and bid_sz >= 0: + ct = self._ct_mult(option_inst_id) if option_inst_id else 0.01 + bid_sz_eth = eth_from_contracts(bid_sz, ct) + # 权利金 = 最新买一价 × 持仓数量;占比 = 买一权利金 / 开仓权利金 cur = float(bid) * opt_qty if bid is not None else None ratio = (cur / init * 100.0) if cur is not None and init > 1e-12 else None liq_detail: str | None @@ -930,6 +941,8 @@ class Matcher: d.update( { "bid_px": bid, + "bid_sz": bid_sz, + "bid_sz_eth": bid_sz_eth, "current_premium": cur, "recovery_pct": ratio, "liquidity_ok": liq_detail is None, diff --git a/backend/tests/test_residual_premium_close.py b/backend/tests/test_residual_premium_close.py index fabccc1..3516b4e 100644 --- a/backend/tests/test_residual_premium_close.py +++ b/backend/tests/test_residual_premium_close.py @@ -206,7 +206,11 @@ def test_manual_close_skips_premium_ratio(tmp_path, monkeypatch) -> None: enriched = m.list_residual_options_enriched() assert len(enriched) == 1 assert enriched[0]["liquidity_ok"] is True - assert enriched[0]["recovery_pct"] is not None + assert enriched[0]["bid_px"] == 5.0 + assert enriched[0]["bid_sz"] == 10_000.0 + assert abs(float(enriched[0]["bid_sz_eth"]) - 100.0) < 1e-9 # 10000*0.01 + assert abs(float(enriched[0]["current_premium"]) - 10.0) < 1e-9 # 5*2 + assert abs(float(enriched[0]["recovery_pct"]) - 10.0) < 1e-9 # 10/100*100 assert float(enriched[0]["recovery_pct"]) < 20.0 r = m.close_residual_manual("G-res") diff --git a/control/frontend/src/pages/Monitor.tsx b/control/frontend/src/pages/Monitor.tsx index ada3837..e4db27f 100644 --- a/control/frontend/src/pages/Monitor.tsx +++ b/control/frontend/src/pages/Monitor.tsx @@ -85,11 +85,22 @@ type ResidualRow = { option_qty_eth: number | null; initial_premium: number | null; bid_px: number | null; + bid_sz_eth: number | null; current_premium: number | null; recovery_pct: number | null; liquidity_ok: boolean; }; +function fmtBidLiquidity( + bidPx: number | null, + bidSzEth: number | null, +): string { + if (bidPx == null && bidSzEth == null) return "—"; + const px = bidPx == null ? "—" : fmtExPx("option", bidPx); + const sz = bidSzEth == null ? "—" : fmt(bidSzEth, 2); + return `${px} / ${sz}`; +} + function collectResiduals(nodes: NodeCard[]): ResidualRow[] { const out: ResidualRow[] = []; for (const n of nodes) { @@ -115,6 +126,7 @@ function collectResiduals(nodes: NodeCard[]): ResidualRow[] { option_qty_eth: numOrNull(r.option_qty_eth), initial_premium: numOrNull(r.initial_premium), bid_px: numOrNull(r.bid_px), + bid_sz_eth: numOrNull(r.bid_sz_eth), current_premium: numOrNull(r.current_premium), recovery_pct: numOrNull(r.recovery_pct), liquidity_ok: r.liquidity_ok === true, @@ -838,8 +850,9 @@ export default function MonitorPage() { 期权名 数量 开仓权利金 - 当前买一权利金 - 回收占比% + 买一流动性 + 买一权利金 + 占比 操作 @@ -851,9 +864,21 @@ export default function MonitorPage() { {r.option_inst_id} {fmt(r.option_qty_eth, 4)} {fmt(r.initial_premium, 2)} + + {fmtBidLiquidity(r.bid_px, r.bid_sz_eth)} + {fmt(r.current_premium, 2)} - {r.recovery_pct == null ? "—" : fmt(r.recovery_pct, 1)} + {r.recovery_pct == null + ? "—" + : `${fmt(r.recovery_pct, 1)}%`}