1
0
Files
2026-01-27 18:30:41 +08:00

9.9 KiB
Raw Permalink Blame History

Spec: Strategy Loading

ADDED Requirements

Requirement: 策略文件接口

策略文件 SHALL 提供两个必需的接口:指标计算函数和策略类获取函数。

Scenario: 标准策略文件结构

  • WHEN 用户创建策略文件
  • THEN 文件 SHALL 包含 calculate_indicators(data) 函数
  • THEN 文件 SHALL 包含 get_strategy() 函数
  • THEN 文件 SHALL 包含一个继承 backtesting.Strategy 的类
  • THEN 所有三个组件 SHALL 在同一文件中

Scenario: calculate_indicators 函数签名

  • WHEN 主流程调用 calculate_indicators(data)
  • THEN 函数接收一个参数data (pandas.DataFrame)
  • THEN 函数返回一个 pandas.DataFrame
  • THEN 返回的 DataFrame SHALL 包含原始列和新增的指标列
  • THEN 函数 SHALL 修改输入的 DataFrame不创建副本

Scenario: get_strategy 函数签名

  • WHEN 主流程调用 get_strategy()
  • THEN 函数不接收参数
  • THEN 函数返回一个类对象
  • THEN 返回的类 SHALL 继承自 backtesting.Strategy

Requirement: 指标计算函数

calculate_indicators 函数 SHALL 计算策略所需的技术指标,并将结果添加到 DataFrame 中。

Scenario: SMA 指标计算

  • WHEN 策略需要简单移动平均线指标
  • THEN 函数使用 data['Close'].rolling(window=N).mean() 计算
  • THEN 函数将结果存储为 data['smaN']
  • THEN N 为具体的周期(如 10, 30, 60, 120

Scenario: MACD 指标计算

  • WHEN 策略需要 MACD 指标
  • THEN 函数使用 data['Close'].ewm(span=12).mean() 计算 EMA12
  • THEN 函数使用 data['Close'].ewm(span=26).mean() 计算 EMA26
  • THEN 函数计算 MACD = EMA12 - EMA26
  • THEN 函数计算 Signal = MACD.ewm(span=9).mean()
  • THEN 函数将结果存储为 data['macd'], data['macd_signal'], data['macd_hist']

Scenario: RSI 指标计算

  • WHEN 策略需要 RSI 指标
  • THEN 函数计算价格变化 delta = data['Close'].diff()
  • THEN 函数计算 gain = delta.where(delta > 0, 0)
  • THEN 函数计算 loss = -delta.where(delta < 0, 0)
  • THEN 函数计算平均收益和平均损失
  • THEN 函数计算 RS = average_gain / average_loss
  • THEN 函数计算 RSI = 100 - (100 / (1 + RS))
  • THEN 函数将结果存储为 data['rsi']

Scenario: 多指标计算

  • WHEN 策略需要多个技术指标
  • THEN 函数按顺序计算每个指标
  • THEN 函数将所有指标列添加到 DataFrame
  • THEN DataFrame 最终包含原始列 + 所有指标列
  • THEN 计算顺序 SHALL 遵循指标间的依赖关系(如 MACD 依赖 EMA

Scenario: 指标列命名约定

  • WHEN 函数添加指标列到 DataFrame
  • THEN 列名 SHALL 使用小写和下划线(如 sma10, macd_signal
  • THEN 列名 SHALL 与策略类的 init() 方法中引用的名称一致
  • THEN 列名 SHALL 避免与原始列冲突

Requirement: 策略类定义

策略类 SHALL 继承 backtesting.Strategy,并实现 init()next() 方法。

Scenario: 策略类继承

  • WHEN 用户定义策略类
  • THEN 类 SHALL 显式继承 backtesting.Strategy
  • THEN 类 SHALL 定义类属性作为可配置参数
  • THEN 类名 SHALL 使用大驼峰命名(如 SmaCross, MacdStrategy

Scenario: init 方法实现

  • WHEN Backtest 框架初始化策略时
  • THEN 系统调用策略类的 init() 方法
  • THEN init() 方法 SHALL 使用 self.I() 注册指标
  • THEN self.I(lambda x: x, self.data.column_name) SHALL 引用 DataFrame 中的指标列
  • THEN init() 方法 SHALL 不执行数据计算

Scenario: next 方法实现 - 金叉买入

  • WHEN 短期均线上穿长期均线(金叉)
  • THEN next() 方法 SHALL 调用 self.position.close() 平仓
  • THEN next() 方法 SHALL 调用 self.buy() 开多仓
  • THEN next() 方法 SHALL 使用 crossover() 函数检测交叉

Scenario: next 方法实现 - 死叉卖出

  • WHEN 短期均线下穿长期均线(死叉)
  • THEN next() 方法 SHALL 调用 self.position.close() 平仓
  • THEN next() 方法 SHALL 调用 self.sell() 开空仓
  • THEN next() 方法 SHALL 使用 crossover() 函数检测交叉

Scenario: next 方法实现 - 避免重复开仓

  • WHEN 策略已持有多仓,且买入信号触发
  • THEN next() 方法 SHALL 先调用 self.position.close()
  • THEN next() 方法 SHALL 再调用 self.buy()
  • THEN 系统 SHALL 自动处理仓位管理(不重复开仓)

Scenario: 可配置策略参数

  • WHEN 策略类定义类属性
  • THEN 类属性 SHALL 作为策略参数(如 short_period = 10
  • THEN Backtest 框架 SHALL 自动访问这些属性
  • THEN 参数 SHALL 可通过 Backtest 构造函数覆盖

Requirement: 策略类指标引用

策略类的 init() 方法 SHALL 正确引用 DataFrame 中计算好的指标列。

Scenario: 引用 SMA 指标

  • WHEN DataFrame 包含 sma10sma30
  • THEN init() 方法注册 self.sma_short = self.I(lambda x: x, self.data.sma10)
  • THEN init() 方法注册 self.sma_long = self.I(lambda x: x, self.data.sma30)
  • THEN next() 方法 SHALL 通过 self.data.sma10self.data.sma30 访问指标

Scenario: 引用 MACD 指标

  • WHEN DataFrame 包含 macdmacd_signal
  • THEN init() 方法注册 self.macd = self.I(lambda x: x, self.data.macd)
  • THEN init() 方法注册 self.signal = self.I(lambda x: x, self.data.macd_signal)
  • THEN next() 方法 SHALL 通过 self.data.macdself.data.macd_signal 访问指标

Scenario: 引用 RSI 指标

  • WHEN DataFrame 包含 rsi
  • THEN init() 方法注册 self.rsi = self.I(lambda x: x, self.data.rsi)
  • THEN next() 方法 SHALL 通过 self.data.rsi 访问指标
  • THEN 策略逻辑 SHALL 使用 RSI 阈值生成信号(如 RSI > 70 超买)

Scenario: 指标列不存在

  • WHEN 策略类引用的列名不存在于 DataFrame
  • THEN Backtest 框架抛出 KeyError
  • THEN 主流程捕获异常并输出错误信息:"指标列 {column} 不存在"
  • THEN 系统退出并返回非零状态码

Requirement: 动态加载机制

主流程 SHALL 使用 importlib 动态加载策略文件模块。

Scenario: 加载顶层策略文件

  • WHEN 用户指定 --strategy-file strategy.py
  • THEN 系统使用 spec_from_file_location('strategy', 'strategy.py') 创建规范
  • THEN 系统使用 module_from_spec(spec) 创建模块对象
  • THEN 系统使用 spec.loader.exec_module(module) 执行模块
  • THEN 系统成功获取 module.calculate_indicatorsmodule.get_strategy

Scenario: 加载子目录策略文件

  • WHEN 用户指定 --strategy-file strategies/macd_strategy.py
  • THEN 系统使用 spec_from_file_location('strategies.macd_strategy', 'strategies/macd_strategy.py')
  • THEN 模块名使用点号分隔(反映目录结构)
  • THEN 系统成功加载子目录中的策略模块

Scenario: 模块命名空间隔离

  • WHEN 系统动态加载多个策略文件
  • THEN 每个策略模块 SHALL 有独立的命名空间
  • THEN 模块间 SHALL 不共享全局变量
  • THEN 系统通过 getattr(module, name) 明确访问函数和类

Scenario: 策略文件导入错误

  • WHEN 策略文件包含语法错误或导入错误
  • THEN exec_module() 抛出 ImportError 或 SyntaxError
  • THEN 主流程捕获异常
  • THEN 系统输出错误信息:"策略文件 {file} 加载失败: {error}"
  • THEN 系统退出并返回非零状态码

Requirement: 策略接口验证

主流程 SHALL 验证策略文件是否符合接口要求。

Scenario: 验证 calculate_indicators 存在

  • WHEN 系统加载策略模块
  • THEN 系统使用 hasattr(module, 'calculate_indicators') 检查函数
  • WHEN 函数不存在
  • THEN 系统抛出 AttributeError
  • THEN 主流程捕获并输出:"策略文件 {file} 缺少 calculate_indicators 函数"

Scenario: 验证 get_strategy 存在

  • WHEN 系统加载策略模块
  • THEN 系统使用 hasattr(module, 'get_strategy') 检查函数
  • WHEN 函数不存在
  • THEN 系统抛出 AttributeError
  • THEN 主流程捕获并输出:"策略文件 {file} 缺少 get_strategy 函数"

Scenario: 验证 get_strategy 返回类

  • WHEN 系统调用 get_strategy()
  • THEN 系统使用 isinstance(returned, type) 检查返回值
  • WHEN 返回值不是类
  • THEN 系统抛出 TypeError
  • THEN 主流程捕获并输出:"get_strategy() 必须返回一个类"

Scenario: 验证策略类继承

  • WHEN 系统获取策略类
  • THEN 系统使用 issubclass(strategy_class, backtesting.Strategy) 检查继承
  • WHEN 策略类未继承 backtesting.Strategy
  • THEN 系统抛出 TypeError
  • THEN 主流程捕获并输出:"策略类必须继承 backtesting.Strategy"

Requirement: 策略文件示例

系统 SHALL 提供策略模板文件作为开发者参考。

Scenario: 提供策略模板

  • WHEN 用户查看 strategy.py 文件
  • THEN 文件 SHALL 包含完整的策略示例SMA 双均线交叉)
  • THEN 文件 SHALL 包含清晰的注释说明每个接口的用途
  • THEN 文件 SHALL 包含代码示例指标计算函数、get_strategy、策略类

Scenario: 策略文件文档

  • WHEN 策略文件开头有文档字符串
  • THEN 文档 SHALL 描述策略逻辑
  • THEN 文档 SHALL 列出需要的指标
  • THEN 文档 SHALL 说明参数含义(如 short_period, long_period

Scenario: 策略参数说明

  • WHEN 策略类定义类属性
  • THEN 每个属性 SHALL 有注释说明(如 short_period = 10 # 短期均线周期
  • THEN 参数 SHALL 使用有意义的名称(不是 param1, param2