From b1ede2747e7d310675403b3bed16c1aabb7c6aeb Mon Sep 17 00:00:00 2001 From: xxm Date: Sat, 15 Aug 2026 23:42:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AD=96=E7=95=A5=E5=8A=A0best=5Fregim?= =?UTF-8?q?e(=E9=80=82=E5=BA=94=E6=B8=A9=E5=8C=BA)=E5=AD=97=E6=AE=B5+?= =?UTF-8?q?=E6=B7=98=E6=B1=B0=E5=88=A4=E5=AE=9A=E6=94=B9=E7=94=A8=E9=80=82?= =?UTF-8?q?=E5=BA=94=E6=B8=A9=E5=8C=BA2y/10y=E5=B9=B4=E5=8C=96=E9=83=BD<0?= =?UTF-8?q?=E2=86=92=E5=8E=86=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.py | 56 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/server.py b/server.py index 24c925f3..8b14f5ed 100644 --- a/server.py +++ b/server.py @@ -661,6 +661,34 @@ def api_research_strategies(): except Exception: pass _candidate_pool |= set(_regime_winrates.keys()) + # ── 2026-08-15 适应温区(best_regime):weights 优先,缺失从温区数据推断(2y最高胜率温区)── + _best_regime_map = {} + try: + for _v, _w in (_weights.get("weights") or {}).items(): + _br = _w.get("best_regime") + if _br: + _best_regime_map[_v] = _br + except Exception: + pass + # 推断缺失的(港股/历史策略) + try: + import sqlite3 as _sqb + _cb = _sqb.connect(str(DATA_DIR / "mofin.db"), timeout=10) + for _v, _rws in _regime_winrates.items(): + if _v in _best_regime_map or not _rws: + continue + _best_br = None + _best_wr = -1 + for _reg, _rw in _rws.items(): + _wr = _rw.get("win_rate") + if _wr is not None and _wr > _best_wr: + _best_wr = _wr + _best_br = _reg + if _best_br: + _best_regime_map[_v] = _best_br + _cb.close() + except Exception: + pass for s in strats: # 2026-08-15 温区加权综合/普适:整体行的综合/普适 = 按温区时长占比加权各温区表现 # (业务逻辑:策略只在特定温区激活,评估应该限定在温区范围才值得参考) @@ -715,6 +743,8 @@ def api_research_strategies(): s['regime_winrates'] = _regime_winrates.get(s['version'], {}) # current = 当前温区激活(替代旧的 CURRENT_VERSIONS 硬编码) s['current'] = s['version'] in _active_set + # 2026-08-15 适应温区(前端方案A:策略只在适应温区表显示一行) + s['best_regime'] = _best_regime_map.get(s['version']) # 2026-08-15 证伪数据驱动:从 strategy_research.deprecated 读(替代前端硬编码) s['deprecated'] = _deprecated.get(s['version']) # 2026-08-15 三状态(派生,不存储):激活/可用未激活/不可用 @@ -725,27 +755,27 @@ def api_research_strategies(): s['state'] = 'unavailable' s['state_reason'] = '已证伪: ' + str(s['deprecated'])[:80] elif s['version'] in _candidate_pool: - # ── 2026-08-15 无价值淘汰:2y+10y 温区年化都跑不赢大盘 → 历史 ── - # 只在 2y/10y 请求时判定;用当次数据(2y) + 缓存(10y),避免额外重算 + # ── 2026-08-15 无价值淘汰:适应温区(best_regime) 2y+10y 年化都<0 → 历史 ── + # (老莫逻辑:温区表数据对应适应温区;适应温区都亏的策略无价值) _elim_reason = None try: - _b2, _b10 = _get_benchmarks() - if _b2 is not None and _b10 is not None: - # 读表拿 2y/10y 温区数据(毫秒级,数据加工层预计算) + _br_v = s.get('best_regime') + if _br_v: _r2 = _compute_regime_winrates_cached('2y', _approx_regime_universality) _r10 = _compute_regime_winrates_cached('10y', _approx_regime_universality) - _v2 = _r2.get(s['version']) or {} - _v10 = _r10.get(s['version']) or {} - if _v2 and _v10: - _best2 = max(r.get('cagr_pct') or -999 for r in _v2.values()) - _best10 = max(r.get('cagr_pct') or -999 for r in _v10.values()) - if _best2 < _b2 and _best10 < _b10: - _elim_reason = f'2y年化{round(_best2,1)}%<大盘{_b2}% 且 10y年化{round(_best10,1)}%<大盘{_b10}%' + _rw2 = (_r2.get(s['version']) or {}).get(_br_v) + _rw10 = (_r10.get(s['version']) or {}).get(_br_v) + if _rw2 and _rw10: + _c2 = _rw2.get('cagr_pct') + _c10 = _rw10.get('cagr_pct') + if _c2 is not None and _c10 is not None and _c2 < 0 and _c10 < 0: + _elim_reason = (f'适应温区[{_br_v}] 2y年化{round(_c2,1)}%<0 ' + f'且 10y年化{round(_c10,1)}%<0') except Exception: _elim_reason = None if _elim_reason: s['state'] = 'unavailable' - s['state_reason'] = '跑不赢大盘已淘汰: ' + _elim_reason + s['state_reason'] = '适应温区亏损已淘汰: ' + _elim_reason else: s['state'] = 'available' s['state_reason'] = '候选池内,当前温区未激活(温区切换即可激活)'