fix: 证伪标记数据驱动——strategy_research加deprecated字段,前后端从数据读(替代硬编码),strategy_router排除证伪策略
This commit is contained in:
@@ -26,8 +26,6 @@ sys.path.insert(0, "/home/hmo/MoFin")
|
||||
OUT = Path("/home/hmo/MoFin/data/strategy_weights.json")
|
||||
|
||||
DEFAULT_FAMILY = "mr"
|
||||
# 已证伪策略(2026-08-15 对齐前端 DEPRECATED——证伪的策略绝不激活)
|
||||
DEPRECATED = {"v_mr", "v_mr_sel", "v_combo"}
|
||||
|
||||
FAMILY_MAP = {
|
||||
"v_weak": "mr", "v_oversold": "mr", "v_mr": "mr", "v_mr2": "mr", "v_mr3": "mr",
|
||||
@@ -114,10 +112,20 @@ def route(regime, temp, perf):
|
||||
current_regime = regime.get("regime", "unknown")
|
||||
band = temp.get("band", "unknown")
|
||||
weights = {}
|
||||
# 已证伪策略(从 strategy_research.deprecated 读,数据驱动——证伪绝不激活)
|
||||
dep = set()
|
||||
try:
|
||||
import sqlite3 as _sq
|
||||
_c = _sq.connect("/home/hmo/MoFin/data/mofin.db", timeout=5)
|
||||
dep = {r[0] for r in _c.execute(
|
||||
"SELECT DISTINCT version FROM strategy_research WHERE deprecated IS NOT NULL AND deprecated != ''")}
|
||||
_c.close()
|
||||
except Exception:
|
||||
pass
|
||||
for name, regs in perf.items():
|
||||
if not regs:
|
||||
continue
|
||||
if name in DEPRECATED: # 已证伪策略绝不激活(2026-08-15)
|
||||
if name in dep: # 已证伪策略绝不激活(数据驱动,2026-08-15)
|
||||
continue
|
||||
cur = regs.get(current_regime)
|
||||
best = max(regs.items(), key=lambda x: x[1].get("win_rate", 0))
|
||||
|
||||
@@ -567,11 +567,26 @@ def api_research_strategies():
|
||||
_c.close()
|
||||
except Exception:
|
||||
pass
|
||||
# 2026-08-15 证伪标记(数据驱动):strategy_research.deprecated
|
||||
_deprecated = {}
|
||||
try:
|
||||
import sqlite3 as _sq2
|
||||
_c2 = _sq2.connect(str(DATA_DIR / "mofin.db"), timeout=10)
|
||||
for _r in _c2.execute(
|
||||
"SELECT version, MAX(deprecated) FROM strategy_research "
|
||||
"WHERE deprecated IS NOT NULL AND deprecated != '' GROUP BY version"
|
||||
).fetchall():
|
||||
_deprecated[_r[0]] = _r[1]
|
||||
_c2.close()
|
||||
except Exception:
|
||||
pass
|
||||
for s in strats:
|
||||
s['description'] = STRATEGY_DESCRIPTIONS.get(s['version'], {})
|
||||
s['regime_winrates'] = _regime_winrates.get(s['version'], {})
|
||||
# current = 当前温区激活(替代旧的 CURRENT_VERSIONS 硬编码)
|
||||
s['current'] = s['version'] in _active_set
|
||||
# 2026-08-15 证伪数据驱动:从 strategy_research.deprecated 读(替代前端硬编码)
|
||||
s['deprecated'] = _deprecated.get(s['version'])
|
||||
|
||||
# 2026-08-13 关键修复:激活策略必须始终在列表中(即使该 period 无回测记录)
|
||||
# 否则 2y 等默认周期下激活策略缺失(只显示 v_oversold)。
|
||||
|
||||
+34
-8
@@ -2263,7 +2263,7 @@ async function loadStrategyRegimeBanner() {
|
||||
' · 温度: <b>' + bandIcon + ' ' + bandName + '</b>' +
|
||||
(rw.temp_rsi ? ' <span class="text-slate-500 text-xs">RSI=' + rw.temp_rsi + '</span>' : '') + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="text-xs text-slate-400 mb-1.5">以下策略在当前温区激活(适用温区由实测胜率动态决定):</div>' +
|
||||
'<div class="text-xs text-slate-400 mb-1.5">A股以下策略在当前温区激活(适用温区由实测胜率动态决定):</div>' +
|
||||
'<div class="flex flex-wrap gap-1.5">';
|
||||
if (activeList.length) {
|
||||
for (const n of activeList) {
|
||||
@@ -2275,7 +2275,36 @@ async function loadStrategyRegimeBanner() {
|
||||
} else {
|
||||
h += '<span class="text-xs text-slate-500">当前温区无激活策略</span>';
|
||||
}
|
||||
h += '</div></div>';
|
||||
h += '</div>';
|
||||
// ── 港股温区(2026-08-14,与A股分别呈现)──
|
||||
try {
|
||||
const hk = rw.markets && rw.markets.hk;
|
||||
if (hk && hk.state) {
|
||||
const hkIcon = {trend_up:'📈', choppy:'〰️', trend_down:'📉'}[hk.state] || '🌐';
|
||||
const hkName = {trend_up:'趋势市', choppy:'震荡市', trend_down:'下跌市'}[hk.state] || hk.state;
|
||||
const hkColor = hk.state==='trend_up' ? 'text-emerald-400' : hk.state==='choppy' ? 'text-amber-400' : 'text-red-400';
|
||||
const hkActive = (hk.active || []).slice(0, 8);
|
||||
h += '<div class="border-t border-sky-500/20 mt-2.5 pt-2.5">' +
|
||||
'<div class="flex items-center justify-between flex-wrap gap-2 mb-1.5">' +
|
||||
'<span class="text-sm font-semibold text-sky-300">🇭🇰 港股温区</span>' +
|
||||
'<span class="text-sm">' + hkIcon + ' <span class="font-bold ' + hkColor + '">' + hkName + '</span>' +
|
||||
(hk.state_date ? ' <span class="text-slate-500 text-xs">(' + hk.state_date + ')</span>' : '') +
|
||||
(hk.temp_band ? ' · <b>' + hk.temp_band + '</b>' : '') + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="text-xs text-slate-400 mb-1.5">港股激活策略(按港股温区独立调度):</div>' +
|
||||
'<div class="flex flex-wrap gap-1.5">';
|
||||
if (hkActive.length) {
|
||||
for (const n of hkActive) {
|
||||
h += '<span class="text-xs bg-sky-500/15 text-sky-300 border border-sky-500/30 rounded px-2 py-0.5">' +
|
||||
'<span class="font-mono font-bold">' + n + '</span></span>';
|
||||
}
|
||||
} else {
|
||||
h += '<span class="text-xs text-slate-500">当前港股温区无激活策略</span>';
|
||||
}
|
||||
h += '</div></div>';
|
||||
}
|
||||
} catch(e) {}
|
||||
h += '</div>';
|
||||
box.innerHTML = h;
|
||||
} catch(e) { box.innerHTML = ''; }
|
||||
}
|
||||
@@ -2337,11 +2366,8 @@ function renderStrategyTable(strategies) {
|
||||
// 条件格式色条:计算每列 min/max,单元格背景按相对大小画色条
|
||||
const CURRENT_STRATEGY = 'v_next4';
|
||||
const CURRENT_FAMILY = new Set(['v_weak', 's2_panic', 'v_next4']);
|
||||
const DEPRECATED = {
|
||||
'v_mr': '已证伪§12: 2018熊市依赖, 去2018后avg+2.14%/wr49.8%掷硬币, 组合回撤45%',
|
||||
'v_mr_sel': '已证伪§12: 144笔中54%集中在2018-10, 单一事件依赖不可信',
|
||||
'v_combo': '未采纳: 2年+47.9%但5年验证2023/24震荡转折年亏损(B动量族假突破)',
|
||||
};
|
||||
// 证伪标记数据驱动(2026-08-15):从 API s.deprecated 读,替代前端硬编码
|
||||
const DEPRECATED = {};
|
||||
const ranges = {};
|
||||
const cols = ['composite','universality_score','full_return_pct','capital_final','cagr_pct','total_trades','avg_hold_days','win_rate','avg_profit_pct','sharpe_ratio','profit_factor','portfolio_max_dd_pct'];
|
||||
for (const c of cols) {
|
||||
@@ -2400,7 +2426,7 @@ function renderStrategyTable(strategies) {
|
||||
const hasResult = st.total_trades != null;
|
||||
const smallSample = hasResult && st.total_trades < 40;
|
||||
const isCurrent = s.current === true;
|
||||
const deprecatedReason = DEPRECATED[s.version];
|
||||
const deprecatedReason = s.deprecated; // 数据驱动(2026-08-15,从API读)
|
||||
// 温区展开:有 regime_winrates 则每温区一行;无则单行("整体")
|
||||
const rws = s.regime_winrates || {};
|
||||
const regimes = Object.keys(rws).length ? Object.keys(rws).sort() : ['overall'];
|
||||
|
||||
Reference in New Issue
Block a user