From 2c8aa5ca5d0cb7eff277862992fdfe4535bff999 Mon Sep 17 00:00:00 2001 From: hmo Date: Wed, 12 Aug 2026 12:56:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=A0=94=E7=A9=B6Tab=E8=BF=911=E6=9C=88?= =?UTF-8?q?/6=E6=9C=88/1=E5=B9=B4=E6=95=B0=E6=8D=AE=E7=A9=BA=E2=80=94?= =?UTF-8?q?=E2=80=94list=5Fstrategies=E5=8A=A0=E5=88=87=E7=AA=97=E5=8F=A3f?= =?UTF-8?q?allback,=E4=BB=8E=E6=9C=80=E9=95=BF=E5=8C=BA=E9=97=B4trades?= =?UTF-8?q?=E6=8C=89=E6=97=B6=E9=97=B4=E7=AA=97=E5=8F=A3=E5=88=87=E9=87=8D?= =?UTF-8?q?=E7=AE=97summary(=E6=97=A0=E9=9C=80=E5=8D=95=E7=8B=AC=E5=9B=9E?= =?UTF-8?q?=E6=B5=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- strategy_lab.py | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/strategy_lab.py b/strategy_lab.py index 612150ab..f5243415 100644 --- a/strategy_lab.py +++ b/strategy_lab.py @@ -2088,6 +2088,41 @@ def list_strategies(period_tag=None): ON sr.id = latest.max_id ORDER BY sr.version """).fetchall() + + # ── 2026-08-12:1m/6m/1y 无独立回测记录时,从最长区间 trades 切时间窗口重算 summary ── + # 研究 Tab 期间筛选按 period_tag 查 DB,但 v_weak/v_oversold 只有 2y/5y/10y—— + # 选近1月/6月/1年时查不到记录显示空。改为从最长区间 trades 切窗口算,无需单独回测。 + PERIOD_SLICE_DAYS = {'1m': 30, '6m': 185, '1y': 365} + sliced_summaries = {} # version -> (summary, trades_count) + if period_tag in PERIOD_SLICE_DAYS: + days = PERIOD_SLICE_DAYS[period_tag] + have_period = {r['version'] for r in rows} + from datetime import datetime as _dt, timedelta as _td + for v in list(STRATEGIES.keys()) + [x for x in STRATEGY_DESCRIPTIONS.keys() if x not in STRATEGIES]: + if v in have_period: + continue # 已有该 period 记录,不用切 + row = conn.execute( + "SELECT results_json FROM strategy_research WHERE version=? ORDER BY " + "CASE COALESCE(period_tag,'2y') WHEN '10y' THEN 3 WHEN '5y' THEN 2 ELSE 1 END DESC, id DESC LIMIT 1", + (v,)).fetchone() + if not row: + continue + try: + res = json.loads(row[0]) + except Exception: + continue + trades = res.get('trades', []) + if not trades: + continue + max_date = max(t.get('entry_date', '') for t in trades) + try: + cutoff = (_dt.strptime(max_date, '%Y-%m-%d') - _td(days=days)).strftime('%Y-%m-%d') + except Exception: + continue + sliced = [t for t in trades if t.get('entry_date', '') >= cutoff] + if not sliced: + continue + sliced_summaries[v] = (calc_summary(sliced, 1000000), len(sliced), period_tag) conn.close() out = [] for r in rows: @@ -2104,22 +2139,25 @@ def list_strategies(period_tag=None): existing = {(d['version'], d['market']) for d in out} for v, s in STRATEGIES.items(): if (v, 'all') not in existing and not any(d['version'] == v for d in out): + # 2026-08-12:1m/6m/1y 优先用切窗口算的 summary(无独立记录时),否则空 + _ss, _tc, _pt = sliced_summaries.get(v, ({}, 0, None)) out.append({ 'version': v, 'name': s['name'], 'summary': s['summary'], 'hypothesis': s['hypothesis'], 'parent': s.get('parent'), 'config_json': json.dumps(s['config'], ensure_ascii=False), - 'summary_stats': {}, 'insights': [], 'created_at': s.get('created'), - 'market': 'all', + 'summary_stats': _ss, 'insights': [], 'created_at': s.get('created'), + 'market': 'all', 'period_tag': _pt or period_tag, 'trades_count': _tc, }) # 2026-08-11:补充仅在 STRATEGY_DESCRIPTIONS 的策略(如 v_oversold 预测扫描策略,无回测 config) for v, s in STRATEGY_DESCRIPTIONS.items(): if v not in STRATEGIES and not any(d['version'] == v for d in out): + _ss, _tc, _pt = sliced_summaries.get(v, ({}, 0, None)) out.append({ 'version': v, 'name': s.get('title', v), 'summary': s.get('algorithm', ''), 'hypothesis': s.get('rationale', ''), 'parent': None, 'config_json': '{}', - 'summary_stats': {}, 'insights': [], 'created_at': None, - 'market': 'all', + 'summary_stats': _ss, 'insights': [], 'created_at': None, + 'market': 'all', 'period_tag': _pt or period_tag, 'trades_count': _tc, }) out.sort(key=lambda x: (x['version'], x.get('market', 'all'))) # 2026-08-11:标记当前实盘/新策略(研究 Tab 拆分当前/历史区域)