From 14cbbc300d40f1f13e1eaff7a5624afd2c9ec4d4 Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 14 Oct 2026 17:34:43 +0800 Subject: [PATCH] Refresh perp mark from public ticker when position metrics are unavailable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sim/no-keys mode was falling back to entry so 标记 stayed frozen at open price. Co-authored-by: Cursor --- lib/hedge_plan/hedge_plan_book_lib.py | 30 +++++++++++++++++++ .../hedge_plan_live_positions_lib.py | 5 +++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/hedge_plan/hedge_plan_book_lib.py b/lib/hedge_plan/hedge_plan_book_lib.py index 9fe09bb..fb32a20 100644 --- a/lib/hedge_plan/hedge_plan_book_lib.py +++ b/lib/hedge_plan/hedge_plan_book_lib.py @@ -105,6 +105,36 @@ def perp_close_book_side(position_side: str) -> str: return "bids" if str(position_side or "").lower() == "long" else "asks" +def fetch_perp_mark_px(exchange: Any, symbol: str) -> Optional[float]: + """公开行情标记价:优先 markPx,其次 last / 买卖中间价.模拟盘无持仓 metrics 时用此刷新标记.""" + sym = str(symbol or "").strip() + if not sym or exchange is None or not hasattr(exchange, "fetch_ticker"): + return None + try: + t = exchange.fetch_ticker(sym) or {} + info = t.get("info") if isinstance(t.get("info"), dict) else {} + for v in ( + info.get("markPx"), + t.get("mark"), + t.get("last"), + info.get("last"), + ): + px = _sf(v) + if px is not None and px > 0: + return float(px) + bid = _sf(t.get("bid") or info.get("bidPx")) + ask = _sf(t.get("ask") or info.get("askPx")) + if bid and ask and bid > 0 and ask > 0: + return float((bid + ask) / 2.0) + if bid and bid > 0: + return float(bid) + if ask and ask > 0: + return float(ask) + except Exception: + return None + return None + + def fetch_perp_close_vwap( exchange: Any, *, diff --git a/lib/hedge_plan/hedge_plan_live_positions_lib.py b/lib/hedge_plan/hedge_plan_live_positions_lib.py index fb28ae9..fbd85c6 100644 --- a/lib/hedge_plan/hedge_plan_live_positions_lib.py +++ b/lib/hedge_plan/hedge_plan_live_positions_lib.py @@ -4,7 +4,7 @@ from __future__ import annotations from datetime import datetime, timedelta, timezone from typing import Any, Optional -from lib.hedge_plan.hedge_plan_book_lib import fetch_perp_close_vwap +from lib.hedge_plan.hedge_plan_book_lib import fetch_perp_close_vwap, fetch_perp_mark_px from lib.hedge_plan.hedge_plan_option_primary_lib import ( estimate_combo_net_pnl, fee_rate, @@ -205,6 +205,9 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any margin = _sf(m.get("initial_margin") or m.get("margin")) except Exception: pass + # 模拟盘/无私钥时持仓 metrics 为空,标记会卡在开仓价 → 改拉公开 ticker + if perp_mark is None or perp_mark <= 0: + perp_mark = fetch_perp_mark_px(ex_perp, sym) if perp_mark is None or perp_mark <= 0: perp_mark = perp_entry if live_contracts and live_contracts > 0: