Skip to content

Selector 回测方案

当前状态:本文档描述的最小链路已经完成第一版落地,对应实现包括:

  • zelqor.strategy.interfaces.DailyPoolSelector
  • zelqor.strategy.interfaces.SelectorContext
  • zelqor.strategy.selector.build_daily_pools
  • zelqor.api.run_selector_backtest
  • examples/run_selector_backtest.py
  • examples/selector_numpy_skeleton.py
  • examples/selector_polars_skeleton.py
  • tests/test_selector_pipeline.py

本文档定义 zelqor 下一阶段要实现的最小链路:

  • selector
  • daily_pools
  • backtest

目标不是立即做成完整平台,而是先让策略脚本可以直接 import zelqor,生成每日股票池,再交给当前最小回测执行核心。

1. 目标

当前已经完成:

  • ClickHouseProvider
  • MarketDataProvider 接口层
  • run_precomputed_backtest(...)

现在缺的是:

  • 谁来生成 daily_pools

因此下一阶段的目标是补齐:

  • selector.select(...)
  • build_daily_pools(...)
  • run_selector_backtest(...)

形成一条完整但最小的链路:

selector -> daily_pools -> run_precomputed_backtest

2. 设计原则

这一阶段遵循以下原则:

  • 先支持库模式
  • 不先做策略文件动态加载
  • 不先做 CLI 托管执行
  • 不先做复杂 selector registry
  • 先把最小调用链打通

也就是说,策略脚本可以直接:

from zelqor import ...

然后自己把 selector 和回测执行串起来。

3. 最小职责拆分

3.1 selector

职责:

  • 接收某一个交易日
  • 基于 provider 和参数生成当日股票池
  • 返回股票代码列表

建议最小接口:

class DailyPoolSelector(Protocol):
    def select(self, context: SelectorContext) -> list[str]:
        ...

推荐同时提供一个轻量上下文对象:

@dataclass(slots=True)
class SelectorContext:
    trade_date: str
    provider: MarketDataProvider
    params: dict[str, Any]

3.2 daily_pools builder

职责:

  • 遍历一段交易日
  • 调用 selector
  • 生成标准 daily_pools

建议最小接口:

def build_daily_pools(
    trade_dates: list[str],
    selector: DailyPoolSelector,
    provider: MarketDataProvider,
    params: dict[str, Any] | None = None,
) -> dict[str, list[str]]:
    ...

3.3 selector backtest API

职责:

  • 调用 build_daily_pools(...)
  • 组装 PrecomputedPoolBacktestInput
  • 调用 run_precomputed_backtest(...)

建议最小接口:

def run_selector_backtest(
    *,
    trade_dates: list[str],
    selector: DailyPoolSelector,
    provider: MarketDataProvider,
    initial_cash: float,
    holding_days: int = 1,
    max_positions: int = 10,
    params: dict[str, Any] | None = None,
) -> BacktestResult:
    ...

4. 数据流

最小数据流如下:

  1. 调用方准备 provider
  2. 调用方准备 trade_dates
  3. selector 针对每个 trade_date 生成股票池
  4. 构造 daily_pools
  5. 调用 run_precomputed_backtest(...)
  6. 得到 BacktestResult

结果上不会新增新的总结果类型,继续复用当前已有的:

  • BacktestResult

5. 非目标

这一阶段明确不做:

  • 动态加载外部 .py 策略文件
  • selector 多类型注册系统
  • selector sandbox
  • selector 与 CLI 的统一托管
  • run_backtest(request) 的完整接线
  • 与 Rust 的重新集成

6. 推荐实现顺序

建议按下面顺序推进:

  1. 调整 strategy/interfaces.py
  2. 实现 build_daily_pools(...)
  3. 实现 run_selector_backtest(...)
  4. 增加最小 selector 测试
  5. 增加 examples/run_selector_backtest.py

7. 最小示例形态

最小 selector 示例可以先很简单,例如:

class DemoSelector:
    def select(self, context: SelectorContext):
        universe = context.get_universe()
        return universe[:2]

然后:

result = run_selector_backtest(
    trade_dates=provider.get_trading_dates("2025-03-24", "2025-03-28"),
    selector=DemoSelector(),
    provider=provider,
    initial_cash=100000,
    holding_days=1,
)

8. 预期产出

完成这一阶段后,zelqor 将具备三层最小能力:

  1. 数据读取层
  2. selector 股票池生成层
  3. 最小回测执行层

这时库模式就已经基本闭环,可以让策略脚本直接 import 并运行一条完整的研究链路。