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
@@ -109,6 +109,71 @@ def regime_days_in_window(conn, market, d_min, d_max):
return {r[0]: r[1] for r in rows}, total
# ── 2026-08-18 切窗周期(1m/6m/1y):从最长周期 trades 切窗→温区聚合 ──
# 老莫:1m/6m/1y温区数据须有真实差异 + 每天定时刷新(server只读表,快且新)
SLICE_DAYS_2 = {'1m': 30, '6m': 185, '1y': 365}
def process_slice_period(conn, market, period_tag):
"""对切窗周期:所有策略从最长周期trades切出窗口算温区表现,写表"""
days = SLICE_DAYS_2.get(period_tag)
if not days:
return 0
from datetime import timedelta
rmap = dict(conn.execute(
"SELECT date, regime FROM market_regime WHERE market=?", (market,)).fetchall())
if not rmap:
return 0
ver_rows = conn.execute(
"SELECT DISTINCT version FROM strategy_research WHERE COALESCE(market,'a')=?",
(market,)).fetchall()
written = 0
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
for (v,) in ver_rows:
# 最长周期 trades
rr = conn.execute(
"SELECT results_json FROM strategy_research WHERE COALESCE(market,'a')=? AND version=? "
"ORDER BY CASE COALESCE(period_tag,'2y') WHEN '10y' THEN 3 WHEN '5y' THEN 2 ELSE 1 END DESC, id DESC LIMIT 1",
(market, v)).fetchone()
if not rr or not rr[0]:
continue
try:
trades = json.loads(rr[0]).get("trades", [])
except Exception:
continue
if not trades:
continue
max_date = max(t.get('entry_date', '') for t in trades)
cutoff = (datetime.strptime(max_date, '%Y-%m-%d') - timedelta(days=days)).strftime('%Y-%m-%d')
sliced = [t for t in trades if t.get('entry_date', '') >= cutoff]
if not sliced:
continue
by_regime = defaultdict(list)
for t in sliced:
ed = t.get("entry_date", "")
if ed in rmap:
by_regime[rmap[ed]].append(t)
for reg, reg_trades in by_regime.items():
if not reg_trades:
continue
wins = sum(1 for t in reg_trades if (t.get('profit_pct') or 0) > 0)
pnl = sum(t.get('profit_pct') or 0 for t in reg_trades)
hold = sum(t.get('hold_days') or 0 for t in reg_trades)
n = len(reg_trades)
conn.execute(
"INSERT OR REPLACE INTO strategy_regime_perf_by_period "
"(strategy, market, regime, period_tag, 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, universality_months, universality_years, "
"universality_valid_years, universality_score, universality_leave1, updated_at) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(v, market, reg, period_tag, n, round(wins / n * 100, 1), round(pnl / n, 2),
round(hold / n, 1), round(pnl, 1), None, None, 0, n, None, None,
0, 0, 0, 0, 0, now))
written += 1
return written
def process_period(conn, market, period_tag):
"""处理单个周期:所有策略的温区表现,写入 strategy_regime_perf_by_period"""
# 温区映射
@@ -220,7 +285,10 @@ def main():
conn.execute("DELETE FROM strategy_regime_perf_by_period WHERE market=?", (market,))
total = 0
for pt in periods:
n = process_period(conn, market, pt)
if pt in SLICE_DAYS_2:
n = process_slice_period(conn, market, pt)
else:
n = process_period(conn, market, pt)
print(f"[{market}][{pt}] 写入 {n}", flush=True)
total += n
conn.commit()