fix: align unrealized PnL across four exchange instances via hub_position_metrics
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+53
-7
@@ -119,14 +119,25 @@ def resolve_position_display_upnl(
|
||||
return exchange_upnl
|
||||
|
||||
|
||||
def _coerce_signed(*values: Any) -> float | None:
|
||||
"""解析可正可负的数值(未实现盈亏等)。"""
|
||||
for v in values:
|
||||
if v is None or v == "":
|
||||
continue
|
||||
f = _finite_or_none(v)
|
||||
if f is not None:
|
||||
return f
|
||||
return None
|
||||
|
||||
|
||||
def parse_position_unrealized_pnl(p: dict[str, Any]) -> float | None:
|
||||
"""四所 ccxt 持仓统一解析未实现盈亏(Gate 常在 info.unrealised_pnl)。"""
|
||||
"""四所 ccxt 持仓统一解析未实现盈亏(Gate/OKX/Binance 字段名不一致)。"""
|
||||
if not isinstance(p, dict):
|
||||
return None
|
||||
info = p.get("info") or {}
|
||||
if not isinstance(info, dict):
|
||||
info = {}
|
||||
for key in (
|
||||
return _coerce_signed(
|
||||
p.get("unrealizedPnl"),
|
||||
p.get("unrealisedPnl"),
|
||||
p.get("unrealized_pnl"),
|
||||
@@ -135,11 +146,46 @@ def parse_position_unrealized_pnl(p: dict[str, Any]) -> float | None:
|
||||
info.get("unrealized_pnl"),
|
||||
info.get("unrealisedPnl"),
|
||||
info.get("unrealizedPnl"),
|
||||
):
|
||||
px = _finite_or_none(key)
|
||||
if px is not None:
|
||||
return px
|
||||
return None
|
||||
info.get("upl"),
|
||||
info.get("uplLast"),
|
||||
)
|
||||
|
||||
|
||||
def enrich_ccxt_position_metrics_out(
|
||||
position: dict[str, Any],
|
||||
out: dict[str, Any],
|
||||
*,
|
||||
contract_size: float = 1.0,
|
||||
funds_decimals: int = 2,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
四所 parse_ccxt_position_metrics 产出后统一:
|
||||
- 标记价用 hub 兜底
|
||||
- 未实现盈亏 = resolve(交易所值, entry/mark/张数/contractSize 推算)
|
||||
"""
|
||||
if not isinstance(position, dict) or not isinstance(out, dict):
|
||||
return out
|
||||
mark = _finite_or_none(out.get("mark_price"))
|
||||
if mark is None or mark <= 0:
|
||||
mp = parse_position_mark_price(position)
|
||||
if mp is not None and mp > 0:
|
||||
out["mark_price"] = round(mp, 8)
|
||||
mark = mp
|
||||
exchange_upnl = parse_position_unrealized_pnl(position)
|
||||
if exchange_upnl is None:
|
||||
exchange_upnl = _coerce_signed(out.get("unrealized_pnl"))
|
||||
c = position_contracts(position)
|
||||
if abs(c) < 1e-12:
|
||||
return out
|
||||
side = position_side_from_ccxt(position, c)
|
||||
entry = parse_position_entry_price(position)
|
||||
cs = contract_size if contract_size and contract_size > 0 else 1.0
|
||||
upnl = resolve_position_display_upnl(
|
||||
side, entry, mark, abs(c), cs, exchange_upnl
|
||||
)
|
||||
if upnl is not None:
|
||||
out["unrealized_pnl"] = round(upnl, funds_decimals)
|
||||
return out
|
||||
|
||||
|
||||
def parse_position_mark_price(p: dict[str, Any]) -> float | None:
|
||||
|
||||
Reference in New Issue
Block a user