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:
@@ -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)}")
|
||||
@@ -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_perf(strategy, 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()
|
||||
@@ -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 不可用时不阻塞单只重评
|
||||
|
||||
|
||||
Reference in New Issue
Block a user