Fix instance dashboard order PnL columns showing empty dashes.

Compute float_pnl and tp_profit from mark/entry/contracts using each exchange contract size during dashboard enrich.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-06 14:12:57 +08:00
parent 993189ce13
commit 75a4175522
5 changed files with 104 additions and 17 deletions
+61 -14
View File
@@ -358,34 +358,81 @@ def collect_options_items(
return out
def _resolve_contract_size(
sym: str,
*,
get_contract_size: Optional[Callable[[str], Any]] = None,
) -> float:
if not callable(get_contract_size) or not sym:
return 1.0
try:
cs = float(get_contract_size(sym) or 1.0)
return cs if cs > 0 else 1.0
except Exception:
return 1.0
def _fill_order_pnl_fields(row: dict[str, Any], *, mark: Optional[float], contract_size: float) -> None:
"""按线性 U 本位补看板「盈利金额 / 浮盈」."""
direction = str(row.get("direction") or "long").lower()
entry = _safe_float(row.get("entry"))
contracts = _safe_float(row.get("contracts"))
tp = _safe_float(row.get("take_profit"))
if entry is None or contracts is None or contracts <= 0:
return
cs = float(contract_size) if contract_size and contract_size > 0 else 1.0
if mark is not None:
try:
from lib.hub.hub_position_metrics import estimate_linear_swap_upnl_usdt
upnl = estimate_linear_swap_upnl_usdt(direction, entry, mark, contracts, cs)
if upnl is not None:
row["float_pnl"] = upnl
except Exception:
pass
if tp is not None and tp > 0:
try:
from lib.strategy.strategy_trend_lib import calc_tp_profit_usdt
profit = calc_tp_profit_usdt(direction, entry, tp, contracts, cs)
if profit is not None:
row["tp_profit"] = round(float(profit), 2)
except Exception:
pass
def enrich_order_items_with_marks(
items: list[dict[str, Any]],
*,
get_price: Optional[Callable[[str], Any]] = None,
get_contract_size: Optional[Callable[[str], Any]] = None,
) -> list[dict[str, Any]]:
"""后台聚合时补标记价(不打全量 fetch_positions;浮盈仍由实盘页口径负责)."""
if not items or not callable(get_price):
"""后台聚合时补标记价,并按张数×合约面值估算盈利金额/浮盈."""
if not items:
return items
if not callable(get_price) and not callable(get_contract_size):
return items
out: list[dict[str, Any]] = []
for it in items:
row = dict(it)
sym = str(row.get("price_symbol") or row.get("symbol") or "").strip()
if not sym:
out.append(row)
continue
try:
px = get_price(sym)
except Exception:
px = None
mark = _safe_float(px)
if mark is None and ":" in sym:
mark = _safe_float(row.get("mark_price"))
if callable(get_price) and sym:
try:
px = get_price(sym.split(":", 1)[0])
px = get_price(sym)
except Exception:
px = None
mark = _safe_float(px)
if mark is not None:
row["mark_price"] = mark
if mark is None and ":" in sym:
try:
px = get_price(sym.split(":", 1)[0])
except Exception:
px = None
mark = _safe_float(px)
if mark is not None:
row["mark_price"] = mark
cs = _resolve_contract_size(sym, get_contract_size=get_contract_size)
_fill_order_pnl_fields(row, mark=mark, contract_size=cs)
out.append(row)
return out