313 lines
14 KiB
Markdown
313 lines
14 KiB
Markdown
# batch-backtest Specification
|
||
|
||
## Purpose
|
||
TBD - created by archiving change refactor-backtest-separate-cli. Update Purpose after archive.
|
||
## Requirements
|
||
### Requirement: 多股票回测参数
|
||
系统 SHALL 支持通过命令行参数传入多个股票代码进行批量回测。
|
||
|
||
#### Scenario: 传入多个股票代码
|
||
- **WHEN** 用户执行 `python backtest_command.py --codes 000001.SZ 600000.SH --start-date 2024-01-01 --end-date 2025-12-31 --strategy-file strategies/macd_strategy.py`
|
||
- **THEN** 系统解析所有股票代码到列表 `['000001.SZ', '600000.SH']`
|
||
- **THEN** 系统按顺序依次执行每个股票的回测
|
||
- **THEN** 系统为每个股票生成独立的回测结果
|
||
|
||
#### Scenario: 传入单个股票代码
|
||
- **WHEN** 用户执行 `python backtest_command.py --codes 000001.SZ --start-date 2024-01-01 --end-date 2025-12-31 --strategy-file strategies/macd_strategy.py`
|
||
- **THEN** 系统解析为单个股票代码列表 `['000001.SZ']`
|
||
- **THEN** 系统执行单个股票回测
|
||
- **THEN** 系统输出详细格式的回测结果
|
||
|
||
#### Scenario: 缺少 --codes 参数
|
||
- **WHEN** 用户未提供 `--codes` 参数
|
||
- **THEN** 系统输出错误信息:"错误: 需要以下参数: --codes"
|
||
- **THEN** 系统退出并返回非零状态码
|
||
|
||
---
|
||
|
||
### Requirement: 批量回测执行
|
||
系统 SHALL 串行执行多个股票的回测,每次加载一个股票的数据并执行回测。
|
||
|
||
#### Scenario: 成功执行多个股票回测
|
||
- **WHEN** 用户传入 N 个股票代码
|
||
- **THEN** 系统循环 N 次,每次加载一个股票的数据
|
||
- **THEN** 系统每次执行完整的回测流程(数据加载、指标计算、回测执行)
|
||
- **THEN** 系统每次执行完成后生成 `BacktestResult` 对象
|
||
- **THEN** 系统返回包含 N 个 `BacktestResult` 的列表
|
||
|
||
#### Scenario: 每个股票独立预热期
|
||
- **WHEN** 系统执行第 i 个股票的回测
|
||
- **THEN** 系统使用 `start_date - warmup_days` 计算该股票的预热开始日期
|
||
- **THEN** 系统独立加载该股票的预热期数据
|
||
- **THEN** 不同股票的预热期互不影响
|
||
|
||
#### Scenario: 第一个股票回测失败
|
||
- **WHEN** 系统执行第一个股票回测时发生错误(数据库连接失败、策略加载失败等)
|
||
- **THEN** 系统捕获异常并输出错误信息
|
||
- **THEN** 系统停止执行后续股票的回测
|
||
- **THEN** 系统退出并返回非零状态码(立即失败策略)
|
||
|
||
#### Scenario: 中间股票回测失败
|
||
- **WHEN** 系统执行第 i 个股票回测时发生错误
|
||
- **THEN** 系统输出错误信息(包含股票代码)
|
||
- **THEN** 系统停止执行后续股票的回测
|
||
- **THEN** 系统退出并返回非零状态码
|
||
|
||
#### Scenario: 资源管理
|
||
- **WHEN** 系统完成第 i 个股票的回测
|
||
- **THEN** 系统关闭该股票的数据库连接(`engine.dispose()`)
|
||
- **THEN** 系统释放该股票的数据内存
|
||
- **THEN** 系统开始加载第 i+1 个股票的数据
|
||
|
||
---
|
||
|
||
### Requirement: 批量回测进度显示
|
||
系统 SHALL 使用 tqdm 显示批量回测的实时进度,提供用户反馈。
|
||
|
||
#### Scenario: 显示进度条
|
||
- **WHEN** 系统开始执行 N 个股票的批量回测
|
||
- **THEN** 系统显示进度条格式:`回测进度: 25%|█████▌ | 1/4 [00:30<01:30, 12.5s/it]`
|
||
- **THEN** 系统在完成每个股票回测后更新进度条
|
||
- **THEN** 进度条显示当前进度(i/N)、已用时间、预计剩余时间
|
||
- **THEN** 进度条在所有股票回测完成后消失
|
||
|
||
#### Scenario: 单股票回测不显示进度条
|
||
- **WHEN** 用户传入单个股票代码
|
||
- **THEN** 系统不显示 tqdm 进度条
|
||
- **THEN** 系统直接输出回测结果
|
||
|
||
#### Scenario: 进度条描述文本
|
||
- **WHEN** 系统显示批量回测进度
|
||
- **THEN** 进度条描述 SHALL 为 "回测进度"(中文)
|
||
- **THEN** 进度条显示已完成/总数(如 "1/4", "2/4")
|
||
|
||
---
|
||
|
||
### Requirement: 批量回测结果展示
|
||
系统 SHALL 使用 tabulate 将多个股票的回测结果格式化为表格,便于横向对比。
|
||
|
||
#### Scenario: 表格化输出多股票结果
|
||
- **WHEN** 用户传入多个股票代码且回测成功
|
||
- **THEN** 系统使用 tabulate 生成表格
|
||
- **THEN** 表格格式 SHALL 为 grid(带边框)
|
||
- **THEN** 表格列 SHALL 包含:股票代码、收益率%、胜率%、最大回撤%、交易次数、SQN
|
||
- **THEN** 系统在表格上方显示表头(中文列名)
|
||
- **THEN** 数值保留 2 位小数(交易次数为整数)
|
||
|
||
#### Scenario: 表格内容填充
|
||
- **WHEN** 系统格式化第 i 个股票的结果
|
||
- **THEN** 系统从 `BacktestResult` 对象提取字段
|
||
- **THEN** "股票代码" 列填充 `result.code`
|
||
- **THEN** "收益率%" 列填充 `result.return_pct`
|
||
- **THEN** "胜率%" 列填充 `result.win_rate`
|
||
- **THEN** "最大回撤%" 列填充 `result.max_drawdown`
|
||
- **THEN** "交易次数" 列填充 `result.trades`
|
||
- **THEN** "SQN" 列填充 `result.sqn`
|
||
|
||
#### Scenario: 单股票回测不使用表格
|
||
- **WHEN** 用户传入单个股票代码
|
||
- **THEN** 系统不使用 tabulate 生成表格
|
||
- **THEN** 系统使用详细格式输出(每个指标单独一行)
|
||
- **THEN** 系统保持原有 `print_stats()` 的输出格式
|
||
|
||
#### Scenario: 表格示例输出
|
||
- **WHEN** 用户传入 2 个股票代码
|
||
- **THEN** 系统输出格式 SHALL 为:
|
||
```
|
||
+-------------+-----------+--------+------------+----------+-------+
|
||
| 股票代码 | 收益率% | 胜率% | 最大回撤% | 交易次数 | SQN |
|
||
+-------------+-----------+--------+------------+----------+-------+
|
||
| 000001.SZ | 20.35 | 55.00 | -8.50 | 45 | 1.85 |
|
||
| 600000.SH | 15.00 | 48.00 | -12.30 | 38 | 1.42 |
|
||
+-------------+-----------+--------+------------+----------+-------+
|
||
```
|
||
|
||
---
|
||
|
||
### Requirement: 多股票图表输出
|
||
系统 SHALL 为每个股票生成独立的 HTML 图表文件,文件名格式为 `{code}.html`。
|
||
|
||
#### Scenario: 指定 --output-dir 参数
|
||
- **WHEN** 用户传入 `--output-dir output/`
|
||
- **THEN** 系统为每个股票生成 HTML 文件到 `output/{code}.html`
|
||
- **THEN** 文件名 SHALL 为股票代码,如 `000001.SZ.html`, `600000.SH.html`
|
||
- **THEN** 系统自动创建 `output/` 目录(`exist_ok=True`)
|
||
- **THEN** 系统在完成后输出提示:"图表已保存到目录: output/" 后列出所有文件
|
||
|
||
#### Scenario: 未指定 --output-dir 参数
|
||
- **WHEN** 用户未传入 `--output-dir` 参数
|
||
- **THEN** 系统不为任何股票生成图表文件
|
||
- **THEN** 系统仅输出控制台统计信息
|
||
|
||
#### Scenario: 图表文件覆盖
|
||
- **WHEN** 系统再次执行相同的批量回测
|
||
- **THEN** 系统覆盖已存在的 HTML 文件
|
||
- **THEN** 系统不提示文件已存在
|
||
|
||
---
|
||
|
||
### Requirement: 结构化回测结果
|
||
系统 SHALL 返回标准化的 `BacktestResult` 对象,包含所有关键指标。
|
||
|
||
#### Scenario: BacktestResult 对象创建
|
||
- **WHEN** 系统完成单股票回测
|
||
- **THEN** 系统从 `stats` 对象提取指标到 `BacktestResult`
|
||
- **THEN** `BacktestResult.code` 设置为股票代码
|
||
- **THEN** `BacktestResult.start_date` 设置为回测开始日期
|
||
- **THEN** `BacktestResult.end_date` 设置为回测结束日期
|
||
- **THEN** `BacktestResult.equity_final` 设置为最终权益
|
||
- **THEN** `BacktestResult.equity_peak` 设置为峰值收益
|
||
- **THEN** `BacktestResult.return_pct` 设置为总收益率
|
||
- **THEN** `BacktestResult.buy_hold_return` 设置为买入持有收益率
|
||
- **THEN** `BacktestResult.return_annual` 设置为年化收益率
|
||
- **THEN** `BacktestResult.volatility_annual` 设置为年化波动率
|
||
- **THEN** `BacktestResult.max_drawdown` 设置为最大回撤
|
||
- **THEN** `BacktestResult.avg_drawdown` 设置为平均回撤
|
||
- **THEN** `BacktestResult.max_drawdown_duration` 设置为最大回撤持续时长
|
||
- **THEN** `BacktestResult.avg_drawdown_duration` 设置为平均回撤持续时长
|
||
- **THEN** `BacktestResult.sortino_ratio` 设置为索提诺比率
|
||
- **THEN** `BacktestResult.calmar_ratio` 设置为卡尔玛比率
|
||
- **THEN** `BacktestResult.trades` 设置为交易次数
|
||
- **THEN** `BacktestResult.win_rate` 设置为胜率
|
||
- **THEN** `BacktestResult.sqn` 设置为系统质量数
|
||
- **THEN** `BacktestResult.cash` 设置为初始资金
|
||
- **THEN** `BacktestResult.commission` 设置为手续费率
|
||
|
||
#### Scenario: BacktestResult 列表返回
|
||
- **WHEN** 系统完成批量回测
|
||
- **THEN** 系统返回 `List[BacktestResult]`
|
||
- **THEN** 列表顺序 SHALL 与输入股票代码顺序一致
|
||
- **THEN** 列表长度 SHALL 等于输入股票代码数量(成功时)
|
||
|
||
#### Scenario: BacktestResult 数据类型
|
||
- **WHEN** 系统创建 `BacktestResult` 对象
|
||
- **THEN** 数值字段 SHALL 为 float 类型(除 `trades`, `max_drawdown_duration` 为 int)
|
||
- **THEN** 日期字段 SHALL 为 str 类型(YYYY-MM-DD 格式)
|
||
- **THEN** 系统支持 `result.to_dict()` 方法(dataclass 自动生成)
|
||
|
||
---
|
||
|
||
### Requirement: 可复用回测引擎接口
|
||
系统 SHALL 提供标准化的函数接口,供其他模块调用回测功能。
|
||
|
||
#### Scenario: run_backtest 函数调用
|
||
- **WHEN** 其他模块调用 `run_backtest(code, start_date, end_date, strategy_file, cash, commission, warmup_days, output_file)`
|
||
- **THEN** 函数接收股票代码、日期范围、策略文件、回测参数、输出文件路径
|
||
- **THEN** 函数执行完整回测流程(数据加载、策略加载、指标计算、回测执行)
|
||
- **THEN** 函数返回 `BacktestResult` 对象
|
||
- **THEN** 函数不打印任何输出(纯函数)
|
||
|
||
#### Scenario: run_batch_backtest 函数调用
|
||
- **WHEN** 其他模块调用 `run_batch_backtest(codes, start_date, end_date, strategy_file, cash, commission, warmup_days, output_dir)`
|
||
- **THEN** 函数接收股票代码列表、日期范围、策略文件、回测参数、输出目录
|
||
- **THEN** 函数串行执行每个股票的回测
|
||
- **THEN** 函数返回 `List[BacktestResult]`
|
||
- **THEN** 函数显示 tqdm 进度条(批量时)
|
||
|
||
#### Scenario: 函数参数默认值
|
||
- **WHEN** 调用者不指定可选参数
|
||
- **THEN** `cash` 默认为 100000
|
||
- **THEN** `commission` 默认为 0.002
|
||
- **THEN** `warmup_days` 默认为 365
|
||
- **THEN** `output_file` 默认为 None(不生成图表)
|
||
- **THEN** `output_dir` 默认为 None(不生成图表)
|
||
|
||
#### Scenario: 函数异常抛出
|
||
- **WHEN** `run_backtest` 或 `run_batch_backtest` 执行时发生错误
|
||
- **THEN** 函数 SHALL 抛出异常(不捕获)
|
||
- **THEN** 异常类型 SHALL 为 ValueError、TypeError 或原始异常
|
||
- **THEN** 异常信息 SHALL 包含具体错误原因
|
||
- **THEN** 调用者负责捕获和处理异常
|
||
|
||
---
|
||
|
||
### Requirement: 集中配置管理
|
||
系统 SHALL 在 config.py 中集中管理数据库配置、默认回测参数、图表配色。
|
||
|
||
#### Scenario: 数据库配置访问
|
||
- **WHEN** backtest_core.py 需要数据库连接参数
|
||
- **THEN** 模块从 config 导入 `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`
|
||
- **THEN** 模块使用这些常量构建连接字符串
|
||
- **THEN** 模块不重复定义数据库配置
|
||
|
||
#### Scenario: 默认参数访问
|
||
- **WHEN** backtest_core.py 需要默认回测参数
|
||
- **THEN** 模块从 config 导入 `DEFAULT_CASH`, `DEFAULT_COMMISSION`, `DEFAULT_WARMUP_DAYS`
|
||
- **THEN** 模块使用这些常量作为函数默认值
|
||
- **THEN** 模块不重复定义默认参数
|
||
|
||
#### Scenario: 图表配色访问
|
||
- **WHEN** backtest_core.py 需要设置图表配色
|
||
- **THEN** 模块从 config 导入 `BULL_COLOR`, `BEAR_COLOR`
|
||
- **THEN** 模块使用这些颜色设置 `plotting.BULL_COLOR` 和 `plotting.BEAR_COLOR`
|
||
- **THEN** 模块不重复定义颜色配置
|
||
|
||
#### Scenario: 配置文件内容
|
||
- **WHEN** 查看 config.py 文件
|
||
- **THEN** 文件包含数据库配置(DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD)
|
||
- **THEN** 文件包含默认回测参数(DEFAULT_CASH, DEFAULT_COMMISSION, DEFAULT_WARMUP_DAYS)
|
||
- **THEN** 文件包含图表配色(BULL_COLOR, BEAR_COLOR)
|
||
- **THEN** 所有配置使用明文常量(不使用环境变量)
|
||
|
||
---
|
||
|
||
### Requirement: 错误处理策略
|
||
系统 SHALL 在批量回测失败时立即停止执行,不继续处理后续股票。
|
||
|
||
#### Scenario: 数据加载失败
|
||
- **WHEN** 系统加载第 i 个股票数据时失败(数据库错误、数据不存在)
|
||
- **THEN** 系统捕获异常
|
||
- **THEN** 系统输出错误信息:"回测失败 [{code}]: {error}"
|
||
- **THEN** 系统停止执行后续股票的回测
|
||
- **THEN** 系统退出并返回非零状态码
|
||
|
||
#### Scenario: 策略加载失败
|
||
- **WHEN** 系统加载策略文件时失败(文件不存在、接口不完整)
|
||
- **THEN** 系统捕获异常
|
||
- **THEN** 系统输出错误信息:"策略加载失败: {error}"
|
||
- **THEN** 系统停止执行所有股票的回测
|
||
- **THEN** 系统退出并返回非零状态码
|
||
|
||
#### Scenario: 回测执行失败
|
||
- **WHEN** 系统执行第 i 个股票回测时失败(策略逻辑错误)
|
||
- **THEN** 系统捕获异常
|
||
- **THEN** 系统输出错误信息和完整堆栈跟踪
|
||
- **THEN** 系统停止执行后续股票的回测
|
||
- **THEN** 系统退出并返回非零状态码
|
||
|
||
#### Scenario: 图表生成失败
|
||
- **WHEN** 系统生成第 i 个股票图表时失败
|
||
- **THEN** 系统捕获异常
|
||
- **THEN** 系统输出警告:"图表生成失败 [{code}]: {error},但回测已完成"
|
||
- **THEN** 系统继续执行后续股票的回测
|
||
- **THEN** 系统在返回的 `BacktestResult` 中设置 `error` 字段(如果设计支持)
|
||
|
||
---
|
||
|
||
### Requirement: 依赖管理
|
||
系统 SHALL 在 pyproject.toml 中添加 tabulate 和 tqdm 依赖。
|
||
|
||
#### Scenario: 添加 tabulate 依赖
|
||
- **WHEN** 查看 pyproject.toml 文件
|
||
- **THEN** 文件包含 `tabulate` 依赖
|
||
- **THEN** 依赖版本 SHALL 为兼容当前 Python 版本的版本
|
||
- **THEN** 系统可以导入 `import tabulate` 无错误
|
||
|
||
#### Scenario: 添加 tqdm 依赖
|
||
- **WHEN** 查看 pyproject.toml 文件
|
||
- **THEN** 文件包含 `tqdm` 依赖
|
||
- **THEN** 依赖版本 SHALL 为兼容当前 Python 版本的版本
|
||
- **THEN** 系统可以导入 `from tqdm import tqdm` 无错误
|
||
|
||
#### Scenario: 依赖安装
|
||
- **WHEN** 用户运行 `uv sync` 或 `pip install -e .`
|
||
- **THEN** 系统自动安装 tabulate 和 tqdm
|
||
- **THEN** 系统显示依赖安装进度
|
||
- **THEN** 系统完成安装后可以正常使用回测工具
|
||
|
||
#### Scenario: 依赖缺失提示
|
||
- **WHEN** 系统导入 tabulate 或 tqdm 时失败
|
||
- **THEN** 系统输出友好错误信息:"缺少依赖: {package_name},请运行: uv add {package_name}"
|
||
- **THEN** 系统退出并返回非零状态码
|
||
|