"use client"; import { useEffect, useState, useCallback } from "react"; import { authFetch } from "@/lib/auth"; import { useAuth } from "@/lib/auth"; import Link from "next/link"; import { ComposedChart, Area, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, ReferenceLine, CartesianGrid, Legend } from "recharts"; type Symbol = "BTC" | "ETH" | "XRP" | "SOL"; interface IndicatorRow { ts: number; cvd_fast: number; cvd_mid: number; cvd_day: number; atr_5m: number; vwap_30m: number; price: number; score: number; signal: string | null; } interface LatestIndicator { ts: number; cvd_fast: number; cvd_mid: number; cvd_day: number; cvd_fast_slope: number; atr_5m: number; atr_percentile: number; vwap_30m: number; price: number; p95_qty: number; p99_qty: number; score: number; signal: string | null; tier?: "light" | "standard" | "heavy" | null; factors?: { track?: string; direction?: { score?: number; max?: number; cvd_resonance?: number; p99_flow?: number; accel_bonus?: number }; crowding?: { score?: number; max?: number; lsr_contrarian?: number; top_trader_position?: number }; environment?: { score?: number; max?: number }; auxiliary?: { score?: number; max?: number; coinbase_premium?: number }; // BTC gate fields gate_passed?: boolean; block_reason?: string; // BTC用 gate_block?: string; // ALT用 obi_raw?: number; spot_perp_div?: number; whale_cvd_ratio?: number; atr_pct_price?: number; alt_score_ref?: number; } | null; } const WINDOWS = [ { label: "1h", value: 60 }, { label: "4h", value: 240 }, { label: "12h", value: 720 }, { label: "24h", value: 1440 }, ]; function bjtStr(ms: number) { const d = new Date(ms + 8 * 3600 * 1000); return `${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}`; } function bjtFull(ms: number) { const d = new Date(ms + 8 * 3600 * 1000); return `${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")} ${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}:${String(d.getUTCSeconds()).padStart(2, "0")}`; } function fmt(v: number, decimals = 1): string { if (Math.abs(v) >= 1000000) return `${(v / 1000000).toFixed(1)}M`; if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(1)}K`; return v.toFixed(decimals); } function LayerScore({ label, score, max, colorClass }: { label: string; score: number; max: number; colorClass: string }) { const ratio = Math.max(0, Math.min((score / max) * 100, 100)); return (
{label}
{score}/{max}
); } // ─── ALT Gate 状态卡片 ────────────────────────────────────────── const ALT_GATE_THRESHOLDS: Record = { ETH: { vol: "0.3%", obi: "0.35", spd: "0.5%", whale: "$50k" }, XRP: { vol: "0.4%", obi: "0.40", spd: "0.6%", whale: "$30k" }, SOL: { vol: "0.6%", obi: "0.45", spd: "0.8%", whale: "$20k" }, }; function ALTGateCard({ symbol, factors }: { symbol: Symbol; factors: LatestIndicator["factors"] }) { if (!factors || symbol === "BTC") return null; const thresholds = ALT_GATE_THRESHOLDS[symbol] ?? ALT_GATE_THRESHOLDS["ETH"]; const passed = factors.gate_passed ?? true; const blockReason = factors.gate_block; return (

🔒 {symbol} Gate-Control

{passed ? "✅ Gate通过" : "❌ 否决"}

波动率

{((factors.atr_pct_price ?? 0) * 100).toFixed(3)}%

需 ≥{thresholds.vol}

OBI

= 0 ? "text-emerald-600" : "text-red-500"}`}> {((factors.obi_raw ?? 0) * 100).toFixed(2)}%

否决±{thresholds.obi}

期现背离

= 0 ? "text-emerald-600" : "text-red-500"}`}> {((factors.spot_perp_div ?? 0) * 10000).toFixed(2)}bps

否决±{thresholds.spd}

鲸鱼阈值

{thresholds.whale}

大单门槛

{blockReason && (

否决原因: {blockReason}

)}
); } // ─── BTC Gate 状态卡片 ─────────────────────────────────────────── function BTCGateCard({ factors }: { factors: LatestIndicator["factors"] }) { if (!factors) return null; return (

⚡ BTC Gate-Control

{factors.gate_passed ? "✅ Gate通过" : "❌ 否决"}

波动率

{((factors.atr_pct_price ?? 0) * 100).toFixed(3)}%

需 ≥0.2%

OBI

= 0 ? "text-emerald-600" : "text-red-500"}`}> {((factors.obi_raw ?? 0) * 100).toFixed(2)}%

盘口失衡

期现背离

= 0 ? "text-emerald-600" : "text-red-500"}`}> {((factors.spot_perp_div ?? 0) * 10000).toFixed(2)}bps

spot-perp

巨鲸CVD

= 0 ? "text-emerald-600" : "text-red-500"}`}> {((factors.whale_cvd_ratio ?? 0) * 100).toFixed(2)}%

>$100k

{factors.block_reason && (

否决原因: {factors.block_reason}

)} {factors.alt_score_ref !== undefined && (

参考评分(ALT逻辑): {factors.alt_score_ref} 分

)}
); } // ─── 实时指标卡片 ──────────────────────────────────────────────── function IndicatorCards({ symbol }: { symbol: Symbol }) { const [data, setData] = useState(null); const strategy = symbol === "BTC" ? "v53_btc" : "v53_alt"; useEffect(() => { const fetch = async () => { try { const res = await authFetch(`/api/signals/latest?strategy=${strategy}`); if (!res.ok) return; const json = await res.json(); setData(json[symbol] || null); } catch {} }; fetch(); const iv = setInterval(fetch, 5000); return () => clearInterval(iv); }, [symbol, strategy]); if (!data) return
等待指标数据...
; const isBTC = symbol === "BTC"; const priceVsVwap = data.price > data.vwap_30m ? "上方" : "下方"; return (
{/* CVD三轨 */}

CVD_fast (30m)

= 0 ? "text-emerald-600" : "text-red-500"}`}> {fmt(data.cvd_fast)}

斜率: = 0 ? "text-emerald-600" : "text-red-500"}> {data.cvd_fast_slope >= 0 ? "↑" : "↓"}{fmt(Math.abs(data.cvd_fast_slope))}

CVD_mid (4h)

= 0 ? "text-emerald-600" : "text-red-500"}`}> {fmt(data.cvd_mid)}

{data.cvd_mid > 0 ? "多" : "空"}头占优

CVD共振

= 0 && data.cvd_mid >= 0 ? "text-emerald-600" : data.cvd_fast < 0 && data.cvd_mid < 0 ? "text-red-500" : "text-slate-400"}`}> {data.cvd_fast >= 0 && data.cvd_mid >= 0 ? "✅ 多头共振" : data.cvd_fast < 0 && data.cvd_mid < 0 ? "✅ 空头共振" : "⚠️ 分歧"}

V5.3核心信号

{/* ATR + VWAP */}

ATR

${fmt(data.atr_5m, 2)}

60 ? "text-amber-600 font-semibold" : "text-slate-400"}> {data.atr_percentile.toFixed(0)}%{data.atr_percentile > 60 ? "🔥" : ""}

VWAP

${data.vwap_30m.toLocaleString("en-US", { maximumFractionDigits: 1 })}

价格在 data.vwap_30m ? "text-emerald-600" : "text-red-500"}>{priceVsVwap}

P95

{data.p95_qty.toFixed(4)}

大单阈值

P99

{data.p99_qty.toFixed(4)}

超大单

{/* 信号状态 */}

{isBTC ? "BTC Gate-Control" : "ALT 四层评分"} {" · "}{isBTC ? "v53_btc" : "v53_alt"}

{data.signal === "LONG" ? "🟢 做多" : data.signal === "SHORT" ? "🔴 做空" : "⚪ 无信号"}

{data.score}/100

{data.tier === "heavy" ? "加仓" : data.tier === "standard" ? "标准" : "不开仓"}

{/* ALT四层 */} {!isBTC && (
)}
{/* ALT Gate 卡片 */} {!isBTC && data.factors && } {/* BTC Gate 卡片 */} {isBTC && data.factors && }
); } // ─── 信号历史 ──────────────────────────────────────────────────── interface SignalRecord { ts: number; score: number; signal: string; } function SignalHistory({ symbol }: { symbol: Symbol }) { const [data, setData] = useState([]); const strategy = symbol === "BTC" ? "v53_btc" : "v53_alt"; useEffect(() => { const fetchData = async () => { try { const res = await authFetch(`/api/signals/signal-history?symbol=${symbol}&limit=20&strategy=${strategy}`); if (!res.ok) return; const json = await res.json(); setData(json.data || []); } catch {} }; fetchData(); const iv = setInterval(fetchData, 15000); return () => clearInterval(iv); }, [symbol, strategy]); if (data.length === 0) return null; return (

最近信号 ({strategy})

{data.map((s, i) => (
{s.signal === "LONG" ? "🟢 LONG" : "🔴 SHORT"} {bjtFull(s.ts)}
{s.score} = 85 ? "bg-red-100 text-red-700" : s.score >= 75 ? "bg-blue-100 text-blue-700" : "bg-slate-100 text-slate-600" }`}> {s.score >= 85 ? "加仓" : s.score >= 75 ? "标准" : "不开仓"}
))}
); } // ─── CVD图表 ──────────────────────────────────────────────────── function CVDChart({ symbol, minutes }: { symbol: Symbol; minutes: number }) { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const strategy = symbol === "BTC" ? "v53_btc" : "v53_alt"; const fetchData = useCallback(async (silent = false) => { try { const res = await authFetch(`/api/signals/indicators?symbol=${symbol}&minutes=${minutes}&strategy=${strategy}`); if (!res.ok) return; const json = await res.json(); setData(json.data || []); if (!silent) setLoading(false); } catch {} }, [symbol, minutes, strategy]); useEffect(() => { setLoading(true); fetchData(); const iv = setInterval(() => fetchData(true), 30000); return () => clearInterval(iv); }, [fetchData]); const chartData = data.map(d => ({ time: bjtStr(d.ts), fast: parseFloat(d.cvd_fast?.toFixed(2) || "0"), mid: parseFloat(d.cvd_mid?.toFixed(2) || "0"), price: d.price, })); const prices = chartData.map(d => d.price).filter(v => v > 0); const pMin = prices.length ? Math.min(...prices) : 0; const pMax = prices.length ? Math.max(...prices) : 0; const pPad = (pMax - pMin) * 0.3 || pMax * 0.001; if (loading) return
加载指标数据...
; if (data.length === 0) return
暂无 V5.3 指标数据,signal-engine 需运行积累
; return ( v >= 1000 ? `$${(v / 1000).toFixed(1)}k` : `$${v.toFixed(0)}`} /> { if (name === "price") return [`$${Number(v).toLocaleString()}`, "币价"]; if (name === "fast") return [fmt(Number(v)), "CVD_fast(30m)"]; return [fmt(Number(v)), "CVD_mid(4h)"]; }} contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, fontSize: 11 }} /> ); } // ─── 主页面 ────────────────────────────────────────────────────── export default function SignalsV53Page() { const { isLoggedIn, loading } = useAuth(); const [symbol, setSymbol] = useState("ETH"); const [minutes, setMinutes] = useState(240); if (loading) return
加载中...
; if (!isLoggedIn) return (
🔒

请先登录查看信号数据

登录 注册
); return (

⚡ 信号引擎 V5.3

四层评分 55/25/15/5 · ALT双轨 + BTC gate-control · {symbol === "BTC" ? " 🔵 BTC轨(gate-control)" : " 🟣 ALT轨(ETH/XRP/SOL)"}

{(["BTC", "ETH", "XRP", "SOL"] as Symbol[]).map(s => ( ))}

CVD三轨 + 币价

蓝=fast(30m) · 紫=mid(4h) · 橙=价格

{WINDOWS.map(w => ( ))}

📖 V5.3 双轨信号说明

🟣 ALT轨(ETH/XRP/SOL)— 四层线性评分

1️⃣ 方向层(55分) — CVD共振30分(fast+mid同向)+ P99大单对齐20分 + 加速奖励5分。删除独立确认层,解决CVD双重计分问题。

2️⃣ 拥挤层(25分) — LSR反向拥挤15分(散户过度拥挤=信号)+ 大户持仓方向10分。

3️⃣ 环境层(15分) — OI变化率,新资金进场vs撤离,判断趋势持续性。

4️⃣ 辅助层(5分) — Coinbase Premium,美系机构动向。

🔵 BTC轨 — Gate-Control逻辑

波动率门控:ATR/Price ≥ 0.2%,低波动行情拒绝开仓

OBI否决:订单簿失衡超阈值且与信号方向冲突时否决(实时100ms)

期现背离否决:spot与perp价差超阈值时否决(实时1s)

巨鲸CVD:>$100k成交额净CVD,15分钟滚动窗口实时计算

档位:<75不开仓 · 75-84标准 · ≥85加仓 · 冷却10分钟
); }