diff --git a/backend/__pycache__/main.cpython-310.pyc b/backend/__pycache__/main.cpython-310.pyc deleted file mode 100644 index dc1700c..0000000 Binary files a/backend/__pycache__/main.cpython-310.pyc and /dev/null differ diff --git a/backend/main.py b/backend/main.py index 0881daa..2517927 100644 --- a/backend/main.py +++ b/backend/main.py @@ -426,13 +426,29 @@ async def get_signal_latest(user: dict = Depends(get_current_user)): result = {} for sym in ["BTCUSDT", "ETHUSDT"]: row = await async_fetchrow( - "SELECT ts, cvd_fast, cvd_mid, cvd_day, cvd_fast_slope, atr_5m, atr_percentile, " - "vwap_30m, price, p95_qty, p99_qty, score, signal " - "FROM signal_indicators WHERE symbol = $1 ORDER BY ts DESC LIMIT 1", + "SELECT to_jsonb(si) AS data FROM signal_indicators si WHERE symbol = $1 ORDER BY ts DESC LIMIT 1", sym ) if row: - result[sym.replace("USDT", "")] = row + result[sym.replace("USDT", "")] = row["data"] + return result + + +@app.get("/api/signals/market-indicators") +async def get_market_indicators(user: dict = Depends(get_current_user)): + """返回最新的market_indicators数据(V5.1新增4个数据源)""" + result = {} + for sym in ["BTCUSDT", "ETHUSDT"]: + indicators = {} + for ind_type in ["long_short_ratio", "top_trader_position", "open_interest_hist", "coinbase_premium"]: + row = await async_fetchrow( + "SELECT value, timestamp_ms FROM market_indicators WHERE symbol = $1 AND indicator_type = $2 ORDER BY timestamp_ms DESC LIMIT 1", + sym, + ind_type, + ) + if row: + indicators[ind_type] = {"value": row["value"], "ts": row["timestamp_ms"]} + result[sym.replace("USDT", "")] = indicators return result diff --git a/frontend/app/signals/page.tsx b/frontend/app/signals/page.tsx index 978c0d2..b7efa9a 100644 --- a/frontend/app/signals/page.tsx +++ b/frontend/app/signals/page.tsx @@ -37,6 +37,26 @@ interface LatestIndicator { p99_qty: number; score: number; signal: string | null; + tier?: "light" | "standard" | "heavy" | null; + factors?: { + direction?: { score?: number }; + crowding?: { score?: number }; + environment?: { score?: number }; + confirmation?: { score?: number }; + auxiliary?: { score?: number }; + } | null; +} + +interface MarketIndicatorValue { + value: number; + ts: number; +} + +interface MarketIndicatorSet { + long_short_ratio?: MarketIndicatorValue; + top_trader_position?: MarketIndicatorValue; + open_interest_hist?: MarketIndicatorValue; + coinbase_premium?: MarketIndicatorValue; } const WINDOWS = [ @@ -57,6 +77,75 @@ function fmt(v: number, decimals = 1): string { return v.toFixed(decimals); } +function pct(v: number, digits = 1): string { + return `${(v * 100).toFixed(digits)}%`; +} + +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} +
+
+
+
+
+ ); +} + +function MarketIndicatorsCards({ symbol }: { symbol: Symbol }) { + const [data, setData] = useState(null); + + useEffect(() => { + const fetch = async () => { + try { + const res = await authFetch("/api/signals/market-indicators"); + if (!res.ok) return; + const json = await res.json(); + setData(json[symbol] || null); + } catch {} + }; + fetch(); + const iv = setInterval(fetch, 5000); + return () => clearInterval(iv); + }, [symbol]); + + if (!data) return
等待市场指标数据...
; + + const ls = Number(data.long_short_ratio?.value ?? 1); + const top = Number(data.top_trader_position?.value ?? 0.5); + const oi = Number(data.open_interest_hist?.value ?? 0); + const premium = Number(data.coinbase_premium?.value ?? 0); + + return ( +
+
+

多空比 (L/S)

+

Long: {(ls / (1 + ls) * 100).toFixed(1)}%

+

Short: {(100 - (ls / (1 + ls) * 100)).toFixed(1)}%

+
+
+

大户持仓

+

大户做多: {(top * 100).toFixed(1)}%

+

方向: {top >= 0.55 ? "多头占优" : top <= 0.45 ? "空头占优" : "中性"}

+
+
+

OI变化

+

{pct(oi, 2)}

+

活跃度: {oi >= 0.03 ? "高" : oi > 0 ? "中" : "低"}

+
+
+

Coinbase Premium

+

= 0 ? "text-emerald-600" : "text-red-500"}`}>{premium >= 0 ? "+" : ""}{pct(premium, 2)}

+

机构: {premium > 0.0005 ? "偏多" : premium < -0.0005 ? "偏空" : "中性"}

+
+
+ ); +} + // ─── 实时指标卡片 ──────────────────────────────────────────────── function IndicatorCards({ symbol }: { symbol: Symbol }) { @@ -149,8 +238,8 @@ function IndicatorCards({ symbol }: { symbol: Symbol }) {
- {/* 信号状态 */} -
-

加分

-

{data.score}/60

+

总分

+

{data.score}/100

+

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

+
+ + + + + +
{data.signal && ( -
+
{core1} CVD_fast方向 {core2} CVD_mid方向 {core3} VWAP位置 @@ -275,8 +372,8 @@ export default function SignalsPage() { {/* 标题 */}
-

V5 信号引擎

-

CVD三轨 + ATR + VWAP + 大单阈值 → 做多/做空信号

+

V5.1 信号引擎

+

五层100分评分 + 市场拥挤度 + 环境确认

{(["BTC", "ETH"] as Symbol[]).map(s => ( @@ -291,6 +388,12 @@ export default function SignalsPage() { {/* 实时指标卡片 */} + {/* Market Indicators */} +
+

Market Indicators

+ +
+ {/* CVD三轨图 */}
@@ -313,9 +416,9 @@ export default function SignalsPage() {
{/* 说明 */} -
-

信号逻辑:CVD_fast方向 + CVD_mid方向 + VWAP位置 = 核心3条件。加分:ATR扩张(+25) + 无反向大单(+20) + 资金费率(+15)。

-

仓位:0-15分→2%仓 / 20-40分→5%仓 / 45-60分→8%仓。冷却10分钟,时间止损30分钟。

+
+

V5.1评分逻辑:方向层45分 + 拥挤层20分 + 环境层15分 + 确认层15分 + 辅助层5分(方向加速可额外+5)。

+

开仓档位:<60不开仓,60-74轻仓,75-84标准仓位,≥85允许加仓;信号冷却10分钟。

);