需求 14(AI 友好版):修复 bug,测试 100% 通过

提醒

可以运行 python run_test_with_log.py 这样可以在 logs 中生成一个 remove-metaprogramming 版本的日志,通过和 master 里面的日志进行对比,找到存在的原因,并修复。

背景

  • logs 目录中已有 master 分支运行 tests/strategies/test_02_multi_extend_data.py 生成的基准日志(例如:logs/test_02_multi_extend_data_master_20251106_185413.log)。

  • remove-metaprogramming 分支与 master 结果不一致,说明当前分支存在功能或行为差异,需要定位并修复。

  • 优先保证功能正确,其次关注性能回退,修复过程中尽量避免显著的性能下降。

数据访问惯例(保持不变)

data = self.datas[0]
data1 = self.datas[1]

# 收盘价序列

close_list = data.close

# 当日价格

close = close_list[0]

# 前一日价格

pre_close = close_list[-1]

# 下个交易日价格

next_close = close_list[1]

# 当前日期

current_date = data.datetime.date(0)

# 前一日时间

pre_date = data.datetime.date(-1)

# 下个交易日时间

next_date = data.datetime.date(1)

```bash

- 多数据情况下的特殊处理:系统推进到“下一共同时间”后,若某数据在该交易日无报价,则:
  - 访问 `[0]` 返回的是其“上一个有交易日”的价格/时间。
  - 访问 `[1]` 返回“下一个有交易日”的价格/时间;若后续再无交易日,则 `[1]` 越界报错。

- --

### 验收标准(Acceptance Criteria)

- 运行 `run_tests.bat`,进程返回码为 0,且 `test_results.log` 中汇总显示:`Total Failed Tests: 0`。
- 运行 `tests/strategies/test_02_multi_extend_data.py` 时,断言全部通过(该用例内已固定期望值):
  - `cerebro.strategy.bar_num == 1885`
  - `sharpe_ratio: 0.46882103593170665`
  - `annual_return: 0.056615798284517765`
  - `max_drawdown: 0.24142378277185714`
  - `trade_num: 1750`
- 不修改任何测试用例,只修改源代码。

### 环境与前置条件

- 平台:Windows(batch 脚本已适配 UTF-8)
- 需要:git、python、pip(建议开启虚拟环境)
- 数据:
  - 示例数据位于 `tests/datas/`  `examples/`(脚本支持自动查找)
  - 可选:设置环境变量 `BACKTRADER_DATA_DIR` 指向自定义数据目录

- --

### 可复现执行步骤(含分支对比与日志)

4) 再次运行同一用例记录日志

- 命令:
  - `python run_test_with_log.py`
- 输出:`logs/test_02_multi_extend_data_remove-metaprogramming_*.log`

5) 对比两个日志的关键指标与断言通过情况,分析差异。

6) 全量测试回归

- 命令:
  - `run_tests.bat`
- 验收:`test_results.log`  `Total Failed Tests: 0`,脚本退出码为 0。

- --

### 代码定位参考(追踪 next/prenext、bar 计数与多数据推进)

- `backtrader/strategy.py`
  - `_oncepost(dt)`:推进策略与指标、设置 `lines.datetime[0]`、调用 `next/nextstart/prenext` 的关键路径
  - `_next()` / `_clk_update()` / `_getminperstatus()`:与数据长度、最小周期、时钟推进相关
- `backtrader/lineiterator.py`
  - 指标与观察者的推进链路,避免重复推进或重复计算
- `tests/strategies/test_02_multi_extend_data.py`
  - 断言的唯一真值来源,包含目标指标与 `bar_num` 的期望

- --

### 常见问题与排查清单(Checklist)

- self.next/self.prenext 触发次数异常
  - 核查是否在推进链路中对指标/策略做了重复推进,导致 `bar_num` > 1885
  - 检查 `strategy._oncepost()` 内部推进与 `strategy._next()` 链路是否重复
- 多数据场景的索引越界
  - 谨慎使用 `[1]`:仅在明确“下一个有交易日”存在时使用,否则应捕获异常或做长度判断
- dt  _idx 同步
  - `dt <= 0` 时不应再触发 `next/nextstart/prenext`
  - 为数据与指标设置 `_idx` 时需使用最新有效位置,避免时间/索引错配
- 性能守护
  - 避免 O(n^2) 级循环与不必要的多次属性访问;优先缓存局部变量(如 `data0 = self.datas[0]`)

- --

### 变更范围与约束

- 仅允许修改源代码(backtrader 包内部);禁止修改任何测试用例
- 每次修改后都必须:`pip install -U .`,再执行回归

### 交付物(Deliverables)

- `test_results.log`:总失败数为 0
- 对比结论:说明 remove-metaprogramming  master 的差异点与根因
- 变更清单:涉及文件与函数、核心改动摘要
- 如有性能影响,提供前后耗时/关键指标对比

- --

### 快速执行清单(给自动化代理)

- 安装:`pip install -U .`
- 单测日志:`python run_test_with_log.py`
- 全量回归:`run_tests.bat`(验收:`Total Failed Tests: 0`)
- 若不通过:回看“代码定位参考 + 排查清单”,迭代修复