feat: 温区数据定时预计算——1m/6m/1y切窗由regime_perf_by_period每天生成+server直读(不再回退1y)

This commit is contained in:
xxm
2026-08-18 09:30:35 +08:00
parent 740ede6a5a
commit 7c8a788bfc
2 changed files with 70 additions and 72 deletions
+1 -71
View File
@@ -50,80 +50,10 @@ def _compute_regime_winrates_cached(pt, _approx_univ):
无对应周期记录时回退到最近更长周期(1m/6m→1y2y→2y5y→5y10y→10y)。
"""
import sqlite3 as _sq
from datetime import datetime as _dt, timedelta as _td
from collections import defaultdict
_pt_chain = {'1m': '1y', '6m': '1y', '1y': '1y', '2y': '2y', '5y': '5y', '10y': '10y'}
_pt_chain = {'1m': '1m', '6m': '6m', '1y': '1y', '2y': '2y', '5y': '5y', '10y': '10y'} # 2026-08-18 切窗周期直读(已预计算)
_pt_use = _pt_chain.get(pt or '2y', '2y')
_regime_winrates = {}
# ── 2026-08-18 切窗周期(1m/6m/1y):从 trades 按 entry_date 切窗+温区聚合 ──
# 原直接回退1y表→1m/6m/1y温区数据完全相同(老莫反馈毫无差别)。
SLICE_DAYS = {'1m': 30, '6m': 185, '1y': 365}
if pt in SLICE_DAYS:
try:
_c = _sq.connect(str(DATA_DIR / "mofin.db"), timeout=20)
_c.execute("PRAGMA busy_timeout=20000")
# 温区map date->regime
_rmap = {}
for _rd in _c.execute("SELECT date, regime FROM market_regime WHERE market='a'"):
_rmap[_rd[0]] = _rd[1]
# 各策略 trades(直接查 strategy_research 最新,含 entry_date/profit_pct
_rows = _c.execute(
"SELECT version, market, results_json FROM strategy_research "
"ORDER BY LENGTH(COALESCE(period_tag,'2y')) DESC, COALESCE(period_tag,'2y') DESC, id DESC").fetchall()
_c.close()
_seen = set()
_today = _dt.now().strftime("%Y-%m-%d")
_cutoff = (_dt.now() - _td(days=SLICE_DAYS[pt])).strftime("%Y-%m-%d")
for _ver, _mkt, _rj in _rows:
if _ver in _seen:
continue
_seen.add(_ver)
try:
_trs = json.loads(_rj).get("trades", []) if _rj else []
except Exception:
continue
if not _trs:
continue
# 按温区分组当前窗口 trades
_agg = defaultdict(lambda: {"trades": 0, "wins": 0, "pnl": 0.0, "holds": 0.0})
_mkt0 = (_mkt or "all")
for _t in _trs:
_ed = _t.get("entry_date", "")
if not _ed or _ed < _cutoff:
continue
_ed_short = _ed[:10]
_rg0 = _rmap.get(_ed_short)
if not _rg0:
continue
_g = _agg[_rg0]
_g["trades"] += 1
_p = _t.get("profit_pct") or 0
if _p > 0:
_g["wins"] += 1
_g["pnl"] += _p
_g["holds"] += _t.get("hold_days") or 0
for _rg0, _g in _agg.items():
_n = _g["trades"]
if _n == 0:
continue
_wr = _g["wins"] / _n * 100
_ap = _g["pnl"] / _n
_ah = _g["holds"] / _n
_regime_winrates.setdefault(_ver, {})[_rg0] = {
"trades": _n, "win_rate": round(_wr, 1), "avg_pnl": round(_ap, 2),
"avg_hold_days": round(_ah, 1), "total_return_pct": round(_ap * _n, 1),
"cagr_pct": None, "max_dd_pct": None, "capital_final": 0,
"positions_taken": _n, "sharpe_ratio": None, "profit_factor": None,
"period_tag": pt,
"portfolio": {"cagr_pct": None, "total_return_pct": round(_ap * _n, 1),
"portfolio_max_dd_pct": None, "capital_final": 0,
"positions_taken": _n, "sharpe_ratio": None, "profit_factor": None},
"universality": _approx_univ(_ver, _rg0, _n),
}
return _regime_winrates # 切窗周期已按真实周期算,不再回退1y表
except Exception:
pass # 失败则回退下面表逻辑
try:
_c = _sq.connect(str(DATA_DIR / "mofin.db"), timeout=10)
_c.execute("PRAGMA busy_timeout=10000")