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
This commit is contained in:
parent
5b704a0a0e
commit
984019d9ab
@ -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}
|
||||
|
||||
|
||||
|
||||
@ -166,17 +166,19 @@ function LatestSignals() {
|
||||
const [signals, setSignals] = useState<Record<string, any>>({});
|
||||
useEffect(() => {
|
||||
const f = async () => {
|
||||
for (const sym of COINS) {
|
||||
try {
|
||||
const r = await authFetch("/api/signals/latest?strategy=v52_8signals");
|
||||
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();
|
||||
setSignals(j);
|
||||
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() {
|
||||
<h3 className="font-semibold text-slate-800 text-xs">最新信号</h3>
|
||||
</div>
|
||||
<div className="divide-y divide-slate-50">
|
||||
{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 (
|
||||
<div key={sym} className="px-3 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
@ -201,25 +203,23 @@ function LatestSignals() {
|
||||
{s.signal === "LONG" ? "🟢" : "🔴"} {s.signal}
|
||||
</span>
|
||||
<span className="font-mono text-xs font-bold text-slate-800">{s.score}分</span>
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded bg-emerald-50 text-emerald-700 font-medium">{s.score >= 85 ? "加仓" : "标准"}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-[10px] text-slate-400">⚪ 无信号</span>
|
||||
<span className="font-mono text-[10px] text-slate-500">{s?.score ?? 0}分</span>
|
||||
</>
|
||||
<span className="text-[10px] text-slate-400">暂无信号</span>
|
||||
)}
|
||||
</div>
|
||||
{ago !== null && <span className="text-[10px] text-slate-400">{ago < 60 ? `${ago}m前` : `${Math.round(ago/60)}h前`}</span>}
|
||||
</div>
|
||||
{f && (
|
||||
{fc && (
|
||||
<div className="flex gap-1 mt-1 flex-wrap">
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-blue-50 text-blue-700">方向{f.direction?.score ?? 0}/{f.direction?.max ?? 40}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-violet-50 text-violet-700">拥挤{f.crowding?.score ?? 0}/{f.crowding?.max ?? 18}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-cyan-50 text-cyan-700">FR{f.funding_rate?.score ?? 0}/{f.funding_rate?.max ?? 5}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-emerald-50 text-emerald-700">环境{f.environment?.score ?? 0}/{f.environment?.max ?? 12}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-amber-50 text-amber-700">确认{f.confirmation?.score ?? 0}/{f.confirmation?.max ?? 15}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-orange-50 text-orange-700">清算{f.liquidation?.score ?? 0}/{f.liquidation?.max ?? 5}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-slate-100 text-slate-600">辅助{f.auxiliary?.score ?? 0}/{f.auxiliary?.max ?? 5}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-blue-50 text-blue-700">方向{fc.direction?.score ?? 0}/{fc.direction?.max ?? 40}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-violet-50 text-violet-700">拥挤{fc.crowding?.score ?? 0}/{fc.crowding?.max ?? 18}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-cyan-50 text-cyan-700">FR{fc.funding_rate?.score ?? 0}/{fc.funding_rate?.max ?? 5}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-emerald-50 text-emerald-700">环境{fc.environment?.score ?? 0}/{fc.environment?.max ?? 12}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-amber-50 text-amber-700">确认{fc.confirmation?.score ?? 0}/{fc.confirmation?.max ?? 15}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-orange-50 text-orange-700">清算{fc.liquidation?.score ?? 0}/{fc.liquidation?.max ?? 5}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-slate-100 text-slate-600">辅助{fc.auxiliary?.score ?? 0}/{fc.auxiliary?.max ?? 5}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -118,13 +118,17 @@ function LatestSignals() {
|
||||
const [signals, setSignals] = useState<Record<string, any>>({});
|
||||
useEffect(() => {
|
||||
const f = async () => {
|
||||
for (const sym of COINS) {
|
||||
try {
|
||||
const r = await authFetch("/api/signals/latest?strategy=v51_baseline");
|
||||
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();
|
||||
setSignals(j);
|
||||
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() {
|
||||
</div>
|
||||
<div className="divide-y divide-slate-50">
|
||||
{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 (
|
||||
<div key={sym} className="px-3 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
@ -151,23 +155,21 @@ function LatestSignals() {
|
||||
{s.signal === "LONG" ? "🟢" : "🔴"} {s.signal}
|
||||
</span>
|
||||
<span className="font-mono text-xs font-bold text-slate-800">{s.score}分</span>
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded bg-emerald-50 text-emerald-700 font-medium">{s.score >= 85 ? "加仓" : "标准"}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-[10px] text-slate-400">⚪ 无信号</span>
|
||||
<span className="font-mono text-[10px] text-slate-500">{s?.score ?? 0}分</span>
|
||||
</>
|
||||
<span className="text-[10px] text-slate-400">暂无信号</span>
|
||||
)}
|
||||
</div>
|
||||
{ago !== null && <span className="text-[10px] text-slate-400">{ago < 60 ? `${ago}m前` : `${Math.round(ago/60)}h前`}</span>}
|
||||
</div>
|
||||
{f && (
|
||||
{fc && (
|
||||
<div className="flex gap-1 mt-1 flex-wrap">
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-blue-50 text-blue-700">方向{f.direction?.score ?? 0}/{f.direction?.max ?? 45}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-violet-50 text-violet-700">拥挤{f.crowding?.score ?? 0}/{f.crowding?.max ?? 20}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-emerald-50 text-emerald-700">环境{f.environment?.score ?? 0}/{f.environment?.max ?? 15}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-amber-50 text-amber-700">确认{f.confirmation?.score ?? 0}/{f.confirmation?.max ?? 15}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-slate-100 text-slate-600">辅助{f.auxiliary?.score ?? 0}/{f.auxiliary?.max ?? 5}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-blue-50 text-blue-700">方向{fc.direction?.score ?? 0}/{fc.direction?.max ?? 45}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-violet-50 text-violet-700">拥挤{fc.crowding?.score ?? 0}/{fc.crowding?.max ?? 20}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-emerald-50 text-emerald-700">环境{fc.environment?.score ?? 0}/{fc.environment?.max ?? 15}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-amber-50 text-amber-700">确认{fc.confirmation?.score ?? 0}/{fc.confirmation?.max ?? 15}</span>
|
||||
<span className="text-[9px] px-1 py-0.5 rounded bg-slate-100 text-slate-600">辅助{fc.auxiliary?.score ?? 0}/{fc.auxiliary?.max ?? 5}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user