From 6ad5c30ab1bcd70faa9015dd91a02181e95f3175 Mon Sep 17 00:00:00 2001 From: hmo Date: Sat, 15 Aug 2026 08:21:06 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AF=81=E4=BC=AA=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=A9=B1=E5=8A=A8=E2=80=94=E2=80=94strategy?= =?UTF-8?q?=5Fresearch=E5=8A=A0deprecated=E5=AD=97=E6=AE=B5,=E5=89=8D?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E4=BB=8E=E6=95=B0=E6=8D=AE=E8=AF=BB(?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3=E7=A1=AC=E7=BC=96=E7=A0=81),strategy=5Froute?= =?UTF-8?q?r=E6=8E=92=E9=99=A4=E8=AF=81=E4=BC=AA=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/profile-scripts/strategy_router.py | 14 ++++++-- server.py | 15 ++++++++ static/index.html | 42 ++++++++++++++++++----- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/deploy/profile-scripts/strategy_router.py b/deploy/profile-scripts/strategy_router.py index a826ddd9..93518407 100644 --- a/deploy/profile-scripts/strategy_router.py +++ b/deploy/profile-scripts/strategy_router.py @@ -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)) diff --git a/server.py b/server.py index ae4f8e73..fa05da56 100644 --- a/server.py +++ b/server.py @@ -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)。 diff --git a/static/index.html b/static/index.html index 33e33cf0..64df84cf 100644 --- a/static/index.html +++ b/static/index.html @@ -2263,7 +2263,7 @@ async function loadStrategyRegimeBanner() { ' · 温度: ' + bandIcon + ' ' + bandName + '' + (rw.temp_rsi ? ' RSI=' + rw.temp_rsi + '' : '') + '' + '' + - '
以下策略在当前温区激活(适用温区由实测胜率动态决定):
' + + '
A股以下策略在当前温区激活(适用温区由实测胜率动态决定):
' + '
'; if (activeList.length) { for (const n of activeList) { @@ -2275,7 +2275,36 @@ async function loadStrategyRegimeBanner() { } else { h += '当前温区无激活策略'; } - h += '
'; + h += ''; + // ── 港股温区(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 += '
' + + '
' + + '🇭🇰 港股温区' + + '' + hkIcon + ' ' + hkName + '' + + (hk.state_date ? ' (' + hk.state_date + ')' : '') + + (hk.temp_band ? ' · ' + hk.temp_band + '' : '') + '' + + '
' + + '
港股激活策略(按港股温区独立调度):
' + + '
'; + if (hkActive.length) { + for (const n of hkActive) { + h += '' + + '' + n + ''; + } + } else { + h += '当前港股温区无激活策略'; + } + h += '
'; + } + } catch(e) {} + h += ''; 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'];