From 5b704a0a0eb331a1b54c576c8208ff59f2bdebea Mon Sep 17 00:00:00 2001 From: root Date: Mon, 2 Mar 2026 02:59:39 +0000 Subject: [PATCH] feat: paper pages show per-strategy signals with layer scores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - V5.1 paper reads /api/signals/latest?strategy=v51_baseline - V5.2 paper reads /api/signals/latest?strategy=v52_8signals - Each coin shows layer score badges (方向/拥挤/环境/确认/辅助) - V5.2 additionally shows FR and 清算 badges --- frontend/app/paper-v52/page.tsx | 65 ++++++++++++++++++++------------- frontend/app/paper/page.tsx | 63 +++++++++++++++++++------------- 2 files changed, 76 insertions(+), 52 deletions(-) diff --git a/frontend/app/paper-v52/page.tsx b/frontend/app/paper-v52/page.tsx index 893a79b..3ecfd78 100644 --- a/frontend/app/paper-v52/page.tsx +++ b/frontend/app/paper-v52/page.tsx @@ -166,17 +166,13 @@ function LatestSignals() { const [signals, setSignals] = useState>({}); useEffect(() => { const f = async () => { - for (const sym of COINS) { - try { - const r = await authFetch(`/api/signals/signal-history?symbol=${sym.replace("USDT", "")}&limit=1`); - if (r.ok) { - const j = await r.json(); - if (j.data && j.data.length > 0) { - setSignals((prev) => ({ ...prev, [sym]: j.data[0] })); - } - } - } catch {} - } + try { + const r = await authFetch("/api/signals/latest?strategy=v52_8signals"); + if (r.ok) { + const j = await r.json(); + setSignals(j); + } + } catch {} }; f(); const iv = setInterval(f, 15000); @@ -190,25 +186,42 @@ function LatestSignals() {
{COINS.map((sym) => { - const s = signals[sym]; const coin = sym.replace("USDT", ""); + const s = signals[coin]; const ago = s?.ts ? Math.round((Date.now() - s.ts) / 60000) : null; + const f = s?.factors; return ( -
-
- {coin} - {s?.signal ? ( - <> - - {s.signal === "LONG" ? "🟢" : "🔴"} {s.signal} - - {s.score}分 - - ) : ( - ⚪ 无信号 - )} +
+
+
+ {coin} + {s?.signal ? ( + <> + + {s.signal === "LONG" ? "🟢" : "🔴"} {s.signal} + + {s.score}分 + + ) : ( + <> + ⚪ 无信号 + {s?.score ?? 0}分 + + )} +
+ {ago !== null && {ago < 60 ? `${ago}m前` : `${Math.round(ago / 60)}h前`}}
- {ago !== null && {ago < 60 ? `${ago}m前` : `${Math.round(ago / 60)}h前`}} + {f && ( +
+ 方向{f.direction?.score ?? 0}/{f.direction?.max ?? 40} + 拥挤{f.crowding?.score ?? 0}/{f.crowding?.max ?? 18} + FR{f.funding_rate?.score ?? 0}/{f.funding_rate?.max ?? 5} + 环境{f.environment?.score ?? 0}/{f.environment?.max ?? 12} + 确认{f.confirmation?.score ?? 0}/{f.confirmation?.max ?? 15} + 清算{f.liquidation?.score ?? 0}/{f.liquidation?.max ?? 5} + 辅助{f.auxiliary?.score ?? 0}/{f.auxiliary?.max ?? 5} +
+ )}
); })} diff --git a/frontend/app/paper/page.tsx b/frontend/app/paper/page.tsx index 7b6ddee..7ad492a 100644 --- a/frontend/app/paper/page.tsx +++ b/frontend/app/paper/page.tsx @@ -118,17 +118,13 @@ function LatestSignals() { const [signals, setSignals] = useState>({}); useEffect(() => { const f = async () => { - for (const sym of COINS) { - try { - const r = await authFetch(`/api/signals/signal-history?symbol=${sym.replace("USDT","")}&limit=1`); - if (r.ok) { - const j = await r.json(); - if (j.data && j.data.length > 0) { - setSignals(prev => ({ ...prev, [sym]: j.data[0] })); - } - } - } catch {} - } + try { + const r = await authFetch("/api/signals/latest?strategy=v51_baseline"); + if (r.ok) { + const j = await r.json(); + setSignals(j); + } + } catch {} }; f(); const iv = setInterval(f, 15000); return () => clearInterval(iv); }, []); @@ -140,25 +136,40 @@ function LatestSignals() {
{COINS.map(sym => { - const s = signals[sym]; const coin = sym.replace("USDT", ""); + const s = signals[coin]; const ago = s?.ts ? Math.round((Date.now() - s.ts) / 60000) : null; + const f = s?.factors; return ( -
-
- {coin} - {s?.signal ? ( - <> - - {s.signal === "LONG" ? "🟢" : "🔴"} {s.signal} - - {s.score}分 - - ) : ( - ⚪ 无信号 - )} +
+
+
+ {coin} + {s?.signal ? ( + <> + + {s.signal === "LONG" ? "🟢" : "🔴"} {s.signal} + + {s.score}分 + + ) : ( + <> + ⚪ 无信号 + {s?.score ?? 0}分 + + )} +
+ {ago !== null && {ago < 60 ? `${ago}m前` : `${Math.round(ago/60)}h前`}}
- {ago !== null && {ago < 60 ? `${ago}m前` : `${Math.round(ago/60)}h前`}} + {f && ( +
+ 方向{f.direction?.score ?? 0}/{f.direction?.max ?? 45} + 拥挤{f.crowding?.score ?? 0}/{f.crowding?.max ?? 20} + 环境{f.environment?.score ?? 0}/{f.environment?.max ?? 15} + 确认{f.confirmation?.score ?? 0}/{f.confirmation?.max ?? 15} + 辅助{f.auxiliary?.score ?? 0}/{f.auxiliary?.max ?? 5} +
+ )}
); })}