"""从成交明细汇总腿盈亏与净盈亏。""" from __future__ import annotations from typing import Any def _as_map(x: Any) -> dict[str, Any]: if isinstance(x, dict): return x try: return dict(x) except Exception: return {} def summarize_fills_pnl(fills: list[Any]) -> dict[str, float | None]: """ 价差盈亏按 fill_px;手续费另扣。 净盈亏 = 期权盈亏 + 永续盈亏 − 全部手续费(开+平)。 允许只有永续已平、期权尚未结算的半组。 手续费拆:fees_perp / fees_option;滑点合计 slip_total(SIM 记账;LIVE 应为 0)。 """ rows = [_as_map(x) for x in fills] opt_open = next( (f for f in rows if f.get("leg") == "option" and f.get("action") == "open"), None, ) opt_close = next( (f for f in rows if f.get("leg") == "option" and f.get("action") == "close"), None, ) perp_open = next( (f for f in rows if f.get("leg") == "perp" and f.get("action") == "open"), None, ) perp_close = next( (f for f in rows if f.get("leg") == "perp" and f.get("action") == "close"), None, ) option_pnl: float | None = None if opt_open and opt_close: qty = float(opt_open.get("qty_eth") or opt_close.get("qty_eth") or 0) option_pnl = (float(opt_close["fill_px"]) - float(opt_open["fill_px"])) * qty perp_pnl: float | None = None if perp_open and perp_close: qty = float(perp_open.get("qty_eth") or perp_close.get("qty_eth") or 0) side = str(perp_open.get("side") or "") o = float(perp_open["fill_px"]) c = float(perp_close["fill_px"]) if side == "long": perp_pnl = (c - o) * qty else: perp_pnl = (o - c) * qty fees_perp = sum( float(f.get("fee") or 0) for f in rows if str(f.get("leg") or "") == "perp" ) fees_option = sum( float(f.get("fee") or 0) for f in rows if str(f.get("leg") or "") == "option" ) fees_total = fees_perp + fees_option slip_total = sum(float(f.get("slip") or 0) for f in rows) gross = None net = None if option_pnl is not None and perp_pnl is not None: gross = option_pnl + perp_pnl net = gross - fees_total elif option_pnl is not None: gross = option_pnl net = option_pnl - fees_total elif perp_pnl is not None: gross = perp_pnl net = perp_pnl - fees_total return { "option_pnl": option_pnl, "perp_pnl": perp_pnl, "fees_perp": fees_perp, "fees_option": fees_option, "fees_total": fees_total, "slip_total": slip_total, "gross_pnl": gross, "net_pnl": net, }