fix: show header realtime PnL from exchange positions and order metrics
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,25 +4,91 @@ from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from lib.hub.hub_monitor_totals_lib import position_unrealized_pnl
|
||||
from lib.hub.hub_position_metrics import parse_position_unrealized_pnl
|
||||
|
||||
|
||||
def _position_contracts(pos: dict[str, Any]) -> float:
|
||||
try:
|
||||
return abs(float(pos.get("contracts") or 0))
|
||||
except (TypeError, ValueError):
|
||||
def position_row_contracts(pos: dict[str, Any]) -> float:
|
||||
"""持仓张数:与三所 app 内 _position_row_effective_contracts 规则一致。"""
|
||||
if not isinstance(pos, dict):
|
||||
return 0.0
|
||||
info = pos.get("info") or {}
|
||||
if not isinstance(info, dict):
|
||||
info = {}
|
||||
for val in (
|
||||
pos.get("contracts"),
|
||||
info.get("positionAmt"),
|
||||
info.get("size"),
|
||||
info.get("pos"),
|
||||
info.get("availPos"),
|
||||
):
|
||||
if val is None or val == "":
|
||||
continue
|
||||
try:
|
||||
x = abs(float(val))
|
||||
if x > 0:
|
||||
return x
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return 0.0
|
||||
|
||||
|
||||
def sum_unrealized_pnl_from_positions(positions: list[dict[str, Any]] | None) -> float:
|
||||
def sum_unrealized_pnl_from_positions(positions: list[dict[str, Any]] | None) -> float | None:
|
||||
total = 0.0
|
||||
found = False
|
||||
for p in positions or []:
|
||||
if not isinstance(p, dict):
|
||||
continue
|
||||
if _position_contracts(p) <= 1e-12:
|
||||
if position_row_contracts(p) <= 1e-12:
|
||||
continue
|
||||
total += position_unrealized_pnl(p)
|
||||
return round(total, 2)
|
||||
upnl = parse_position_unrealized_pnl(p)
|
||||
if upnl is None:
|
||||
continue
|
||||
found = True
|
||||
total += float(upnl)
|
||||
return round(total, 2) if found else None
|
||||
|
||||
|
||||
def _row_field(row: Any, key: str, default: str = "") -> str:
|
||||
if row is None:
|
||||
return default
|
||||
try:
|
||||
if hasattr(row, "keys") and key in row.keys():
|
||||
val = row[key]
|
||||
elif isinstance(row, dict):
|
||||
val = row.get(key)
|
||||
else:
|
||||
val = None
|
||||
except Exception:
|
||||
val = None
|
||||
return str(val or default).strip()
|
||||
|
||||
|
||||
def sum_unrealized_pnl_from_metrics(
|
||||
rows: list[dict[str, Any]] | list[Any],
|
||||
get_metrics_fn: Callable[[str, str], dict[str, Any] | None],
|
||||
) -> float | None:
|
||||
"""按活跃监控单逐笔拉交易所 metrics 汇总(与持仓卡浮盈亏一致)。"""
|
||||
total = 0.0
|
||||
found = False
|
||||
for row in rows or []:
|
||||
ex_sym = _row_field(row, "exchange_symbol")
|
||||
sym = _row_field(row, "symbol")
|
||||
direction = _row_field(row, "direction", "long").lower() or "long"
|
||||
target = ex_sym or sym
|
||||
if not target:
|
||||
continue
|
||||
metrics = get_metrics_fn(target, direction)
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
upnl = metrics.get("unrealized_pnl")
|
||||
if upnl is None:
|
||||
continue
|
||||
try:
|
||||
total += float(upnl)
|
||||
found = True
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return round(total, 2) if found else None
|
||||
|
||||
|
||||
def fetch_unrealized_pnl(fetch_positions_fn: Callable[[], list[dict[str, Any]] | None]) -> float | None:
|
||||
@@ -30,3 +96,17 @@ def fetch_unrealized_pnl(fetch_positions_fn: Callable[[], list[dict[str, Any]] |
|
||||
return sum_unrealized_pnl_from_positions(fetch_positions_fn() or [])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def resolve_instance_unrealized_pnl(
|
||||
fetch_positions_fn: Callable[[], list[dict[str, Any]] | None],
|
||||
active_rows: list[Any] | None,
|
||||
get_metrics_fn: Callable[[str, str], dict[str, Any] | None] | None,
|
||||
) -> float | None:
|
||||
"""先全量持仓汇总,失败或无数据时回退到活跃监控单 metrics。"""
|
||||
total = fetch_unrealized_pnl(fetch_positions_fn)
|
||||
if total is not None:
|
||||
return total
|
||||
if active_rows and get_metrics_fn:
|
||||
return sum_unrealized_pnl_from_metrics(active_rows, get_metrics_fn)
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user