1
0
Files

313 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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** 系统退出并返回非零状态码