Files
MoFin/deploy/profile-scripts/strategy_router.py
T

150 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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",
}
def load_regime():
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():
try:
from temp_band import get_market_temp
return get_market_temp()
except Exception:
return {"band": "unknown", "rsi": None}
def load_regime_perf():
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"
).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 = {}
for name, regs in perf.items():
if not regs:
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
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():
regime = load_regime()
temp = load_temp()
perf = load_regime_perf()
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)乘数;策略全温区发信号,适用温区由实测动态决定",
}
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"激活策略: {out['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()