From 740ede6a5a0abbc91a42fd10f45ee46c424b20b6 Mon Sep 17 00:00:00 2001 From: xxm Date: Tue, 18 Aug 2026 09:09:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201m/6m/1y=E6=B8=A9=E5=8C=BA=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=88=87=E7=AA=97=E8=81=9A=E5=90=88(=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=9B=9E=E9=80=801y)=E2=80=94=E2=80=94=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=91=A8=E6=9C=9F=E6=B8=A9=E5=8C=BA=E8=A1=A8=E7=8E=B0?= =?UTF-8?q?=E6=9C=89=E7=9C=9F=E5=AE=9E=E5=B7=AE=E5=BC=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/server.py b/server.py index 1e44afab..b77ade8c 100644 --- a/server.py +++ b/server.py @@ -50,10 +50,80 @@ def _compute_regime_winrates_cached(pt, _approx_univ): 无对应周期记录时回退到最近更长周期(1m/6m→1y,2y→2y,5y→5y,10y→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_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")