回测是量化交易不可缺少的组成部分,本文是 vnpy 回测引擎原理笔记

回测 Demo

vnpy 官方提供了两个 jupyter notebook 作为 Demo,代码文件在源代码中的 examples/cta_backtesting 目录:

  • backtesting.ipynb: 常规的单标 CTA
  • portfolio.ipynb: 组合回测

backtesting.ipynb:

创建回测:

engine = BacktestingEngine()
engine.set_parameters(...) # 回测参数
engine.add_strategy(AtrRsiStrategy, {}) # 添加策略

运行回测

engine.load_data() # 载入数据
engine.run_backtesting() # 跑回测, 得到成交记录
df = engine.calculate_result() # 从成交记录计算盯市盈亏
engine.calculate_statistics() # 计算统计指标
engine.show_chart() # 显示结果

引擎(BacktestingEngine)主要函数及大体实现逻辑

set_parameters: 设置并保存参数,包括要回测的标的,回测时间等

add_strategy: 添加策略类(策略类需是 CtaTemplate的子类),实例化策略对象

load_data: 载入历史数据, 保存在 self.history_data;这里会调用 load_bar_data 或者 load_tick_data 从数据库读取数据

run_backtesting: 跑回测,本身支持 Tick / K 线;但是如果使用图形界面,则只支持 K 线。运行流程:

  • 调用 strategy (CtaTemplate子类) 的 on_init 函数进行初始化
  • 如果 strategy 调用了 self.load_bar,engine 则会把对应的历史数据推送给策略进行初始化
  • 完成后 engine 标记 self.strategy.inited = True
  • 下一步,engine 调用 on_start 函数启动策略
  • 启动完成后,engine 标记 self.strategy.trading = True
  • 对于剩下的数据,调用 new_bar / new_tick 将剩下的数据用于回测

calculate_result: 把交易聚合成逐日盈亏

calculate_statistics: 基于逐日盈亏计算统计指标,比较多地使用了 pandas

show_chart: 用 matplotlib 可视化指标

new_bar / new_tick:

  • 缓存最新的 K 线 / Tick 数据,模拟撮合限价单(cross_limit_order)和停止单(cross_stop_order)
  • 将最新的 K 线 / Tick 推给 strategy.on_bar (防止未来函数,因为实盘走完 K 线之后才会下单)
  • 更新收盘价,方便计算逐日盯市盈亏

cross_limit_order (K 线)

  • long_cross_price = self.bar.low_price, 对于 long (下在 bid), 如果最低价大于买单,则有成交机会
  • short_cross_price = self.bar.high_price, 对于 short (下在 ask), 如果最高价大于卖单,则有成交机会
  • long_best_price = self.bar.open_price, short_best_price = self.bar.open_price, 按 taker 模拟撮合, 可能按开盘价成交
  • 模拟撮合,遍历所有 limit order:
    • 对于状态等于 Status.SUBMITTING 的,更新为 Status.NOTTRADED,调用 on_order 回调
    • order.price >= long_cross_price/order.price <= short_cross_price, 计算是否成交, 不成交则直接 continue
    • 若成交, 则认为全部成交, 更新状态为 Status.ALLTRADED,调用 on_order 回调
    • 按照限价单的 price, 以及 best_price, 计算成交价格
    • 生成成交记录 TradeData, 维护 strategy.pos, 调用 on_trade 回调

cross_stop_order (K 线)

  • long_cross_price = self.bar.high_price, short_cross_price = self.bar.low_price, short_best_price = long_best_price = self.bar.open_price
  • 模拟撮合,遍历所有 stop order:
    • order.price <= long_cross_price/order.price >= short_cross_price, 停止单触发,新增一个限价单OrderData
    • 按照停止单的 price, 以及 best_price, 计算成交价格
    • 生成成交记录 TradeData
    • 更新原停止单状态为, StopOrderStatus.TRIGGERED, 调用 on_stop_order 回调
    • 对生成的限价单, 调用 on_order 回调
    • 维护 strategy.pos, 调用 on_trade 回调
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐