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

175 lines
6.5 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 -*-
"""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()