Implement P1 local matcher/ledger and P2 strategy engine.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,91 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { apiFetch } from "../api/client";
|
||||
|
||||
type Group = {
|
||||
group_id: string;
|
||||
status: string;
|
||||
bias: string | null;
|
||||
option_side: string | null;
|
||||
perp_side: string | null;
|
||||
initial_premium: number;
|
||||
realized_pnl: number;
|
||||
close_reason: string | null;
|
||||
open_at_ms: number | null;
|
||||
close_at_ms: number | null;
|
||||
};
|
||||
|
||||
type Fill = {
|
||||
id: number;
|
||||
leg: string;
|
||||
action: string;
|
||||
side: string;
|
||||
fill_px: number;
|
||||
fee: number;
|
||||
qty_eth: number;
|
||||
};
|
||||
|
||||
export default function TradesPage() {
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
const [selected, setSelected] = useState<string | null>(null);
|
||||
const [fills, setFills] = useState<Fill[]>([]);
|
||||
const [err, setErr] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
apiFetch<{ groups: Group[] }>("/api/trades/groups")
|
||||
.then((r) => setGroups(r.groups))
|
||||
.catch((e) => setErr(e instanceof Error ? e.message : String(e)));
|
||||
}, []);
|
||||
|
||||
async function openGroup(id: string) {
|
||||
setSelected(id);
|
||||
try {
|
||||
const r = await apiFetch<{ fills: Fill[] }>(`/api/trades/groups/${id}`);
|
||||
setFills(r.fills);
|
||||
} catch (e) {
|
||||
setErr(e instanceof Error ? e.message : String(e));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div>
|
||||
<h2 style={{ marginTop: 0 }}>交易记录</h2>
|
||||
<p style={{ color: "var(--muted)" }}>按组成交明细将在 P1/P2 接入。</p>
|
||||
{err ? <div className="err">{err}</div> : null}
|
||||
<div className="card" style={{ marginBottom: 12 }}>
|
||||
{groups.length === 0 ? (
|
||||
<p style={{ color: "var(--muted)" }}>暂无成交组</p>
|
||||
) : (
|
||||
groups.map((g) => (
|
||||
<div
|
||||
key={g.group_id}
|
||||
className="kv"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => openGroup(g.group_id)}
|
||||
>
|
||||
<span className="mono">
|
||||
{g.group_id} · {g.status} · {g.perp_side}/{g.option_side}
|
||||
</span>
|
||||
<span className="mono">
|
||||
PnL {Number(g.realized_pnl || 0).toFixed(2)} · {g.close_reason || "—"}
|
||||
</span>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
{selected ? (
|
||||
<div className="card">
|
||||
<h3 style={{ marginTop: 0 }}>{selected} 成交明细</h3>
|
||||
{fills.map((f) => (
|
||||
<div key={f.id} className="kv">
|
||||
<span className="mono">
|
||||
{f.leg} {f.action} {f.side}
|
||||
</span>
|
||||
<span className="mono">
|
||||
px {f.fill_px.toFixed(4)} · qty {f.qty_eth} · fee {f.fee.toFixed(4)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user