Split fee stats by leg and hide LIVE slip.
SIM shows perp/option fees and slip separately; LIVE keeps real exchange fees only with slip forced to zero. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,7 +4,9 @@ from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from ..config import get_settings
|
||||
from ..models.db import get_db
|
||||
from ..sim.pnl import summarize_fills_pnl
|
||||
from .auth import require_user
|
||||
|
||||
router = APIRouter(prefix="/api/stats", tags=["stats"])
|
||||
@@ -13,12 +15,30 @@ router = APIRouter(prefix="/api/stats", tags=["stats"])
|
||||
@router.get("/summary")
|
||||
async def stats_summary(_user: Annotated[str, Depends(require_user)]) -> dict:
|
||||
db = get_db()
|
||||
s = get_settings()
|
||||
mode = "LIVE" if not s.is_sim else "SIM"
|
||||
rows = db.fetchall("SELECT * FROM groups WHERE status='closed'")
|
||||
n = len(rows)
|
||||
wins = sum(1 for r in rows if float(r["realized_pnl"] or 0) > 0)
|
||||
total_pnl = sum(float(r["realized_pnl"] or 0) for r in rows)
|
||||
total_fees = sum(float(r["fees"] or 0) for r in rows)
|
||||
total_slip = sum(float(r["slip_cost"] or 0) for r in rows)
|
||||
|
||||
fees_perp = 0.0
|
||||
fees_option = 0.0
|
||||
total_slip = 0.0
|
||||
for r in rows:
|
||||
fills = db.fetchall(
|
||||
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC",
|
||||
(r["group_id"],),
|
||||
)
|
||||
summary = summarize_fills_pnl(list(fills))
|
||||
fees_perp += float(summary.get("fees_perp") or 0)
|
||||
fees_option += float(summary.get("fees_option") or 0)
|
||||
# LIVE 不展示、不计入滑点;按组成交模式判断(可混有历史 SIM 组)
|
||||
exec_mode = str(r["exec_mode"] or mode).upper()
|
||||
if exec_mode != "LIVE":
|
||||
total_slip += float(summary.get("slip_total") or 0)
|
||||
|
||||
total_fees = fees_perp + fees_option
|
||||
reasons: dict[str, int] = {}
|
||||
for r in rows:
|
||||
k = str(r["close_reason"] or "unknown")
|
||||
@@ -32,12 +52,16 @@ async def stats_summary(_user: Annotated[str, Depends(require_user)]) -> dict:
|
||||
for r in sorted(rows, key=lambda x: int(x["close_at_ms"] or 0))
|
||||
]
|
||||
return {
|
||||
"mode": mode,
|
||||
"show_slip": mode == "SIM",
|
||||
"groups": n,
|
||||
"wins": wins,
|
||||
"win_rate": (wins / n) if n else 0.0,
|
||||
"total_pnl": total_pnl,
|
||||
"fees_perp": fees_perp,
|
||||
"fees_option": fees_option,
|
||||
"total_fees": total_fees,
|
||||
"total_slip": total_slip,
|
||||
"total_slip": total_slip if mode == "SIM" else 0.0,
|
||||
"close_reasons": reasons,
|
||||
"equity_curve": curve,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user