From e866e822995a9bf187a09d47539a2fef75075e9d Mon Sep 17 00:00:00 2001 From: Justin Gu <97915@qq.com> Date: Thu, 3 Sep 2026 03:27:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=80=E9=94=AE=E5=AF=BB=E4=BC=98?= =?UTF-8?q?=E6=89=80=E6=9C=89=E7=AD=96=E7=95=A5=E8=A1=A5=E9=BD=90=2035=20?= =?UTF-8?q?=E4=B8=AA=E6=96=B0=E7=AD=96=E7=95=A5=E9=A2=84=E8=AE=BE=E7=BD=91?= =?UTF-8?q?=E6=A0=BC=EF=BC=88STRATEGY=5FPRESETS=2019=E2=86=9254=EF=BC=8C?= =?UTF-8?q?=E7=BD=91=E6=A0=BC=E7=82=B9=20174=E2=86=92328=EF=BC=89+=20?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E2=86=94=E9=A2=84=E8=AE=BE=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=80=A7=E5=9B=9E=E5=BD=92=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/easy_tdx/backtest/strategies/presets.py | 123 ++++++++++++++++++++ tests/unit/test_optimizer.py | 49 ++++++++ 2 files changed, 172 insertions(+) diff --git a/src/easy_tdx/backtest/strategies/presets.py b/src/easy_tdx/backtest/strategies/presets.py index 2f360f5..655cb2f 100644 --- a/src/easy_tdx/backtest/strategies/presets.py +++ b/src/easy_tdx/backtest/strategies/presets.py @@ -98,6 +98,129 @@ STRATEGY_PRESETS: dict[str, dict[str, list[Any]]] = { # capital 仅作粗档扫描(1千万/1亿/10亿股),覆盖小盘→大盘 "capital": [1e7, 1e8, 1e9, 1e10], }, # 4 + # ── V1.30.2 存量指标补齐 ──────────────────────────────────────────────── + "psy_reversal": { + "n": [12, 20], + "oversold": [20, 25, 30], + }, # 6 + "mtm_cross": { + "n": [6, 12, 24], + }, # 3 + "roc_zero": { + "n": [6, 12, 24], + }, # 3 + "expma_cross": { + "n1": [5, 10, 12], + "n2": [26, 50, 60], + }, # 9(n1 None: + """注册表与 STRATEGY_PRESETS 键集一一对应(双向:不多不少)。""" + from easy_tdx.backtest.strategies import ( + builtin, # noqa: F401 # 触发注册 + get_registry, + ) + from easy_tdx.backtest.strategies.presets import STRATEGY_PRESETS + + names = set(get_registry().names()) + missing = names - set(STRATEGY_PRESETS) + extra = set(STRATEGY_PRESETS) - names + assert not missing, f"这些策略无预设网格,会被一键寻优静默跳过: {sorted(missing)}" + assert not extra, f"这些预设指向未注册的策略: {sorted(extra)}" + + def test_preset_values_within_param_bounds(self) -> None: + """预设取值必须在参数 schema 边界内(否则寻优端点 422)。""" + from easy_tdx.backtest.strategies import ( + builtin, # noqa: F401 # 触发注册 + get_registry, + ) + from easy_tdx.backtest.strategies.presets import STRATEGY_PRESETS + + registry = get_registry() + for name, grid in STRATEGY_PRESETS.items(): + params = {p.name: p for p in registry.get(name).params} + for pname, values in grid.items(): + assert pname in params, f"{name}: 预设参数 {pname} 不在 schema 中" + for v in values: + params[pname].validate(v) # 越界抛 ValueError + + def test_preset_grid_size_within_limit(self) -> None: + """单策略笛卡尔积 ≤ 200(ParamGridOptimizer.MAX_GRID_POINTS)。""" + import math + + from easy_tdx.backtest.strategies.presets import STRATEGY_PRESETS + + for name, grid in STRATEGY_PRESETS.items(): + size = math.prod(len(v) for v in grid.values()) if grid else 1 + assert size <= 200, f"{name}: 预设网格 {size} 点超上限"