feat: dual-axis Delta+price chart, show vwap price overlay on delta chart

This commit is contained in:
root 2026-02-27 14:08:45 +00:00
parent f27fdca6ac
commit 4a6232ed05

View File

@ -5,7 +5,7 @@ import { authFetch } from "@/lib/auth";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
import Link from "next/link"; import Link from "next/link";
import { import {
AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer, ReferenceLine, CartesianGrid ComposedChart, Area, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, ReferenceLine, CartesianGrid, Legend
} from "recharts"; } from "recharts";
// ─── Types ─────────────────────────────────────────────────────── // ─── Types ───────────────────────────────────────────────────────
@ -253,44 +253,55 @@ function FlowAnalysis({ symbol }: { symbol: Symbol }) {
</div> </div>
</div> </div>
{/* Delta折线图 */} {/* Delta + 价格双Y轴图 */}
<div> <div>
<p className="text-xs text-slate-500 mb-2 font-medium"> Delta==</p> <p className="text-xs text-slate-500 mb-2 font-medium"> Delta==+ </p>
<ResponsiveContainer width="100%" height={160}> <ResponsiveContainer width="100%" height={200}>
<AreaChart data={chartData} margin={{ top: 4, right: 8, bottom: 0, left: 8 }}> <ComposedChart data={chartData} margin={{ top: 4, right: 60, bottom: 0, left: 8 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#f1f5f9" /> <CartesianGrid strokeDasharray="3 3" stroke="#f1f5f9" />
<XAxis dataKey="time" tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} interval="preserveStartEnd" /> <XAxis dataKey="time" tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} interval="preserveStartEnd" />
<YAxis tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} axisLine={false} width={50} /> {/* 左轴Delta */}
<YAxis yAxisId="delta" tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} axisLine={false} width={55} />
{/* 右轴:价格 */}
<YAxis yAxisId="price" orientation="right" tick={{ fill: "#f59e0b", fontSize: 10 }} tickLine={false} axisLine={false} width={65}
tickFormatter={(v: number) => v >= 1000 ? `$${(v/1000).toFixed(1)}k` : `$${v.toFixed(0)}`}
/>
<Tooltip <Tooltip
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
formatter={(v: any) => [`${Number(v).toFixed(2)}`, "Delta"]} formatter={(v: any, name: any) => {
if (name === "price") return [`$${Number(v).toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 })}`, "币价(VWAP)"];
return [`${Number(v).toFixed(2)}`, "Delta"];
}}
contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, fontSize: 11 }} contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, fontSize: 11 }}
/> />
<ReferenceLine y={0} stroke="#94a3b8" strokeDasharray="4 2" /> <Legend wrapperStyle={{ fontSize: 11 }} />
<Area type="monotone" dataKey="delta" <ReferenceLine yAxisId="delta" y={0} stroke="#94a3b8" strokeDasharray="4 2" />
stroke="#2563eb" fill="#eff6ff" strokeWidth={1.5} <Area yAxisId="delta" type="monotone" dataKey="delta" name="Delta"
dot={false} connectNulls stroke="#2563eb" fill="#eff6ff" strokeWidth={1.5} dot={false} connectNulls
/> />
</AreaChart> <Line yAxisId="price" type="monotone" dataKey="vwap" name="price"
stroke="#f59e0b" strokeWidth={1.5} dot={false} connectNulls strokeDasharray="4 2"
/>
</ComposedChart>
</ResponsiveContainer> </ResponsiveContainer>
</div> </div>
{/* 买卖量柱状图 */} {/* 买卖量图 */}
<div> <div>
<p className="text-xs text-slate-500 mb-2 font-medium"> vs </p> <p className="text-xs text-slate-500 mb-2 font-medium"> vs </p>
<ResponsiveContainer width="100%" height={160}> <ResponsiveContainer width="100%" height={160}>
<AreaChart data={chartData} margin={{ top: 4, right: 8, bottom: 0, left: 8 }}> <ComposedChart data={chartData} margin={{ top: 4, right: 8, bottom: 0, left: 8 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#f1f5f9" /> <CartesianGrid strokeDasharray="3 3" stroke="#f1f5f9" />
<XAxis dataKey="time" tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} interval="preserveStartEnd" /> <XAxis dataKey="time" tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} interval="preserveStartEnd" />
<YAxis tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} axisLine={false} width={50} /> <YAxis yAxisId="vol" tick={{ fill: "#94a3b8", fontSize: 10 }} tickLine={false} axisLine={false} width={50} />
<Tooltip <Tooltip
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
formatter={(v: any, name: any) => [`${Number(v).toFixed(2)}`, name === "buy" ? "主动买" : "主动卖"]} formatter={(v: any, name: any) => [`${Number(v).toFixed(2)}`, name === "buy" ? "主动买" : "主动卖"]}
contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, fontSize: 11 }} contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, fontSize: 11 }}
/> />
<Area type="monotone" dataKey="buy" stroke="#16a34a" fill="#f0fdf4" strokeWidth={1.5} dot={false} connectNulls /> <Area yAxisId="vol" type="monotone" dataKey="buy" name="buy" stroke="#16a34a" fill="#f0fdf4" strokeWidth={1.5} dot={false} connectNulls />
<Area type="monotone" dataKey="sell" stroke="#dc2626" fill="#fef2f2" strokeWidth={1.5} dot={false} connectNulls /> <Area yAxisId="vol" type="monotone" dataKey="sell" name="sell" stroke="#dc2626" fill="#fef2f2" strokeWidth={1.5} dot={false} connectNulls />
</AreaChart> </ComposedChart>
</ResponsiveContainer> </ResponsiveContainer>
</div> </div>