10. 过程数据(proc.*)与动态回测(设计说明)

本章说明 qteasy 中过程数据(process data)的定义、访问方式,以及回测时静态分支动态分支的选择与一致性约定,供实现与扩展时参考。使用层面的说明可在《策略如何声明与使用数据》与 API 文档中配合阅读。

10.1. 1. 背景与目标

1.1 过程数据的含义

部分策略需要依赖随回测或实盘执行路径而变化的数据,例如:

  • 当前/历史持仓、可用现金;

  • 历史成交数量、成交价格、交易成本;

  • 由持仓与价格推导的市值、总资产等。

这类数据无法在回测开始前一次性预生成,只能由 Backtester(回测)或 Trader(实盘)在运行过程中维护,并在策略生成信号的每一步按“当前可见范围”提供给策略。我们将其统称为过程数据

1.2 设计目标

  • 统一入口:过程数据与静态历史数据一样,通过 Strategy.get_data() 获取,降低学习成本。

  • 无前视:策略在生成第 k 步信号时,不能看到第 k 步的成交结果,只能使用已完成步的历史。

  • 回测/实盘一致:同一套策略与 get_data('proc.xxx') 调用方式,在回测与实盘中均可使用;当需要过程数据时走动态执行路径,否则可与原有静态路径在结果上保持一致。

10.2. 2. 过程数据的统一定义(proc.*)

2.1 命名与曝光方式

  • 所有过程数据对策略以 proc.<field_name> 的形式曝光,例如 proc.own_cashproc.trade_records

  • 过程数据不需要在策略的 __init__ 中通过 data_types 声明,由 Backtester / Trader 在运行时注入,策略只需在 realize() 中按需调用 get_data('proc.xxx', ...)

2.2 已实现的内建字段

当前版本已实现并可在策略中使用的过程数据字段包括:

类别

字段名

含义

账户标量

proc.own_cash

当前步开始时账户总现金

proc.available_cash

当前步开始时可用于下单的现金

proc.total_value

当前步开始时总资产市值(持仓估值 + 现金)

持仓向量

proc.own_amounts

当前步开始时各标的总持仓数量

proc.available_amounts

当前步开始时各标的可卖出数量

proc.position_value

当前步开始时各标的持仓市值(由内部 price 与持仓计算)

成交结果

proc.trade_records

各步各标的的实际成交数量(正买负卖)

proc.trade_cost

各步各标的的交易成本

proc.trade_price

各步各标的的成交价格

上述字段在回测与实盘中的时间语义与可见性约束见第 4 节。

2.3 后续可扩展字段(可选)

设计上可继续扩展的字段包括:proc.realized_pnlproc.unrealized_pnlproc.last_trade_priceproc.last_trade_volume 等,具体以实现与文档为准。

10.3. 3. 访问接口:Strategy.get_data() 与 proc.*

3.1 静态数据(无 proc. 前缀)

  • 单源:self.get_data('close_E_d');多源:self.get_data('close_E_d', 'high_E_d')

  • 静态数据不支持 lag / window 参数;若传入则抛出英文 ValueError

3.2 过程数据(proc. 前缀)

  • 调用示例

    • self.get_data('proc.own_cash'):截至当前步的现金序列;

    • self.get_data('proc.own_cash', lag=0):最近一步的现金;

    • self.get_data('proc.own_cash', lag='1d'):按时间回溯 1 天对应的步;

    • self.get_data('proc.own_cash', window='5d'):过去 5 天内的窗口切片。

  • 约束

    • 一次调用仅允许一个 proc.* 字段;多字段或与静态数据混合调用时抛出英文 ValueError

    • lagwindow 不可同时指定;支持 lag 为整数(步)或字符串(如 '1d''8h'),window 为字符串(如 '5d''8h')。

  • 返回值:统一为 np.ndarray,形状与数据类型以 API 文档为准。

10.4. 4. 回测分支选择与过程数据协作

4.1 静态分支与动态分支

  • 静态分支_backtest_static_operator):一次性对所有时间步调用 run_strategies 生成全部信号,再用 Numba 向量化函数 backtest_batch_steps 等完成回测。适用于不依赖过程数据的策略。

  • 动态分支_backtest_dynamic_operator):按时间步循环,每步生成信号 → 解析并模拟成交 → 更新持仓与现金,再进入下一步。过程数据由 Backtester 维护并在每步前注入 Operator,供策略通过 get_data('proc.xxx') 访问。

Backtester 在运行前通过 Operator.check_dynamic_data() 决定走哪条分支。

4.2 check_dynamic_data() 的判定逻辑(当前实现)

以下任一为真则返回 True,走动态分支:

  1. op_type == ‘stepwise’:Operator 显式配置为逐步模式。

  2. 策略源码中使用 proc.*:通过 _strategies_use_proc_data() 检查各策略 realize() 的源码是否包含 'proc.'"proc.",若包含则视为依赖过程数据。

因此,只要在 realize() 中调用了 get_data(‘proc.xxx’),无需任何声明即可自动走动态分支。过程数据仅通过 proc.* 访问,不再支持通过 DataType 声明(旧式 op_* 类型已移除)。

4.3 无前视保证

  • 策略在第 k 步生成信号时:

    • 账户/持仓类(如 own_cashesown_amounts)最多可见到索引 [0..k](即当前步开始时的状态);

    • 成交类(如 trade_recordstrade_cost)最多可见到 [0..k-1]不包含本步尚未发生的成交

  • 实现上由 Operator 的 _current_signal_index 与 Strategy 的 _get_process_data_single() 按上述范围切片,Backtester 在每步生成信号前更新索引,保证无前视。

4.4 Backtester 与 Operator 的注入关系

  • Backtester(动态分支):在 _backtest_dynamic_operator 入口处将 own_cashesavailable_cashesown_amounts_arrayavailable_amounts_arraytrade_records_arraytrade_cost_arraytrade_price_arraytrade_price_data 等以 _process_data_sources 注入 Operator,并将 _process_time_index 设为与 op_signal_index 对齐的时间轴。

  • Operator:在 run_strategy(step_index) 中,在每次调用 stg.generate() 前根据 group_timing_tablegroup_merge_type 计算并更新 _current_signal_index,供 Strategy 截取“当前可见”的过程数据。

4.5 实盘(Trader)中的过程数据

operator.check_dynamic_data() 为 True 时,Trader 在 _run_strategy() 中会:

  • 将当前账户现金、持仓、可用数量及当前价格等组装为单步_process_data_sources_process_time_index(实盘单次运行视为一步);

  • 策略在该步内调用 get_data('proc.own_cash') 等即可获得当前账户/持仓视图;成交历史在该步内为空,与“尚未发生本步成交”的语义一致。

10.5. 5. 动态/静态路径一致性约定

  • 当策略不使用过程数据时:应走静态分支;若因其他原因走动态分支,回测结果应与静态分支在数值上完全一致(相同配置与数据下)。

  • 当策略使用过程数据时:必须走动态分支,否则 _process_data_sources 未注入会触发 RuntimeError。

  • 测试约定:使用 StaticSignalStg(纯静态)与 ProcAwareButStaticLogicStg(调用 proc 但不用于信号)在相同配置下回测,对 own_cashesown_amounts_arraytrade_records_array 等做数值一致性断言,见 tests/test_process_data_api.py 的 B 组。

10.6. 6. 测试与文档索引

  • 专门测试tests/test_process_data_api.py

    • A 组:check_dynamic_data() 在纯静态 / 使用 proc.* 策略下的行为;

    • B 组:静态策略与“调用 proc 但逻辑等价”策略的回测数组一致性;

    • C 组:get_data 对静态多源、lag/window 拒绝、proc 单字段与混合调用的报错行为;

    • D 组:proc.trade_records 无前视验证;

    • E 组:基于 process data 的真实动态策略路径正确性。

  • 项目记忆.cursor/rules/process-data-and-dynamic-backtest.mdc(实现与约定摘要)。

  • 策略数据总览策略如何声明与使用数据回测入口与模式回测、实盘与优化