e51f357b48
Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
3.8 KiB
TypeScript
122 lines
3.8 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { apiFetch, MarketSnapshot } from "../api/client";
|
||
|
||
function fmt(n: number | null | undefined, d = 2) {
|
||
if (n == null || Number.isNaN(n)) return "—";
|
||
return n.toFixed(d);
|
||
}
|
||
|
||
export default function PlanPage() {
|
||
const [snap, setSnap] = useState<MarketSnapshot | null>(null);
|
||
const [err, setErr] = useState("");
|
||
|
||
useEffect(() => {
|
||
let alive = true;
|
||
const load = async () => {
|
||
try {
|
||
const data = await apiFetch<MarketSnapshot>("/api/market/snapshot");
|
||
if (alive) {
|
||
setSnap(data);
|
||
setErr("");
|
||
}
|
||
} catch (e) {
|
||
if (alive) setErr(e instanceof Error ? e.message : String(e));
|
||
}
|
||
};
|
||
load();
|
||
const t = window.setInterval(load, 2000);
|
||
return () => {
|
||
alive = false;
|
||
window.clearInterval(t);
|
||
};
|
||
}, []);
|
||
|
||
const bias = snap?.ask_compare?.bias;
|
||
const biasTag =
|
||
bias === "call_ask_gt_put" ? (
|
||
<span className="tag up">Call卖一 > Put卖一 → 永续多+期权空</span>
|
||
) : bias === "put_ask_gt_call" ? (
|
||
<span className="tag down">Put卖一 > Call卖一 → 永续空+期权多</span>
|
||
) : (
|
||
<span className="tag">等待 / 相等</span>
|
||
);
|
||
|
||
return (
|
||
<div>
|
||
<h2 style={{ marginTop: 0 }}>自动对冲计划</h2>
|
||
<p style={{ color: "var(--muted)", marginTop: -8 }}>
|
||
P0 行情只读 · 策略开平仓待拍板后接入
|
||
</p>
|
||
{err ? <div className="err">{err}</div> : null}
|
||
|
||
<div className="card" style={{ marginBottom: 12 }}>
|
||
<div className="kv">
|
||
<span>模式</span>
|
||
<span className="mono">SIM · 测试环境</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>行情连接</span>
|
||
<span className="mono">{snap?.connected ? "WS 已连接" : "REST/未连"}</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>指数</span>
|
||
<span className="mono">{fmt(snap?.index_px)}</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>选约</span>
|
||
<span className="mono">
|
||
{snap?.pair
|
||
? `${snap.pair.expiry_ymd} @ ${snap.pair.strike}`
|
||
: "—"}
|
||
</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>选向</span>
|
||
{biasTag}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid-2">
|
||
<div className="card">
|
||
<h3 style={{ marginTop: 0 }}>永续 ETH-USDT-SWAP</h3>
|
||
<div className="kv">
|
||
<span>买一</span>
|
||
<span className="mono">{fmt(snap?.perp?.bid)} × {fmt(snap?.perp?.bid_sz, 2)}</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>卖一</span>
|
||
<span className="mono">{fmt(snap?.perp?.ask)} × {fmt(snap?.perp?.ask_sz, 2)}</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>标记</span>
|
||
<span className="mono">{fmt(snap?.perp?.mark_px)}</span>
|
||
</div>
|
||
</div>
|
||
<div className="card">
|
||
<h3 style={{ marginTop: 0 }}>期权 ATM</h3>
|
||
<div className="kv">
|
||
<span>Call 卖一</span>
|
||
<span className="mono">{fmt(snap?.call?.ask)} / 买一 {fmt(snap?.call?.bid)}</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>Put 卖一</span>
|
||
<span className="mono">{fmt(snap?.put?.ask)} / 买一 {fmt(snap?.put?.bid)}</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>Call</span>
|
||
<span className="mono" style={{ fontSize: 12 }}>
|
||
{snap?.pair?.call_inst_id || "—"}
|
||
</span>
|
||
</div>
|
||
<div className="kv">
|
||
<span>Put</span>
|
||
<span className="mono" style={{ fontSize: 12 }}>
|
||
{snap?.pair?.put_inst_id || "—"}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|