8.1 KiB
8.1 KiB
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