feat: add history and about pages
This commit is contained in:
parent
05c9428fad
commit
8bff7b19bd
81
frontend/app/about/page.tsx
Normal file
81
frontend/app/about/page.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6 max-w-3xl">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold text-slate-100">策略说明</h1>
|
||||||
|
<p className="text-slate-400 text-sm mt-1">资金费率套利原理与历史数据</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6 space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold text-cyan-400">策略原理</h2>
|
||||||
|
<p className="text-slate-300 text-sm leading-relaxed">
|
||||||
|
永续合约每8小时结算一次资金费率。多头多时,多头付钱给空头(费率为正);空头多时,空头付钱给多头(费率为负)。
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-300 text-sm leading-relaxed">
|
||||||
|
<span className="text-cyan-400 font-medium">套利做法:</span>现货买入 + 永续做空,完全对冲币价风险,净收资金费率(USDT结算)。
|
||||||
|
</p>
|
||||||
|
<div className="bg-slate-900/50 rounded-lg p-4 text-sm font-mono text-slate-300 space-y-1">
|
||||||
|
<div>买入 1 BTC 现货($96,000)</div>
|
||||||
|
<div>做空 1 BTC 永续($96,000,1倍杠杆)</div>
|
||||||
|
<div className="text-slate-500">───────────────────────</div>
|
||||||
|
<div className="text-emerald-400">BTC涨跌:两边对冲,净盈亏 = 0</div>
|
||||||
|
<div className="text-emerald-400">资金费率:每8小时直接收 USDT ✓</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6 space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold text-cyan-400">历史年化数据(2019-2026,露露×小周15轮验证)</h2>
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-slate-700">
|
||||||
|
<th className="text-left py-2 text-slate-400">资产</th>
|
||||||
|
<th className="text-right py-2 text-slate-400">全周期毛年化</th>
|
||||||
|
<th className="text-right py-2 text-slate-400">PM净年化</th>
|
||||||
|
<th className="text-right py-2 text-slate-400">负费率占比</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-slate-300">
|
||||||
|
<tr className="border-b border-slate-700/50">
|
||||||
|
<td className="py-2 text-cyan-400 font-medium">BTC</td>
|
||||||
|
<td className="py-2 text-right">12.33%</td>
|
||||||
|
<td className="py-2 text-right text-emerald-400 font-medium">11.67%</td>
|
||||||
|
<td className="py-2 text-right">13.07%</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-slate-700/50">
|
||||||
|
<td className="py-2 text-violet-400 font-medium">ETH</td>
|
||||||
|
<td className="py-2 text-right">14.87%</td>
|
||||||
|
<td className="py-2 text-right text-emerald-400 font-medium">14.09%</td>
|
||||||
|
<td className="py-2 text-right">12.17%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="py-2 text-emerald-400 font-medium">50/50组合</td>
|
||||||
|
<td className="py-2 text-right">13.81%</td>
|
||||||
|
<td className="py-2 text-right text-emerald-400 font-bold">13.08%</td>
|
||||||
|
<td className="py-2 text-right">—</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p className="text-slate-500 text-xs">PM = Portfolio Margin模式,资金利用率约95%。数据来源:Binance fapi/v1/fundingRate官方API</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6 space-y-3">
|
||||||
|
<h2 className="text-lg font-semibold text-cyan-400">风险说明</h2>
|
||||||
|
<div className="space-y-2 text-sm">
|
||||||
|
{[
|
||||||
|
{ level: "🟡 中", risk: "市场周期", desc: "熊市年化可降至0-4%,但不亏本金" },
|
||||||
|
{ level: "🟢 低", risk: "费率持续为负", desc: "历史负费率仅12-13%,长期均值为正" },
|
||||||
|
{ level: "🟡 中", risk: "交易所对手方", desc: "FTX教训,建议考虑分散" },
|
||||||
|
{ level: "🟢 低", risk: "爆仓风险", desc: "1倍杠杆+对冲,理论需BTC翻倍才触发" },
|
||||||
|
{ level: "🟢 低", risk: "基差波动", desc: "长期持有不影响,只影响平仓时机" },
|
||||||
|
].map((r, i) => (
|
||||||
|
<div key={i} className="flex items-start gap-3 bg-slate-900/40 rounded-lg px-4 py-2">
|
||||||
|
<span className="text-xs w-12 shrink-0 mt-0.5">{r.level}</span>
|
||||||
|
<span className="text-slate-300 font-medium w-24 shrink-0">{r.risk}</span>
|
||||||
|
<span className="text-slate-400">{r.desc}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
91
frontend/app/history/page.tsx
Normal file
91
frontend/app/history/page.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { HistoryPoint } from "@/lib/api";
|
||||||
|
import {
|
||||||
|
LineChart, Line, XAxis, YAxis, Tooltip, Legend,
|
||||||
|
ResponsiveContainer, ReferenceLine
|
||||||
|
} from "recharts";
|
||||||
|
|
||||||
|
export default function HistoryPage() {
|
||||||
|
const [btc, setBtc] = useState<HistoryPoint[]>([]);
|
||||||
|
const [eth, setEth] = useState<HistoryPoint[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch("/api/history")
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((d) => { setBtc(d.BTC ?? []); setEth(d.ETH ?? []); setLoading(false); })
|
||||||
|
.catch(() => setLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const btcMap = new Map(btc.map((p) => [p.timestamp.slice(0, 13), p.fundingRate * 100]));
|
||||||
|
const ethMap = new Map(eth.map((p) => [p.timestamp.slice(0, 13), p.fundingRate * 100]));
|
||||||
|
const allTimes = Array.from(new Set([...btcMap.keys(), ...ethMap.keys()])).sort();
|
||||||
|
const chartData = allTimes.slice(-42).map((t) => ({
|
||||||
|
time: t.slice(5).replace("T", " "),
|
||||||
|
BTC: btcMap.get(t) ?? null,
|
||||||
|
ETH: ethMap.get(t) ?? null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const tableData = allTimes.slice().reverse().slice(0, 50).map((t) => ({
|
||||||
|
time: t.replace("T", " "),
|
||||||
|
btc: btcMap.get(t),
|
||||||
|
eth: ethMap.get(t),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold text-slate-100">历史费率</h1>
|
||||||
|
<p className="text-slate-400 text-sm mt-1">过去7天 BTC / ETH 资金费率记录</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="text-slate-400">加载中...</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6">
|
||||||
|
<h2 className="text-slate-200 font-semibold mb-4">费率走势(过去7天)</h2>
|
||||||
|
<ResponsiveContainer width="100%" height={260}>
|
||||||
|
<LineChart data={chartData} margin={{ top: 4, right: 8, bottom: 4, left: 8 }}>
|
||||||
|
<XAxis dataKey="time" tick={{ fill: "#64748b", fontSize: 10 }} tickLine={false} interval="preserveStartEnd" />
|
||||||
|
<YAxis tick={{ fill: "#64748b", fontSize: 10 }} tickLine={false} axisLine={false} tickFormatter={(v) => `${v.toFixed(3)}%`} width={60} />
|
||||||
|
<Tooltip formatter={(v: number) => `${v.toFixed(4)}%`} contentStyle={{ background: "#1e293b", border: "1px solid #475569", borderRadius: 8 }} />
|
||||||
|
<Legend wrapperStyle={{ fontSize: 12, color: "#94a3b8" }} />
|
||||||
|
<ReferenceLine y={0} stroke="#475569" strokeDasharray="4 2" />
|
||||||
|
<Line type="monotone" dataKey="BTC" stroke="#06b6d4" strokeWidth={1.5} dot={false} connectNulls />
|
||||||
|
<Line type="monotone" dataKey="ETH" stroke="#8b5cf6" strokeWidth={1.5} dot={false} connectNulls />
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-xl border border-slate-700 bg-slate-800/50 overflow-hidden">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-slate-700 bg-slate-800">
|
||||||
|
<th className="text-left px-4 py-3 text-slate-400 font-medium">时间</th>
|
||||||
|
<th className="text-right px-4 py-3 text-cyan-400 font-medium">BTC 费率</th>
|
||||||
|
<th className="text-right px-4 py-3 text-violet-400 font-medium">ETH 费率</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{tableData.map((row, i) => (
|
||||||
|
<tr key={i} className="border-b border-slate-700/50 hover:bg-slate-700/30">
|
||||||
|
<td className="px-4 py-2 text-slate-400 font-mono text-xs">{row.time}</td>
|
||||||
|
<td className={`px-4 py-2 text-right font-mono text-xs ${row.btc == null ? "text-slate-500" : row.btc >= 0 ? "text-emerald-400" : "text-red-400"}`}>
|
||||||
|
{row.btc != null ? `${row.btc.toFixed(4)}%` : "--"}
|
||||||
|
</td>
|
||||||
|
<td className={`px-4 py-2 text-right font-mono text-xs ${row.eth == null ? "text-slate-500" : row.eth >= 0 ? "text-emerald-400" : "text-red-400"}`}>
|
||||||
|
{row.eth != null ? `${row.eth.toFixed(4)}%` : "--"}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user