完成回测脚本
This commit is contained in:
195
openspec/specs/backtest-cli/spec.md
Normal file
195
openspec/specs/backtest-cli/spec.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Spec: Backtest CLI
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 命令行参数解析
|
||||
回测脚本 SHALL 通过命令行参数接收用户输入,参数 SHALL 包含股票代码、时间范围、策略文件、回测参数等。
|
||||
|
||||
#### Scenario: 基础回测执行
|
||||
- **WHEN** 用户执行 `python backtest.py --code 000001.SZ --start-date 2024-01-01 --end-date 2025-12-31 --strategy-file strategy.py`
|
||||
- **THEN** 系统解析所有必需参数,无错误提示
|
||||
- **THEN** 开始执行回测流程
|
||||
- **THEN** 回测完成后输出统计信息到控制台
|
||||
|
||||
#### Scenario: 可选参数未指定
|
||||
- **WHEN** 用户未指定 `--cash` 参数
|
||||
- **THEN** 系统使用默认值 100000 作为初始资金
|
||||
- **WHEN** 用户未指定 `--commission` 参数
|
||||
- **THEN** 系统使用默认值 0.002 作为手续费率
|
||||
- **WHEN** 用户未指定 `--output` 参数
|
||||
- **THEN** 系统不生成 HTML 图表文件
|
||||
|
||||
#### Scenario: 必需参数缺失
|
||||
- **WHEN** 用户未提供 `--code` 参数
|
||||
- **THEN** 系统输出错误信息:"错误: 需要以下参数: --code"
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
- **WHEN** 用户未提供 `--start-date` 或 `--end-date` 参数
|
||||
- **THEN** 系统输出对应的错误信息
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
#### Scenario: 自定义参数值
|
||||
- **WHEN** 用户指定 `--cash 500000 --commission 0.001 --output result.html`
|
||||
- **THEN** 系统使用指定的 500000 作为初始资金
|
||||
- **THEN** 系统使用指定的 0.001 作为手续费率
|
||||
- **THEN** 回测完成后生成 HTML 图表到 result.html
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 数据库数据加载
|
||||
回测脚本 SHALL 从 PostgreSQL 数据库加载指定股票的历史价格数据,并自动处理复权。
|
||||
|
||||
#### Scenario: 成功加载数据
|
||||
- **WHEN** 用户指定有效的股票代码和时间范围
|
||||
- **THEN** 系统连接数据库并执行查询
|
||||
- **THEN** 返回 DataFrame,包含列: [Open, High, Low, Close, Volume, factor]
|
||||
- **THEN** DataFrame 的索引为 trade_date (DatetimeIndex)
|
||||
- **THEN** 数据已应用复权计算(price * factor)
|
||||
|
||||
#### Scenario: 数据库连接失败
|
||||
- **WHEN** 数据库连接失败(凭证错误、网络问题等)
|
||||
- **THEN** 系统捕获异常并输出错误信息:"数据库连接失败: {error}"
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
#### Scenario: 未找到股票数据
|
||||
- **WHEN** 指定的股票代码或时间范围内无数据
|
||||
- **THEN** 系统抛出 ValueError: "未找到股票 {code} 在指定时间范围内的数据"
|
||||
- **THEN** 主流程捕获异常并输出友好错误信息
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
#### Scenario: 数据验证
|
||||
- **WHEN** 数据库返回的 DataFrame 为空
|
||||
- **THEN** 系统提示数据为空并退出
|
||||
- **WHEN** 数据库返回的 DataFrame 少于 10 条记录
|
||||
- **THEN** 系统提示数据不足并退出
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 策略动态加载
|
||||
回测脚本 SHALL 支持动态加载指定路径的策略文件,并验证策略接口。
|
||||
|
||||
#### Scenario: 加载有效策略文件
|
||||
- **WHEN** 用户指定 `--strategy-file strategy.py`
|
||||
- **THEN** 系统通过 importlib 加载该模块
|
||||
- **THEN** 系统获取模块的 `calculate_indicators` 函数
|
||||
- **THEN** 系统调用模块的 `get_strategy()` 函数获取策略类
|
||||
- **THEN** 系统返回 (calculate_indicators, strategy_class) 元组
|
||||
|
||||
#### Scenario: 策略文件不存在
|
||||
- **WHEN** 用户指定的策略文件路径不存在
|
||||
- **THEN** 系统捕获 FileNotFoundError
|
||||
- **THEN** 输出错误信息:"策略文件 {file} 不存在"
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
#### Scenario: 策略接口不完整
|
||||
- **WHEN** 策略文件缺少 `calculate_indicators` 函数
|
||||
- **THEN** 系统捕获 AttributeError
|
||||
- **THEN** 输出错误信息:"策略文件 {file} 缺少 calculate_indicators 函数"
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
- **WHEN** 策略文件缺少 `get_strategy` 函数
|
||||
- **THEN** 系统捕获 AttributeError
|
||||
- **THEN** 输出错误信息:"策略文件 {file} 缺少 get_strategy 函数"
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
#### Scenario: 加载子目录中的策略
|
||||
- **WHEN** 用户指定 `--strategy-file strategies/macd_strategy.py`
|
||||
- **THEN** 系统正确加载子目录中的策略模块
|
||||
- **THEN** 系统成功获取策略类和指标计算函数
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 指标计算
|
||||
回测脚本 SHALL 在执行回测前调用策略的指标计算函数,将技术指标添加到数据集中。
|
||||
|
||||
#### Scenario: 成功计算指标
|
||||
- **WHEN** 系统调用 `calculate_indicators(data)`
|
||||
- **THEN** 函数接收包含 [Open, High, Low, Close, Volume, factor] 的 DataFrame
|
||||
- **THEN** 函数计算策略所需的指标(如 SMA, MACD, RSI)
|
||||
- **THEN** 函数返回添加了指标列的 DataFrame
|
||||
- **THEN** DataFrame 保留原始列,新增指标列
|
||||
|
||||
#### Scenario: 指标计算产生 NaN 值
|
||||
- **WHEN** 滚动窗口计算导致前 N 行的指标值为 NaN
|
||||
- **THEN** DataFrame 包含 NaN 值(系统不自动删除)
|
||||
- **THEN** Backtest 框架在回测时会跳过 NaN 值的行
|
||||
|
||||
#### Scenario: 指标计算函数抛出异常
|
||||
- **WHEN** `calculate_indicators(data)` 执行时抛出异常
|
||||
- **THEN** 主流程捕获异常
|
||||
- **THEN** 输出错误信息:"指标计算失败: {error}"
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 回测执行
|
||||
回测脚本 SHALL 使用 backtesting 库执行回测,传入数据、策略和参数。
|
||||
|
||||
#### Scenario: 成功执行回测
|
||||
- **WHEN** 系统调用 `Backtest(data, strategy_class, cash=..., commission=...).run()`
|
||||
- **THEN** Backtest 初始化时调用策略类的 `init()` 方法
|
||||
- **THEN** Backtest 逐个时间步调用策略类的 `next()` 方法
|
||||
- **THEN** 系统返回包含回测统计信息的 stats 对象
|
||||
|
||||
#### Scenario: 回测参数传递
|
||||
- **WHEN** 用户指定 `--cash 500000 --commission 0.001`
|
||||
- **THEN** Backtest 实例化时使用 cash=500000
|
||||
- **THEN** Backtest 实例化时使用 commission=0.001
|
||||
- **THEN** Backtest 实例化时使用 finalize_trades=True
|
||||
|
||||
#### Scenario: 回测运行时错误
|
||||
- **WHEN** 策略的 `next()` 方法执行时抛出异常
|
||||
- **THEN** backtesting 库捕获异常
|
||||
- **THEN** 系统输出错误信息和堆栈跟踪
|
||||
- **THEN** 系统退出并返回非零状态码
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 结果输出
|
||||
回测脚本 SHALL 将回测统计信息格式化输出到控制台,并可选生成 HTML 图表文件。
|
||||
|
||||
#### Scenario: 控制台输出
|
||||
- **WHEN** 回测成功完成
|
||||
- **THEN** 系统调用 `print_stats(stats)` 函数
|
||||
- **THEN** 系统输出回测统计信息,使用中文标签
|
||||
- **THEN** 输出内容包括:最终收益、总收益率、年化收益率、最大回撤、胜率等
|
||||
- **THEN** 数值格式化(保留 2 位小数)
|
||||
|
||||
#### Scenario: 生成 HTML 图表
|
||||
- **WHEN** 用户指定 `--output result.html`
|
||||
- **THEN** 系统调用 `bt.plot(filename='result.html', show=False)`
|
||||
- **THEN** 系统生成 HTML 文件到 result.html
|
||||
- **THEN** 系统输出提示:"图表已保存到: result.html"
|
||||
- **THEN** 图表包含价格曲线、资金曲线、买卖信号等
|
||||
|
||||
#### Scenario: 不生成 HTML 图表
|
||||
- **WHEN** 用户未指定 `--output` 参数
|
||||
- **THEN** 系统不调用 bt.plot() 方法
|
||||
- **THEN** 系统不生成任何图表文件
|
||||
- **THEN** 系统仅输出控制台统计信息
|
||||
|
||||
#### Scenario: 图表生成失败
|
||||
- **WHEN** bt.plot() 方法执行时抛出异常
|
||||
- **THEN** 系统捕获异常
|
||||
- **THEN** 系统输出警告:"图表生成失败,但回测已完成: {error}"
|
||||
- **THEN** 系统不影响控制台统计信息的输出
|
||||
- **THEN** 系统正常退出(返回状态码 0)
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 错误处理
|
||||
回测脚本 SHALL 对所有可能的错误进行捕获和处理,提供友好的错误提示。
|
||||
|
||||
#### Scenario: 数据库错误
|
||||
- **WHEN** 数据库操作抛出 sqlalchemy.exc.SQLAlchemyError
|
||||
- **THEN** 系统输出错误信息:"数据库错误: {error}"
|
||||
- **THEN** 系统退出并返回状态码 2
|
||||
|
||||
#### Scenario: 文件操作错误
|
||||
- **WHEN** 图表文件保存失败(权限、磁盘空间等)
|
||||
- **THEN** 系统输出错误信息:"文件操作错误: {error}"
|
||||
- **THEN** 系统退出并返回状态码 3
|
||||
|
||||
#### Scenario: 未预期的错误
|
||||
- **WHEN** 发生其他未捕获的异常
|
||||
- **THEN** 系统输出错误信息:"未知错误: {error}"
|
||||
- **THEN** 系统输出完整的堆栈跟踪
|
||||
- **THEN** 系统退出并返回状态码 1
|
||||
Reference in New Issue
Block a user