From 984019d9ab38f8fc5028172e57bbd49dd101f154 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 2 Mar 2026 03:08:19 +0000 Subject: [PATCH] fix: paper latest signals use signal-history with strategy filter + layer scores - V5.1 paper: signal-history?strategy=v51_baseline - V5.2 paper: signal-history?strategy=v52_8signals - API now returns factors in signal-history - Each signal shows layer score badges below --- backend/main.py | 11 +++++-- frontend/app/paper-v52/page.tsx | 52 ++++++++++++++++----------------- frontend/app/paper/page.tsx | 40 +++++++++++++------------ 3 files changed, 56 insertions(+), 47 deletions(-) diff --git a/backend/main.py b/backend/main.py index 901b271..f771e32 100644 --- a/backend/main.py +++ b/backend/main.py @@ -570,14 +570,21 @@ async def get_signal_history( strategy: str = "v52_8signals", user: dict = Depends(get_current_user), ): - """返回最近的信号历史(只返回有信号的记录)""" + """返回最近的信号历史(只返回有信号的记录),含各层分数""" sym_full = symbol.upper() + "USDT" rows = await async_fetch( - "SELECT ts, score, signal FROM signal_indicators " + "SELECT ts, score, signal, factors FROM signal_indicators " "WHERE symbol = $1 AND strategy = $2 AND signal IS NOT NULL " "ORDER BY ts DESC LIMIT $3", sym_full, strategy, limit ) + # factors可能是JSON string + for r in rows: + if isinstance(r.get("factors"), str): + try: + r["factors"] = json.loads(r["factors"]) + except Exception: + pass return {"symbol": symbol, "count": len(rows), "data": rows} diff --git a/frontend/app/paper-v52/page.tsx b/frontend/app/paper-v52/page.tsx index 3ecfd78..62c98a7 100644 --- a/frontend/app/paper-v52/page.tsx +++ b/frontend/app/paper-v52/page.tsx @@ -166,17 +166,19 @@ function LatestSignals() { const [signals, setSignals] = useState>({}); useEffect(() => { const f = async () => { - try { - const r = await authFetch("/api/signals/latest?strategy=v52_8signals"); - if (r.ok) { - const j = await r.json(); - setSignals(j); - } - } catch {} + for (const sym of COINS) { + try { + const r = await authFetch(`/api/signals/signal-history?symbol=${sym.replace("USDT","")}&limit=1&strategy=v52_8signals`); + if (r.ok) { + const j = await r.json(); + if (j.data && j.data.length > 0) { + setSignals(prev => ({ ...prev, [sym]: j.data[0] })); + } + } + } catch {} + } }; - f(); - const iv = setInterval(f, 15000); - return () => clearInterval(iv); + f(); const iv = setInterval(f, 15000); return () => clearInterval(iv); }, []); return ( @@ -185,11 +187,11 @@ function LatestSignals() {

最新信号

- {COINS.map((sym) => { + {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; + const fc = s?.factors; return (
@@ -201,25 +203,23 @@ function LatestSignals() { {s.signal === "LONG" ? "🟢" : "🔴"} {s.signal} {s.score}分 + {s.score >= 85 ? "加仓" : "标准"} ) : ( - <> - ⚪ 无信号 - {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 && ( + {fc && (
- 方向{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} + 方向{fc.direction?.score ?? 0}/{fc.direction?.max ?? 40} + 拥挤{fc.crowding?.score ?? 0}/{fc.crowding?.max ?? 18} + FR{fc.funding_rate?.score ?? 0}/{fc.funding_rate?.max ?? 5} + 环境{fc.environment?.score ?? 0}/{fc.environment?.max ?? 12} + 确认{fc.confirmation?.score ?? 0}/{fc.confirmation?.max ?? 15} + 清算{fc.liquidation?.score ?? 0}/{fc.liquidation?.max ?? 5} + 辅助{fc.auxiliary?.score ?? 0}/{fc.auxiliary?.max ?? 5}
)}
diff --git a/frontend/app/paper/page.tsx b/frontend/app/paper/page.tsx index 7ad492a..ca5eb96 100644 --- a/frontend/app/paper/page.tsx +++ b/frontend/app/paper/page.tsx @@ -118,13 +118,17 @@ function LatestSignals() { const [signals, setSignals] = useState>({}); useEffect(() => { const f = async () => { - try { - const r = await authFetch("/api/signals/latest?strategy=v51_baseline"); - if (r.ok) { - const j = await r.json(); - setSignals(j); - } - } catch {} + for (const sym of COINS) { + try { + const r = await authFetch(`/api/signals/signal-history?symbol=${sym.replace("USDT","")}&limit=1&strategy=v51_baseline`); + if (r.ok) { + const j = await r.json(); + if (j.data && j.data.length > 0) { + setSignals(prev => ({ ...prev, [sym]: j.data[0] })); + } + } + } catch {} + } }; f(); const iv = setInterval(f, 15000); return () => clearInterval(iv); }, []); @@ -136,10 +140,10 @@ 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; + const fc = s?.factors; return (
@@ -151,23 +155,21 @@ function LatestSignals() { {s.signal === "LONG" ? "🟢" : "🔴"} {s.signal} {s.score}分 + {s.score >= 85 ? "加仓" : "标准"} ) : ( - <> - ⚪ 无信号 - {s?.score ?? 0}分 - + 暂无信号 )}
{ago !== null && {ago < 60 ? `${ago}m前` : `${Math.round(ago/60)}h前`}}
- {f && ( + {fc && (
- 方向{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} + 方向{fc.direction?.score ?? 0}/{fc.direction?.max ?? 45} + 拥挤{fc.crowding?.score ?? 0}/{fc.crowding?.max ?? 20} + 环境{fc.environment?.score ?? 0}/{fc.environment?.max ?? 15} + 确认{fc.confirmation?.score ?? 0}/{fc.confirmation?.max ?? 15} + 辅助{fc.auxiliary?.score ?? 0}/{fc.auxiliary?.max ?? 5}
)}