#!/usr/bin/env python3 # -*- coding: utf-8 -*- """regime_perf.py v2 — 策略-温区表现常态化记录(2026-08-13 温区级组合模拟) 记录策略在不同温区(trend_up/choppy/trend_down)的表现,含【温区级组合模拟】—— 每个策略×温区,把该温区 trades 跑 portfolio_sim(100万本金/10槽/含费), 得到温区级 total_return/cagr/max_dd/capital_final/positions_taken/sharpe/profit_factor。 表 strategy_regime_perf 扩展列(温区级组合指标): strategy, regime, trades, win_rate, avg_pnl, avg_hold_days, total_return_pct, cagr_pct, portfolio_max_dd_pct, capital_final, positions_taken, sharpe_ratio, profit_factor, updated_at """ import sys import json import math 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(): 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]] def load_regime_map(): 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): 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 portfolio_sim_wrap(trades, capital=1000000, max_positions=10): """温区 trades → 组合模拟(复用 strategy_lab.portfolio_sim)""" if not trades: return {} try: from strategy_lab import portfolio_sim return portfolio_sim(trades, capital=capital, max_positions=max_positions, cost=True) except Exception: return {} def calc_extra(trades): """从 trades 算温区级 win_rate/avg_pnl/avg_hold/sharpe/profit_factor""" if not trades: return {} profits = [t.get("profit_pct", 0) for t in trades] wins = [p for p in profits if p > 0] losses = [p for p in profits if p <= 0] win_rate = len(wins) / len(profits) * 100 if profits else 0 avg_p = sum(profits) / len(profits) if profits else 0 avg_w = sum(wins) / len(wins) if wins else 0 avg_l = abs(sum(losses) / len(losses)) if losses else 1 pf = avg_w / avg_l if avg_l > 0 else 0 mean_r = avg_p / 100 std_r = math.sqrt(sum((p / 100 - mean_r) ** 2 for p in profits) / (len(profits) - 1)) if len(profits) > 1 else 0 sharpe = mean_r / std_r * math.sqrt(252) if std_r > 0 else 0 holds = [t.get("hold_days", 0) for t in trades if t.get("hold_days")] avg_hold = sum(holds) / len(holds) if holds else 0 return { "win_rate": round(win_rate, 1), "avg_pnl": round(avg_p, 2), "avg_hold_days": round(avg_hold, 1), "sharpe_ratio": round(sharpe, 2), "profit_factor": round(pf, 2), } def main(): regime_map = load_regime_map() strategies = load_all_strategies() print(f"策略数: {len(strategies)}") conn = sqlite3.connect(DB, timeout=60) conn.execute("PRAGMA busy_timeout=60000") conn.execute(""" CREATE TABLE IF NOT EXISTS strategy_regime_perf ( strategy TEXT, regime TEXT, trades INTEGER, win_rate REAL, avg_pnl REAL, avg_hold_days REAL, total_return_pct REAL, cagr_pct REAL, portfolio_max_dd_pct REAL, capital_final REAL, positions_taken INTEGER, sharpe_ratio REAL, profit_factor REAL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (strategy, regime) ) """) conn.execute("DELETE FROM strategy_regime_perf") written = 0 now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") for v in strategies: trades = get_trades_from_research(v) if not trades: continue # 按温区分组 by_regime = defaultdict(list) for t in trades: ed = t.get("entry_date", "") if ed in regime_map: by_regime[regime_map[ed]].append(t) for reg, reg_trades in by_regime.items(): if len(reg_trades) < 2: continue extra = calc_extra(reg_trades) sim = portfolio_sim_wrap(reg_trades) if not sim: continue conn.execute( """INSERT OR REPLACE INTO strategy_regime_perf (strategy, regime, trades, win_rate, avg_pnl, avg_hold_days, total_return_pct, cagr_pct, portfolio_max_dd_pct, capital_final, positions_taken, sharpe_ratio, profit_factor, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", (v, reg, len(reg_trades), extra.get("win_rate"), extra.get("avg_pnl"), extra.get("avg_hold_days"), sim.get("total_return_pct"), sim.get("cagr_pct"), sim.get("portfolio_max_dd_pct"), sim.get("capital_final"), sim.get("positions_taken"), extra.get("sharpe_ratio"), extra.get("profit_factor"), now) ) written += 1 conn.commit() conn.close() print(f"写入 strategy_regime_perf {written} 条(含温区级组合模拟)") # 打印样例 conn = sqlite3.connect(DB, timeout=30) rows = conn.execute( "SELECT strategy, regime, trades, win_rate, cagr_pct, capital_final FROM strategy_regime_perf " "WHERE strategy IN ('v_oversold','v_mr_sel','s2_panic') ORDER BY strategy, regime" ).fetchall() conn.close() for r in rows: print(f" {r[0]:<12} {r[1]:<12} {r[2]:>4}笔 胜率{r[3]:>5.1f}% 年化{r[4]:>6.1f}% 资产{r[5]:>12.0f}") if __name__ == "__main__": main()