From bb6e9e9041ab404fe15dded93faf32e511adb0c5 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 2 Oct 2026 16:13:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E6=8E=A7=E5=BF=AB=E7=85=A7=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=BC=80=E4=BB=93=E6=9D=83=E5=88=A9=E9=87=91=E6=8A=98?= =?UTF-8?q?U=E5=8F=8A=E5=8D=A0=E8=B4=A6=E6=88=B7=E8=B5=84=E9=87=91?= =?UTF-8?q?=E6=AF=94=E4=BE=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- lib/hub/hub_bridge_register.py | 53 +++++++++++++++++++++++++--- lib/options/options_positions_lib.py | 24 ++++++++++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/lib/hub/hub_bridge_register.py b/lib/hub/hub_bridge_register.py index 3f0457e..fa27315 100644 --- a/lib/hub/hub_bridge_register.py +++ b/lib/hub/hub_bridge_register.py @@ -51,6 +51,8 @@ def _enrich_hub_protocol(snap: dict[str, Any], *, app_module: Any) -> dict[str, positions = out.get("positions") if isinstance(out.get("positions"), list) else [] slim_pos = [] + premium_total_usdt = 0.0 + has_premium = False for p in positions: if not isinstance(p, dict): continue @@ -86,12 +88,26 @@ def _enrich_hub_protocol(snap: dict[str, Any], *, app_module: Any) -> dict[str, pnl_usdt = display_pnl_usdt_from_option_row(p) except Exception: pnl_usdt = p.get("net_pnl_usdt") + premium_paid = p.get("premium_paid") + premium_usdt = None + try: + from lib.options.options_positions_lib import premium_paid_usdt_from_option_row + + premium_usdt = premium_paid_usdt_from_option_row(p) + except Exception: + premium_usdt = None + if premium_usdt is not None: + has_premium = True + premium_total_usdt += float(premium_usdt) slim_pos.append( { "inst_id": p.get("inst_id"), "opt_type": p.get("opt_type"), "sheets": p.get("sheets") or p.get("pos"), "pnl_usdt": pnl_usdt, + "premium_paid": premium_paid, + "premium_paid_usdt": round(float(premium_usdt), 4) if premium_usdt is not None else None, + "premium_ccy": p.get("premium_ccy"), "mark": p.get("mark") or p.get("mark_px"), "underlying": p.get("underlying"), "source_label": p.get("source_label"), @@ -113,15 +129,44 @@ def _enrich_hub_protocol(snap: dict[str, Any], *, app_module: Any) -> dict[str, "margin_mode": out.get("options_margin_mode") or "coin", "underly": out.get("options_underly") or "ETH", } + funding_usdt = out.get("funding_usdt") + trading_usdt = out.get("trading_usdt") + funding_usdc = out.get("funding_usdc") + trading_usdc = out.get("trading_usdc") out["funds"] = { - "funding_usdt": out.get("funding_usdt"), - "trading_usdt": out.get("trading_usdt"), - "funding_usdc": out.get("funding_usdc"), - "trading_usdc": out.get("trading_usdc"), + "funding_usdt": funding_usdt, + "trading_usdt": trading_usdt, + "funding_usdc": funding_usdc, + "trading_usdc": trading_usdc, } + + def _f(v: Any) -> float | None: + if v is None or v == "": + return None + try: + return float(v) + except (TypeError, ValueError): + return None + + # 账户资金口径:优先 USDT 资+交;都空则回退 USDC(≈U) + equity_parts = [_f(trading_usdt), _f(funding_usdt)] + if all(x is None for x in equity_parts): + equity_parts = [_f(trading_usdc), _f(funding_usdc)] + known = [x for x in equity_parts if x is not None] + equity_usdt = round(sum(known), 4) if known else None + premium_usdt = round(premium_total_usdt, 4) if has_premium else (0.0 if not slim_pos else None) + ratio_pct = None + if premium_usdt is not None and equity_usdt is not None and equity_usdt > 1e-9: + ratio_pct = round(float(premium_usdt) / float(equity_usdt) * 100.0, 2) + out["pnl"] = { "unrealized_usdt": out.get("upl_total_usdc"), } + out["premium"] = { + "paid_usdt": premium_usdt, + "equity_usdt": equity_usdt, + "ratio_pct": ratio_pct, + } out["positions_brief"] = slim_pos try: from lib.options.options_time_lib import options_app_now_str diff --git a/lib/options/options_positions_lib.py b/lib/options/options_positions_lib.py index c1f5b01..2602854 100644 --- a/lib/options/options_positions_lib.py +++ b/lib/options/options_positions_lib.py @@ -136,9 +136,26 @@ def display_pnl_usdt_from_option_row(row: dict[str, Any]) -> float | None: pnl = display_pnl_from_option_row(row) if pnl is None: return None + return _coin_amount_to_usdt(pnl, row) + + +def premium_paid_usdt_from_option_row(row: dict[str, Any]) -> float | None: + """开仓权利金折 USDT:币本位 × 指数;USDC 原样.""" + paid = _safe_float(row.get("premium_paid")) + if paid is None: + return None + return _coin_amount_to_usdt(paid, row) + + +def _coin_amount_to_usdt(amount: float, row: dict[str, Any]) -> float | None: + """币本位金额按指数折 U;USDC 原样.""" + try: + n = float(amount) + except (TypeError, ValueError): + return None ccy = str(row.get("premium_ccy") or "USDC").strip().upper() or "USDC" if not ccy or ccy == "USDC": - # 兜底:按合约推断币本位,避免 premium_ccy 缺失时把 BTC 盈亏当 U + # 兜底:按合约推断币本位,避免 premium_ccy 缺失时把 BTC 数量当 U inst = str(row.get("inst_id") or row.get("instId") or "") mode = str(row.get("margin_mode") or "").strip().lower() if mode == "coin" or ( @@ -150,10 +167,9 @@ def display_pnl_usdt_from_option_row(row: dict[str, Any]) -> float | None: if ccy in ("ETH", "BTC"): idx = _safe_float(row.get("idx_px") or row.get("idxPx") or row.get("index_px")) if idx is None or idx <= 0: - # 无指数时无法折算,跳过该仓以免把「币数量」当成 U return None - return float(pnl) * float(idx) - return float(pnl) + return float(n) * float(idx) + return float(n) def sum_options_net_pnl_usdc(