diff --git a/manual_trading_hub/hub_ai/context.py b/manual_trading_hub/hub_ai/context.py index c4aa790..efd2005 100644 --- a/manual_trading_hub/hub_ai/context.py +++ b/manual_trading_hub/hub_ai/context.py @@ -1057,7 +1057,7 @@ def resolve_position_monitor_source(pos: dict, hub_mon: Optional[dict]) -> str: def _options_source_label(p: dict) -> str: - """看板期权来源:仅对冲标期期/永期;纯期权或对不上监控显示 —.""" + """看板期权来源:期期/永期对冲,其余为纯期权.""" source = str(p.get("source") or "").strip() label = str(p.get("source_label") or "").strip() if source == "perp_options" or label == "永期对冲": @@ -1067,7 +1067,9 @@ def _options_source_label(p: dict) -> str: hedge = p.get("hedge_plan_target") if isinstance(p.get("hedge_plan_target"), dict) else None if hedge: return _hedge_source_label(hedge) - return "—" + if label == "纯期权" or source in ("", "option"): + return "纯期权" + return label or "纯期权" def _options_target_monitor_text(p: dict) -> str: @@ -1115,19 +1117,27 @@ def format_dashboard_account_detail(ac: dict) -> dict[str, Any]: row = dict(p) row["source_label"] = _options_source_label(p) row["target_monitor_text"] = _options_target_monitor_text(p) + try: + from lib.options.options_positions_lib import net_pnl_from_display_row + + net = net_pnl_from_display_row(row) + except Exception: + net = None + row["net_pnl"] = round(float(net), 4) if net is not None else None options_positions.append(row) inst = row.get("inst_id") or "?" opt_type = (row.get("opt_type") or "").upper() label = "Call" if opt_type == "C" else "Put" if opt_type == "P" else opt_type or "OPT" - upl = row.get("upl") line: dict[str, Any] = { "kind": "options", - "source": row.get("source_label") or "—", + "source": row.get("source_label") or "纯期权", "text": f"期权 {inst} {label}", } - if upl is not None: + if row.get("net_pnl") is not None: + line["pnl"] = row["net_pnl"] + elif row.get("upl") is not None: try: - line["pnl"] = round(float(upl), 4) + line["pnl"] = round(float(row["upl"]), 4) except (TypeError, ValueError): pass position_lines.append(line) diff --git a/manual_trading_hub/static/dashboard.js b/manual_trading_hub/static/dashboard.js index f5614f3..a69bbef 100644 --- a/manual_trading_hub/static/dashboard.js +++ b/manual_trading_hub/static/dashboard.js @@ -147,6 +147,7 @@ function sourceBadgeClass(source) { const s = String(source || ""); if (s.indexOf("对冲") >= 0) return "is-hedge"; + if (s.indexOf("纯期权") >= 0 || s === "期权") return "is-opt"; if (s.indexOf("顺势") >= 0) return "is-roll"; if (s.indexOf("趋势") >= 0) return "is-trend"; if (s.indexOf("关键位") >= 0) return "is-key"; @@ -187,6 +188,20 @@ `; } + function optionsNetPnl(p) { + if (!p || typeof p !== "object") return null; + if (p.net_pnl != null && Number.isFinite(Number(p.net_pnl))) return Number(p.net_pnl); + const preview = p.close_preview || {}; + if (preview.estimated_pnl != null && Number.isFinite(Number(preview.estimated_pnl))) { + return Number(preview.estimated_pnl); + } + if (preview.total_received != null && p.premium_paid != null) { + const n = Number(preview.total_received) - Number(p.premium_paid); + return Number.isFinite(n) ? n : null; + } + return null; + } + function renderDashboardOptionsTable(positions) { const pos = Array.isArray(positions) ? positions : []; if (!pos.length) return ""; @@ -198,9 +213,10 @@ : (p.opt_type || "").toUpperCase() === "P" ? "Put" : p.opt_type || "—"; - const source = String(p.source_label || p.source || "—"); + const source = String(p.source_label || p.source || "纯期权"); const target = String(p.target_monitor_text || "—"); const targetCls = target && target !== "—" ? "dash-target-monitor is-on" : "dash-target-monitor"; + const net = optionsNetPnl(p); return `