fix: V5.2 scoring uses strategy weights, total capped at 100
Backend: - Each layer score scaled by strategy config weights - direction: 0~40, crowding: 0~18, FR: 0~5, environment: 0~12 - confirmation: 0~15, liquidation: 0~5, auxiliary: 0~5 - total_score clamped to 0~100 - factors include max field for frontend Frontend: - V5.2 signals page reads max from factors
This commit is contained in:
parent
5849bf6522
commit
05673c0850
@ -556,24 +556,49 @@ class SymbolState:
|
|||||||
else:
|
else:
|
||||||
aux_score = 0
|
aux_score = 0
|
||||||
|
|
||||||
total_score = direction_score + accel_bonus + crowding_score + fr_score + environment_score + confirmation_score + liq_score + aux_score
|
# 按策略配置的权重缩放各层分数
|
||||||
|
weights = strategy_cfg.get("weights", {})
|
||||||
|
w_direction = weights.get("direction", 45)
|
||||||
|
w_crowding = weights.get("crowding", 20)
|
||||||
|
w_fr = weights.get("funding_rate", 0)
|
||||||
|
w_environment = weights.get("environment", 15)
|
||||||
|
w_confirmation = weights.get("confirmation", 15)
|
||||||
|
w_liq = weights.get("liquidation", 0)
|
||||||
|
w_auxiliary = weights.get("auxiliary", 5)
|
||||||
|
|
||||||
|
# 原始评分范围: direction 0~45, crowding 0~20, environment 0~15, confirmation 0~15, auxiliary 0~5
|
||||||
|
# FR 0~5 (or -5~5), Liq 0~5
|
||||||
|
# 缩放到配置权重
|
||||||
|
scaled_direction = round(direction_score / 45 * w_direction) + accel_bonus
|
||||||
|
scaled_crowding = round(crowding_score / 20 * w_crowding)
|
||||||
|
scaled_fr = fr_score if w_fr > 0 else 0 # FR already 0~5 range, matches w_fr=5
|
||||||
|
scaled_environment = round(environment_score / 15 * w_environment)
|
||||||
|
scaled_confirmation = round(confirmation_score / 15 * w_confirmation)
|
||||||
|
scaled_liq = liq_score if w_liq > 0 else 0 # Liq already 0~5 range, matches w_liq=5
|
||||||
|
scaled_auxiliary = round(aux_score / 5 * w_auxiliary)
|
||||||
|
|
||||||
|
total_score = scaled_direction + scaled_crowding + scaled_fr + scaled_environment + scaled_confirmation + scaled_liq + scaled_auxiliary
|
||||||
|
total_score = max(0, min(total_score, 100)) # clamp 0~100
|
||||||
|
|
||||||
result["score"] = total_score
|
result["score"] = total_score
|
||||||
result["direction"] = direction
|
result["direction"] = direction
|
||||||
result["factors"] = {
|
result["factors"] = {
|
||||||
"direction": {
|
"direction": {
|
||||||
"score": direction_score,
|
"score": scaled_direction,
|
||||||
|
"max": w_direction,
|
||||||
"cvd_fast": 15 if ((direction == "LONG" and cvd_fast > 0) or (direction == "SHORT" and cvd_fast < 0)) else 0,
|
"cvd_fast": 15 if ((direction == "LONG" and cvd_fast > 0) or (direction == "SHORT" and cvd_fast < 0)) else 0,
|
||||||
"cvd_mid": 15 if ((direction == "LONG" and cvd_mid > 0) or (direction == "SHORT" and cvd_mid < 0)) else 0,
|
"cvd_mid": 15 if ((direction == "LONG" and cvd_mid > 0) or (direction == "SHORT" and cvd_mid < 0)) else 0,
|
||||||
"p99_flow": 15 if has_aligned_p99 else (10 if not has_adverse_p99 else 0),
|
"p99_flow": 15 if has_aligned_p99 else (10 if not has_adverse_p99 else 0),
|
||||||
"accel_bonus": accel_bonus,
|
"accel_bonus": accel_bonus,
|
||||||
},
|
},
|
||||||
"crowding": {"score": crowding_score, "long_short_ratio": ls_score, "top_trader_position": top_trader_score},
|
"crowding": {"score": scaled_crowding, "max": w_crowding, "long_short_ratio": ls_score, "top_trader_position": top_trader_score},
|
||||||
"environment": {"score": environment_score, "open_interest_hist": oi_change},
|
"environment": {"score": scaled_environment, "max": w_environment, "open_interest_hist": oi_change},
|
||||||
"confirmation": {"score": confirmation_score},
|
"confirmation": {"score": scaled_confirmation, "max": w_confirmation},
|
||||||
"auxiliary": {"score": aux_score, "coinbase_premium": coinbase_premium},
|
"auxiliary": {"score": scaled_auxiliary, "max": w_auxiliary, "coinbase_premium": coinbase_premium},
|
||||||
"funding_rate": {"score": fr_score, "value": funding_rate},
|
"funding_rate": {"score": scaled_fr, "max": w_fr, "value": funding_rate},
|
||||||
"liquidation": {
|
"liquidation": {
|
||||||
"score": liq_score,
|
"score": scaled_liq,
|
||||||
|
"max": w_liq,
|
||||||
"long_usd": liq_data.get("long_usd", 0) if liq_data else 0,
|
"long_usd": liq_data.get("long_usd", 0) if liq_data else 0,
|
||||||
"short_usd": liq_data.get("short_usd", 0) if liq_data else 0,
|
"short_usd": liq_data.get("short_usd", 0) if liq_data else 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -39,13 +39,13 @@ interface LatestIndicator {
|
|||||||
signal: string | null;
|
signal: string | null;
|
||||||
tier?: "light" | "standard" | "heavy" | null;
|
tier?: "light" | "standard" | "heavy" | null;
|
||||||
factors?: {
|
factors?: {
|
||||||
direction?: { score?: number };
|
direction?: { score?: number; max?: number };
|
||||||
crowding?: { score?: number };
|
crowding?: { score?: number; max?: number };
|
||||||
environment?: { score?: number };
|
environment?: { score?: number; max?: number };
|
||||||
confirmation?: { score?: number };
|
confirmation?: { score?: number; max?: number };
|
||||||
auxiliary?: { score?: number };
|
auxiliary?: { score?: number; max?: number };
|
||||||
funding_rate?: { score?: number; value?: number };
|
funding_rate?: { score?: number; max?: number; value?: number };
|
||||||
liquidation?: { score?: number; long_usd?: number; short_usd?: number };
|
liquidation?: { score?: number; max?: number; long_usd?: number; short_usd?: number };
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,13 +325,13 @@ function IndicatorCards({ symbol }: { symbol: Symbol }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 space-y-1">
|
<div className="mt-2 space-y-1">
|
||||||
<LayerScore label="方向" score={data.factors?.direction?.score ?? Math.min(Math.round(data.score * 0.40), 40)} max={40} colorClass="bg-blue-600" />
|
<LayerScore label="方向" score={data.factors?.direction?.score ?? 0} max={data.factors?.direction?.max ?? 40} colorClass="bg-blue-600" />
|
||||||
<LayerScore label="拥挤" score={data.factors?.crowding?.score ?? Math.min(Math.round(data.score * 0.20), 20)} max={20} colorClass="bg-violet-600" />
|
<LayerScore label="拥挤" score={data.factors?.crowding?.score ?? 0} max={data.factors?.crowding?.max ?? 18} colorClass="bg-violet-600" />
|
||||||
<LayerScore label="FR" score={data.factors?.funding_rate?.score ?? 0} max={5} colorClass="bg-cyan-600" />
|
<LayerScore label="FR" score={data.factors?.funding_rate?.score ?? 0} max={data.factors?.funding_rate?.max ?? 5} colorClass="bg-cyan-600" />
|
||||||
<LayerScore label="环境" score={data.factors?.environment?.score ?? Math.min(Math.round(data.score * 0.15), 15)} max={15} colorClass="bg-emerald-600" />
|
<LayerScore label="环境" score={data.factors?.environment?.score ?? 0} max={data.factors?.environment?.max ?? 12} colorClass="bg-emerald-600" />
|
||||||
<LayerScore label="确认" score={data.factors?.confirmation?.score ?? Math.min(Math.round(data.score * 0.15), 15)} max={15} colorClass="bg-amber-500" />
|
<LayerScore label="确认" score={data.factors?.confirmation?.score ?? 0} max={data.factors?.confirmation?.max ?? 15} colorClass="bg-amber-500" />
|
||||||
<LayerScore label="清算" score={data.factors?.liquidation?.score ?? 0} max={5} colorClass="bg-orange-500" />
|
<LayerScore label="清算" score={data.factors?.liquidation?.score ?? 0} max={data.factors?.liquidation?.max ?? 5} colorClass="bg-orange-500" />
|
||||||
<LayerScore label="辅助" score={data.factors?.auxiliary?.score ?? Math.min(Math.round(data.score * 0.05), 5)} max={5} colorClass="bg-slate-500" />
|
<LayerScore label="辅助" score={data.factors?.auxiliary?.score ?? 0} max={data.factors?.auxiliary?.max ?? 5} colorClass="bg-slate-500" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -109,3 +109,18 @@
|
|||||||
2026-03-02 00:22:37,395 [INFO] signal-engine: [XRPUSDT] 🚨 信号[v52_8signals]: LONG score=77 price=1.4
|
2026-03-02 00:22:37,395 [INFO] signal-engine: [XRPUSDT] 🚨 信号[v52_8signals]: LONG score=77 price=1.4
|
||||||
2026-03-02 00:23:08,756 [INFO] signal-engine: [ETHUSDT] 🚨 信号[v51_baseline]: LONG score=85 price=1943.5
|
2026-03-02 00:23:08,756 [INFO] signal-engine: [ETHUSDT] 🚨 信号[v51_baseline]: LONG score=85 price=1943.5
|
||||||
2026-03-02 00:23:08,756 [INFO] signal-engine: [ETHUSDT] 🚨 信号[v52_8signals]: LONG score=90 price=1943.5
|
2026-03-02 00:23:08,756 [INFO] signal-engine: [ETHUSDT] 🚨 信号[v52_8signals]: LONG score=90 price=1943.5
|
||||||
|
2026-03-02 00:25:13,809 [INFO] signal-engine: 已加载策略配置: v51_baseline, v52_8signals
|
||||||
|
2026-03-02 00:25:17,084 [INFO] signal-engine: [BTCUSDT] 冷启动完成: 加载467,836条历史数据 (窗口=4h)
|
||||||
|
2026-03-02 00:25:20,192 [INFO] signal-engine: [ETHUSDT] 冷启动完成: 加载457,043条历史数据 (窗口=4h)
|
||||||
|
2026-03-02 00:25:20,634 [INFO] signal-engine: [XRPUSDT] 冷启动完成: 加载65,387条历史数据 (窗口=4h)
|
||||||
|
2026-03-02 00:25:21,081 [INFO] signal-engine: [SOLUSDT] 冷启动完成: 加载67,363条历史数据 (窗口=4h)
|
||||||
|
2026-03-02 00:25:21,081 [INFO] signal-engine: === Signal Engine (PG) 启动完成 ===
|
||||||
|
2026-03-02 00:25:21,333 [INFO] signal-engine: [BTCUSDT] 🚨 信号[v51_baseline]: LONG score=90 price=65913.4
|
||||||
|
2026-03-02 00:25:21,334 [INFO] signal-engine: [BTCUSDT] 🚨 信号[v52_8signals]: LONG score=90 price=65913.4
|
||||||
|
2026-03-02 00:25:21,612 [INFO] signal-engine: [ETHUSDT] 🚨 信号[v51_baseline]: LONG score=90 price=1943.7
|
||||||
|
2026-03-02 00:25:21,612 [INFO] signal-engine: [ETHUSDT] 🚨 信号[v52_8signals]: LONG score=90 price=1943.7
|
||||||
|
2026-03-02 00:25:21,693 [INFO] signal-engine: [XRPUSDT] 🚨 信号[v51_baseline]: LONG score=87 price=1.4
|
||||||
|
2026-03-02 00:25:21,693 [INFO] signal-engine: [XRPUSDT] 🚨 信号[v52_8signals]: LONG score=82 price=1.4
|
||||||
|
2026-03-02 00:25:21,780 [INFO] signal-engine: [SOLUSDT] 🚨 信号[v51_baseline]: LONG score=92 price=83.8
|
||||||
|
2026-03-02 00:25:21,780 [INFO] signal-engine: [SOLUSDT] 🚨 信号[v52_8signals]: LONG score=87 price=83.8
|
||||||
|
2026-03-02 00:25:53,073 [INFO] signal-engine: 冷启动保护期结束,模拟盘开仓已启用
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user