#!/usr/bin/env python3 # -*- coding: utf-8 -*- """strategy_router.py v5 — MoFin 策略动态路由(动态策略库,2026-08-13) 核心(老莫原则): - 策略全温区发信号(不留温区门控在策略内) - 策略-温区表现常态化记录(regime_perf.py / strategy_regime_perf),适用温区动态化 - 组合按当前温区自动激活/休眠策略:当前温区实测胜率高的策略优先 - 不漏历史策略(v_mr_sel trend_down 93%、v7.3 trend_up 94% 等被埋没的强者) 数据流: market_regime(三态) + regime_tracker(平滑K=5) → 当前温区 temp_band(rsi) → 温度/仓位乘数 regime_perf(实测) → 各策略温区表现 → strategy_weights.json(各策略权重/激活状态) """ import json import sys from pathlib import Path from datetime import datetime _SCRIPT_DIR = Path(__file__).resolve().parent sys.path.insert(0, str(_SCRIPT_DIR)) sys.path.insert(0, "/home/hmo/MoFin") OUT = Path("/home/hmo/MoFin/data/strategy_weights.json") DEFAULT_FAMILY = "mr" FAMILY_MAP = { "v_weak": "mr", "v_oversold": "mr", "v_mr": "mr", "v_mr2": "mr", "v_mr3": "mr", "v_mr4": "mr", "v_mr_sel": "mr", "v_lurk_v1": "mr", "v_lurk_v2": "mr", "v_lurk_v3": "mr", "s2_panic": "mr", "v_osc": "mr", "v_next": "trend", "v_next3": "trend", "v_next4": "trend", "v_next5": "trend", "v8.0": "trend", "v8.1": "trend", "v8.2": "trend", "v8.3": "trend", "v7.0": "trend", "v7.1": "trend", "v7.1b": "trend", "v7.2": "trend", "v7.3": "trend", "v6.0": "trend", "v6.1": "trend", "v6.2": "trend", "v9.0": "trend", "v9.1": "trend", "v9.2": "trend", "v_combo": "trend", "v1.0": "trend", "v2.0": "trend", "v3.0": "trend", "v4.0": "trend", "v4.0a": "trend", "v4.0b": "trend", "v4.0c": "trend", "v4.0d": "trend", "v4.0e": "trend", "v4.0f": "trend", "v4.0g": "trend", "v4.0h": "trend", "v4.1": "trend", "v5.0": "trend", "v11.0": "trend", "v11.1": "trend", "hk_mr1": "mr", # 港股深度超卖反弹(2026-08-14 港股归因研发,trend_down 主战场) } def load_regime(market='a'): """读取当前温区。market='a'(默认,A股 smoothed 顶层,行为不变) / 'hk'(港股 markets.hk)""" if market == 'hk': try: p = Path("/home/hmo/MoFin/data/market_regime_smoothed.json") if p.exists(): d = json.loads(p.read_text(encoding="utf-8")) mk = (d.get("markets") or {}).get("hk") or {} if mk.get("current_regime"): return {"regime": mk["current_regime"], "date": mk.get("current_date", "")} except Exception: pass try: from market_regime import load_market_regime return load_market_regime(market='hk') except Exception: return {"regime": "unknown"} try: p = Path("/home/hmo/MoFin/data/market_regime_smoothed.json") if p.exists(): d = json.loads(p.read_text(encoding="utf-8")) return {"regime": d.get("current_regime", "unknown"), "date": d.get("current_date", "")} except Exception: pass try: from market_regime import load_market_regime return load_market_regime() except Exception: return {"regime": "unknown"} def load_temp(market='a'): """读取当前温度。market='a'(默认,A股) / 'hk'(港股)""" try: from temp_band import get_market_temp return get_market_temp(market=market) except Exception: return {"band": "unknown", "rsi": None} def load_regime_perf(market='a'): """读取策略-温区表现。market='a'(默认,A股,行为不变) / 'hk'(港股策略)""" conn = None try: import sqlite3 conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db", timeout=30) conn.execute("PRAGMA busy_timeout=30000") rows = conn.execute( "SELECT strategy, regime, trades, win_rate, avg_pnl FROM strategy_regime_perf " "WHERE COALESCE(market,'a')=?", (market,) ).fetchall() result = {} for strat, reg, n, wr, pnl in rows: result.setdefault(strat, {})[reg] = {"trades": n, "win_rate": wr, "avg_pnl": pnl} return result except Exception: return {} finally: if conn: conn.close() 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 dep: # 已证伪策略绝不激活(数据驱动,2026-08-15) continue cur = regs.get(current_regime) best = max(regs.items(), key=lambda x: x[1].get("win_rate", 0)) best_wr = best[1].get("win_rate", 0) cur_wr = cur.get("win_rate") if cur else None # 2026-08-16 资格标准A(老莫):长期10y/近期2y/当下1y 适应温区年化>大盘 三项达标 + 手动可用 # (替代只看胜率≥50%的旧判定——旧标准会让年化-41%但胜率51%的策略激活) try: from strategy_qualify import evaluate_strategy, is_available, get_benchmarks _qbench = get_benchmarks('a') _qev = evaluate_strategy(name, 'a', best_regime=best[0], bench=_qbench) _qual_ok = bool(_qev and _qev.get("long_ok") and _qev.get("mid_ok") and _qev.get("short_ok")) _manual_ok = is_available(name) matched = _qual_ok and _manual_ok except Exception: matched = (best_wr >= 50) and (cur_wr is not None and cur_wr >= 50) # 兜底旧标准 family = FAMILY_MAP.get(name, DEFAULT_FAMILY) base = 1.0 if matched else 0.3 try: from temp_band import temp_multiplier mult = temp_multiplier(band, family) except Exception: mult = 0.8 weights[name] = { "weight": round(base * mult, 2), "regime": current_regime, "temp_band": band, "family": family, "matched": matched, "best_regime": best[0], "best_win_rate": best[1].get("win_rate", 0), "cur_win_rate": cur.get("win_rate") if cur else None, "cur_avg_pnl": cur.get("avg_pnl") if cur else None, "trades": cur.get("trades") if cur else 0, } return dict(sorted(weights.items(), key=lambda x: -x[1]["weight"])) def main(): # A股路由(完全不变) regime = load_regime() # A股温区 temp = load_temp() # A股温度 perf = load_regime_perf('a') # A股策略表现 weights = route(regime, temp, perf) out = { "state": regime.get("regime", "unknown"), "state_date": regime.get("date", ""), "temp_band": temp.get("band", "unknown"), "temp_rsi": temp.get("rsi"), "weights": weights, "active": [k for k, v in weights.items() if v["matched"]], "updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "note": "温区(平滑K=5)选策略类型 + 温度(rsi)乘数;策略全温区发信号,适用温区由实测动态决定", } # 港股路由(2026-08-14 新增,不影响A股)——港股组合按温区调度(hk_strategies 定义) try: hk_regime = load_regime('hk') hk_temp = load_temp('hk') from hk_strategies import strategies_for_regime hk_active = strategies_for_regime(hk_regime.get("regime", "unknown")) out["markets"] = {"hk": { "state": hk_regime.get("regime", "unknown"), "state_date": hk_regime.get("date", ""), "temp_band": hk_temp.get("band", "unknown"), "active": hk_active, }} except Exception as e: print(f"港股路由失败(不影响A股): {e}", flush=True) OUT.write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8") print(f"strategy_weights.json: 状态={out['state']} 温度={out['temp_band']}(rsi={out['temp_rsi']})") print(f"激活策略(A股): {out['active']}") if "markets" in out: hk = out["markets"]["hk"] print(f"港股温区={hk['state']} 激活策略(港股): {hk['active']}") print("权重排名(前15):") for name, w in list(weights.items())[:15]: mark = "✓" if w["matched"] else "观察" print(f" {name:<12} w={w['weight']:<5} {w['best_regime']:<11} 最佳{w['best_win_rate']:>3.0f}% {mark}") if __name__ == "__main__": main()