import { useEffect, useState } from "react"; import { apiFetch } from "../api/client"; import { closeReasonZh } from "../labels"; type Summary = { mode?: "SIM" | "LIVE" | string; show_slip?: boolean; groups: number; wins: number; win_rate: number; total_pnl: number; fees_perp?: number; fees_option?: number; total_fees: number; total_slip: number; close_reasons: Record; equity_curve: { group_id: string; realized_pnl: number }[]; }; export default function StatsPage() { const [s, setS] = useState(null); const [err, setErr] = useState(""); useEffect(() => { apiFetch("/api/stats/summary") .then(setS) .catch((e) => setErr(e instanceof Error ? e.message : String(e))); }, []); const reasonEntries = s ? Object.entries(s.close_reasons).sort((a, b) => b[1] - a[1]) : []; const showSlip = s?.show_slip ?? s?.mode !== "LIVE"; return (

统计

{err ?
{err}
: null} {!s ? (

加载中…

) : ( <>
组数 / 胜率 {s.groups} / {(s.win_rate * 100).toFixed(1)}%
总盈亏 {s.total_pnl.toFixed(2)}
永续手续费 {(s.fees_perp ?? 0).toFixed(4)}
期权手续费 {(s.fees_option ?? 0).toFixed(4)}
手续费合计 {s.total_fees.toFixed(4)}
{showSlip ? (
滑点合计 {s.total_slip.toFixed(4)}
) : null}
平仓原因 {reasonEntries.length === 0 ? "—" : reasonEntries.map(([k, n]) => (
{closeReasonZh(k)} × {n}
))}

按组盈亏

{s.equity_curve.map((x) => (
{x.group_id} {x.realized_pnl.toFixed(2)}
))} )}
); }