From 5691bb8432ce457a1e0bb2dc08f205ca6ebf74d2 Mon Sep 17 00:00:00 2001 From: Justin Gu <97915@qq.com> Date: Wed, 10 Jun 2026 02:13:28 +0800 Subject: [PATCH] refactor: screen() reuses run_combination(), single runner across combo sizes - screen() now calls run_combination() internally, eliminating duplicated signal extraction/combination logic - _run_combo_screen creates one CombinationRunner before the size loop, so signal cache is reused across 2-factor and 3-factor screens - Add MAJORITY(2)=AND note to screen() docstring --- run_all_strategies.py | 15 ++++++++------- src/easy_tdx/backtest/combo.py | 13 ++++--------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/run_all_strategies.py b/run_all_strategies.py index cc1f366..298ac7d 100644 --- a/run_all_strategies.py +++ b/run_all_strategies.py @@ -97,19 +97,20 @@ def _run_combo_screen( from math import comb + # 单个 Runner 跨 size 复用信号缓存,避免重复提取 + runner = CombinationRunner( + strategy_classes=strategy_classes, + df=df, + cash=cash, + commission=commission, + ) + for size in combo_sizes: total = comb(len(strategy_classes), size) click.echo("\n" + "=" * 80) click.echo(f"[*] {size}因子组合回测 (共{total}组, 模式={combo_mode})") click.echo("=" * 80) - runner = CombinationRunner( - strategy_classes=strategy_classes, - df=df, - cash=cash, - commission=commission, - ) - results = runner.screen(combo_sizes=(size,), mode=combo_mode.upper()) if not results: diff --git a/src/easy_tdx/backtest/combo.py b/src/easy_tdx/backtest/combo.py index d81f16d..b582d6b 100644 --- a/src/easy_tdx/backtest/combo.py +++ b/src/easy_tdx/backtest/combo.py @@ -398,6 +398,8 @@ class CombinationRunner: Args: combo_sizes: 要尝试的组合大小(如 (2, 3) 表示 2 因子和 3 因子组合) mode: 信号合并模式(AND / OR / MAJORITY) + 注意:MAJORITY 模式下 2 因子需要两个都同意(等同 AND), + 因为阈值 = 2/2 = 1.0,需 > 1.0 才触发。 filter_zero_trades: 是否过滤零交易组合 top_n: 只返回前 N 名(0 = 全部返回) @@ -412,16 +414,9 @@ class CombinationRunner: continue for combo in itertools.combinations(range(n_factors), size): + result = self.run_combination(combo, mode=mode) + signals = [self._get_factor_signals(i) for i in combo] - - # 合并信号 - buy_mask, sell_mask = combine_masks(signals, mode=mode) - - # 包装为策略并运行 - combo_cls = _make_combo_strategy(buy_mask, sell_mask) - engine = self._make_engine(combo_cls) - result = engine.run(self._df) - name = " + ".join(s.name for s in signals) results.append(