feat: 温区自适应全链路——动态策略库(regime_perf全策略×温区实测,含被埋没的v_mr_sel trend_down 93%/v7.3 trend_up 94%), router v5按当前温区激活, 选股(mr_scanner/predictive_oversold平滑温区门控)+买卖(price_monitor非激活抑制买入机会风控仍推)+重评(strategy_lifecycle平滑温区trend_down vs 原始choppy)全接入, 前端research Tab温区横幅+温度+激活策略

This commit is contained in:
xxm
2026-08-13 10:36:00 +08:00
parent a62b05f11d
commit 1094bf75af
4 changed files with 310 additions and 2 deletions
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""regime_gate.py — 温区门控共享工具(2026-08-13)
scanner/重评统一调用:读取当前温区(优先平滑 K=5,回退原始 market_regime),
判断某策略是否在当前温区激活。
用法:
from regime_gate import get_current_regime, is_strategy_active, strategy_enabled
rg = get_current_regime() # {"regime": "trend_down", "date": "..."}
ok = is_strategy_active("v_weak") # 该策略是否当前温区激活(读 strategy_weights.json
"""
import json
import sqlite3
from pathlib import Path
MOFIN_DATA = "/home/hmo/MoFin/data"
WEIGHTS_FILE = Path(MOFIN_DATA) / "strategy_weights.json"
SMOOTHED_FILE = Path(MOFIN_DATA) / "market_regime_smoothed.json"
DB = Path(MOFIN_DATA) / "mofin.db"
_cache_regime = None
_cache_weights = None
def get_current_regime(use_smoothed=True):
"""读取当前温区。优先平滑(K=5),回退原始 market_regime。返回 {"regime","date"}"""
global _cache_regime
if _cache_regime:
return _cache_regime
# 1. 平滑温区(regime_tracker K=5
if use_smoothed:
try:
if SMOOTHED_FILE.exists():
d = json.loads(SMOOTHED_FILE.read_text(encoding="utf-8"))
_cache_regime = {
"regime": d.get("current_regime", "unknown"),
"date": d.get("current_date", ""),
}
return _cache_regime
except Exception:
pass
# 2. 原始 market_regime 表
try:
conn = sqlite3.connect(str(DB), timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
row = conn.execute(
"SELECT date, regime FROM market_regime ORDER BY date DESC LIMIT 1"
).fetchone()
conn.close()
if row:
_cache_regime = {"regime": row[1], "date": row[0]}
return _cache_regime
except Exception:
pass
return {"regime": "unknown", "date": ""}
def _load_weights():
"""读取 strategy_weights.json(缓存)"""
global _cache_weights
if _cache_weights is not None:
return _cache_weights
try:
if WEIGHTS_FILE.exists():
_cache_weights = json.loads(WEIGHTS_FILE.read_text(encoding="utf-8"))
return _cache_weights
except Exception:
pass
_cache_weights = {}
return _cache_weights
def is_strategy_active(strategy_name):
"""该策略是否在当前温区激活(matched)。无数据默认激活"""
w = _load_weights()
if not w or not w.get("weights"):
return True
entry = w["weights"].get(strategy_name)
if entry is None:
return True # 不在权重表 → 默认激活(持仓管理类)
return entry.get("matched", True)
def strategy_enabled(strategy_name):
"""该策略权重>0(激活 + 非0乘数)"""
w = _load_weights()
if not w or not w.get("weights"):
return True
entry = w["weights"].get(strategy_name)
if entry is None:
return True
return entry.get("weight", 0) > 0
if __name__ == "__main__":
rg = get_current_regime()
print(f"当前温区: {rg['regime']} ({rg['date']})")
for s in ["v_weak", "v_oversold", "v_next4", "s2_panic", "v_mr_sel"]:
print(f" {s}: active={is_strategy_active(s)} enabled={strategy_enabled(s)}")
+174
View File
@@ -0,0 +1,174 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""regime_perf.py — 策略-温区表现常态化记录(2026-08-13)
记录策略在不同温区(trend_up/choppy/trend_down)的表现,供"适用温度"动态评估。
- 数据来源:strategy_research 回测 trades(按入场日归入温区)+ 实盘 strategy_tracking
- 表:strategy_regime_perfstrategy, regime, trades, win_rate, avg_pnl, updated_at
- 原则:策略全温区发信号(去门控后),记录各温区真实表现;适用温区是动态的,随数据更新
用法:
python3 regime_perf.py # 全量更新(从回测+实盘重算)
from regime_perf import get_regime_perf
"""
import sys
import json
import sqlite3
from pathlib import Path
from datetime import datetime
from collections import defaultdict
_SCRIPT_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(_SCRIPT_DIR))
sys.path.insert(0, "/home/hmo/MoFin")
DB = "/home/hmo/MoFin/data/mofin.db"
def load_all_strategies():
"""从 strategy_research 读取所有策略版本(含历史/表现不佳的——可能在特定温区能打)"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
rows = conn.execute("SELECT DISTINCT version FROM strategy_research ORDER BY version").fetchall()
conn.close()
return [r[0] for r in rows if r[0]]
# 关注的策略(动态:全部版本)
STRATEGIES = load_all_strategies()
def load_regime_map():
"""date -> regime(用平滑 regime_tracker 的周期反查更合理,这里用 market_regime 原始 + 手动按 K=5 平滑)
简化:直接用 market_regime 的 regime(与平滑 K=5 差异主要在边界几天,评估可接受)"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
rows = conn.execute("SELECT date, regime FROM market_regime").fetchall()
conn.close()
return dict(rows)
def get_trades_from_research(version):
"""从 strategy_research 取最新回测 trades"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
rows = conn.execute(
"SELECT results_json FROM strategy_research WHERE version=? ORDER BY period_tag DESC, created_at DESC LIMIT 1",
(version,)
).fetchall()
conn.close()
if not rows:
return []
try:
return json.loads(rows[0][0]).get("trades", [])
except Exception:
return []
def get_trades_from_tracking():
"""从实盘 strategy_tracking 取已平仓交易"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
rows = conn.execute(
"SELECT version_seq, tracked_at, theoretical_pnl FROM strategy_tracking WHERE status='closed'"
).fetchall()
conn.close()
result = []
for version_seq, tracked_at, pnl in rows:
if version_seq and tracked_at:
result.append({"version": version_seq, "entry_date": tracked_at[:10], "profit_pct": pnl})
return result
def compute(use_tracking=True):
"""计算所有策略各温区表现"""
regime_map = load_regime_map()
stats = defaultdict(lambda: defaultdict(lambda: {"n": 0, "win": 0, "pnl": 0}))
for v in STRATEGIES:
trades = get_trades_from_research(v)
for t in trades:
ed = t.get("entry_date", "")
if ed not in regime_map:
continue
reg = regime_map[ed]
pnl = t.get("profit_pct", 0) or 0
stats[v][reg]["n"] += 1
stats[v][reg]["pnl"] += pnl
if pnl > 0:
stats[v][reg]["win"] += 1
if use_tracking:
for t in get_trades_from_tracking():
v = t["version"]
if v not in stats:
stats[v] = defaultdict(lambda: {"n": 0, "win": 0, "pnl": 0})
ed = t["entry_date"]
if ed in regime_map:
reg = regime_map[ed]
pnl = t["profit_pct"] or 0
stats[v][reg]["n"] += 1
stats[v][reg]["pnl"] += pnl
if pnl > 0:
stats[v][reg]["win"] += 1
return stats
def save(stats):
"""写入 strategy_regime_perf 表(清空重建,保持与最新数据同步)"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
conn.execute("""
CREATE TABLE IF NOT EXISTS strategy_regime_perf (
strategy TEXT,
regime TEXT,
trades INTEGER,
win_rate REAL,
avg_pnl REAL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (strategy, regime)
)
""")
conn.execute("DELETE FROM strategy_regime_perf")
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
for v, regs in stats.items():
for reg, s in regs.items():
if s["n"] < 2:
continue # 样本太少不记录(>=2 给观察机会)
wr = s["win"] / s["n"] * 100
avg = s["pnl"] / s["n"]
conn.execute(
"INSERT OR REPLACE INTO strategy_regime_perf (strategy, regime, trades, win_rate, avg_pnl, updated_at) VALUES (?,?,?,?,?,?)",
(v, reg, s["n"], round(wr, 1), round(avg, 2), now)
)
conn.commit()
conn.close()
def main():
stats = compute(use_tracking=True)
save(stats)
# 打印
print("=== 策略-温区表现(strategy_regime_perf===")
conn = sqlite3.connect(DB, timeout=30)
rows = conn.execute("SELECT strategy, regime, trades, win_rate, avg_pnl FROM strategy_regime_perf ORDER BY strategy, regime").fetchall()
conn.close()
for r in rows:
print(f" {r[0]:<12} {r[1]:<12} {r[2]:>4}笔 胜率{r[3]:.0f}% 均盈{r[4]:+.2f}%")
def get_regime_perf(strategy=None):
"""读取策略-温区表现(供 router 动态适用温区)"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
if strategy:
rows = conn.execute(
"SELECT regime, trades, win_rate, avg_pnl FROM strategy_regime_perf WHERE strategy=?",
(strategy,)
).fetchall()
else:
rows = conn.execute(
"SELECT strategy, regime, trades, win_rate, avg_pnl FROM strategy_regime_perf"
).fetchall()
conn.close()
if strategy:
return {r[0]: {"trades": r[1], "win_rate": r[2], "avg_pnl": r[3]} for r in rows}
result = defaultdict(dict)
for r in rows:
result[r[0]][r[1]] = {"trades": r[2], "win_rate": r[3], "avg_pnl": r[4]}
return dict(result)
if __name__ == "__main__":
main()
+13 -2
View File
@@ -2035,8 +2035,19 @@ def reassess_with_context(code, name, price, cost, shares, current_action,
# 大盘市场阶段(market_regime)— 与回测 _load_index_ctx 同算法,趋势市放行追涨
market_regime = None
try:
import market_regime as _mr
market_regime = _mr.load_market_regime()
# 2026-08-13 平滑温区优先(K=5 regime_tracker),回退原始 market_regime
import sys as _sys
from pathlib import Path as _P
_sp = _P("/home/hmo/MoFin/deploy/profile-scripts")
if str(_sp) not in _sys.path:
_sys.path.insert(0, str(_sp))
from regime_gate import get_current_regime
_rg = get_current_regime()
if _rg and _rg.get("regime") != "unknown":
market_regime = _rg
else:
import market_regime as _mr
market_regime = _mr.load_market_regime()
except Exception:
pass # market_regime 不可用时不阻塞单只重评