Use exchange fees, UPL, funding for LIVE PnL; USDC 1:1 to USDT.

Option open P&L stays local (option net); exit target ignores estimated close fees.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-26 21:49:20 +08:00
parent b7e055e37b
commit ca66c494e7
12 changed files with 685 additions and 25 deletions
+25 -10
View File
@@ -22,15 +22,23 @@ async def list_groups(_user: Annotated[str, Depends(require_user)]) -> dict:
groups = []
for r in rows:
g = _row(r)
if g.get("status") == "closed":
fills = db.fetchall(
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC",
(g["group_id"],),
)
summary = summarize_fills_pnl(fills)
g["pnl_summary"] = summary
if summary.get("net_pnl") is not None:
g["net_pnl"] = summary["net_pnl"]
fills = db.fetchall(
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC",
(g["group_id"],),
)
summary = summarize_fills_pnl(fills)
# LIVE:优先 groups.realized_pnl(已按交易所回写,含资金费)
if str(g.get("exec_mode") or "").upper() == "LIVE" and g.get("realized_pnl") is not None:
summary = dict(summary)
summary["net_pnl"] = float(g["realized_pnl"])
if g.get("funding_usdt") is not None:
summary["funding_usdt"] = float(g["funding_usdt"])
summary["pnl_source"] = "live_exchange"
g["pnl_summary"] = summary
if summary.get("net_pnl") is not None:
g["net_pnl"] = summary["net_pnl"]
elif g.get("realized_pnl") is not None:
g["net_pnl"] = float(g["realized_pnl"])
groups.append(g)
return {"groups": groups}
@@ -47,8 +55,15 @@ async def group_detail(
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC", (group_id,)
)
summary = summarize_fills_pnl(fills)
gr = _row(g)
if str(gr.get("exec_mode") or "").upper() == "LIVE" and gr.get("realized_pnl") is not None:
summary = dict(summary)
summary["net_pnl"] = float(gr["realized_pnl"])
if gr.get("funding_usdt") is not None:
summary["funding_usdt"] = float(gr["funding_usdt"])
summary["pnl_source"] = "live_exchange"
return {
"group": _row(g),
"group": gr,
"fills": [_row(x) for x in fills],
"pnl_summary": summary,
}