#!/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()