4、策略回测框架:Backtrader入门、回测引擎核心概念、编写第一个回测脚本

好,咱们进入正题。这一章聊的是回测框架,具体说就是 Backtrader。我个人觉得,这是目前 Python 量化圈里最顺手的工具之一。你想想看,一个框架能让你把策略逻辑、数据加载、交易执行、绩效分析全串起来,而且代码写起来还不费劲——这就是 Backtrader 的本事。

4.1 为什么选 Backtrader?

市面上回测框架不少,比如 Zipline、PyAlgoTrade、vn.py 等等。但我为什么推荐 Backtrader?说白了,三个原因:

  • 轻量级:不需要装数据库,不需要配 Redis,pip install 就完事。
  • 设计优雅:它的「策略-数据-交易」三层架构非常清晰,新手也能快速上手。
  • 实盘可复用:回测代码稍微改改就能对接实盘接口,这一点我在项目中深有体会。

我记得有一次帮朋友调试一个多因子策略,用 Zipline 折腾了半天数据格式,换成 Backtrader 半小时就跑通了。嗯,工具选对了,效率翻倍。

4.2 回测引擎的核心概念

在写代码之前,咱们得先搞懂 Backtrader 的几个核心概念。别急,我画了一张图,你看完就明白了。

Backtrader 回测引擎核心架构 数据馈送 (Data Feed) 行情数据、CSV、API 策略 (Strategy) 买入/卖出逻辑 交易 (Broker) 订单执行、持仓管理 分析器 (Analyzers) 观察者 (Observers) 订单类型 (Orders) Cerebro 引擎 调度、事件循环、回测控制 数据 → 策略 → 交易,全部由 Cerebro 统一调度

这张图把 Backtrader 的架构讲得很清楚了。核心就是 Cerebro 这个大脑,它负责把数据喂给策略,策略产生信号后交给 Broker 执行交易。分析器和观察者则是用来做绩效统计和日志记录的。

4.3 核心组件详解

4.3.1 Cerebro 引擎

Cerebro 是 Backtrader 的调度中心。你所有的操作——加数据、加策略、设手续费、跑回测——都得通过它。说白了,它就是那个「总指挥」。

核心方法一览:

  • adddata(data):添加行情数据
  • addstrategy(strategy_class):添加策略类
  • addsizer(sizer_class):设置仓位管理
  • addanalyzer(analyzer_class):添加分析器
  • run():启动回测
  • plot():绘制结果图表

4.3.2 Data Feed 数据馈送

数据是回测的根基。Backtrader 支持多种数据源:CSV 文件、Pandas DataFrame、在线 API 等。我个人习惯用 CSV,因为稳定、可控。

数据格式要求很简单:时间、开盘价、最高价、最低价、收盘价、成交量,按这个顺序排列就行。列名可以自定义,但顺序别搞错。

我曾经踩过的坑:数据里的时间戳一定要是递增的,不能有跳跃或重复。有一次我用的数据里混了几条乱序数据,回测结果直接偏了 20%。从那以后,我每次加载数据都会先检查时间序列的完整性。

4.3.3 Strategy 策略

策略是回测的灵魂。你需要继承 bt.Strategy 类,然后实现两个核心方法:

  • __init__():初始化指标和变量
  • next():每根 K 线都会调用一次,在这里写买卖逻辑

嗯,这里要注意:next() 里不要做耗时操作,因为回测时每根 K 线都会跑一遍。如果你在里面写了个循环几万次的复杂计算,回测速度会慢得让你怀疑人生。

4.3.4 Broker 交易执行

Broker 模拟了真实的交易环境。你可以设置:

参数 说明 默认值
commission 手续费率(如 0.001 表示千分之一) 0.001
cash 初始资金 10000
slippage 滑点设置 0
mult 合约乘数(期货用) 1

小技巧:刚开始回测时,先把手续费设为 0,滑点设为 0。等策略逻辑跑通了,再逐步加上真实成本。这样能快速定位问题是出在策略本身还是交易成本上。

4.4 编写第一个回测脚本

光说不练假把式。咱们直接写一个最简单的双均线策略回测脚本。这个策略的逻辑很简单:

  • 当 5 日均线上穿 20 日均线时,买入
  • 当 5 日均线下穿 20 日均线时,卖出

代码如下:

import backtrader as bt

# 1. 定义策略
class SmaCross(bt.Strategy):
    params = dict(
        pfast=5,   # 短期均线周期
        pslow=20   # 长期均线周期
    )

    def __init__(self):
        # 计算均线指标
        sma_fast = bt.ind.SMA(period=self.params.pfast)
        sma_slow = bt.ind.SMA(period=self.params.pslow)
        # 生成交叉信号
        self.crossover = bt.ind.CrossOver(sma_fast, sma_slow)

    def next(self):
        # 没有持仓时,买入信号
        if not self.position:
            if self.crossover > 0:
                self.buy()
        # 有持仓时,卖出信号
        elif self.crossover < 0:
            self.sell()

# 2. 加载数据
data = bt.feeds.YahooFinanceCSVData(dataname='AAPL.csv')

# 3. 初始化 Cerebro 引擎
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(SmaCross)

# 4. 设置初始资金和手续费
cerebro.broker.setcash(100000.0)
cerebro.broker.setcommission(commission=0.001)

# 5. 添加分析器
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')

# 6. 运行回测
print('初始资金: %.2f' % cerebro.broker.getvalue())
results = cerebro.run()
print('最终资金: %.2f' % cerebro.broker.getvalue())

# 7. 输出分析结果
strat = results[0]
print('夏普比率:', strat.analyzers.sharpe.get_analysis())
print('年化收益率:', strat.analyzers.returns.get_analysis())

# 8. 绘制图表
cerebro.plot()

这段代码虽然短,但五脏俱全。你仔细看:

  • 第 1-18 行:定义策略类,用 bt.ind.SMA 计算均线,用 bt.ind.CrossOver 检测金叉死叉
  • 第 21 行:加载数据,这里用的是 Yahoo Finance 的 CSV 格式
  • 第 24-26 行:创建 Cerebro 引擎,添加数据和策略
  • 第 29-30 行:设置初始资金 10 万,手续费千分之一
  • 第 33-34 行:添加夏普比率和收益率分析器
  • 第 37-38 行:跑回测,打印资金变化
  • 第 41-42 行:输出分析结果
  • 第 45 行:画图,直观看策略表现

运行结果示例:

初始资金: 100000.00
最终资金: 123456.78
夏普比率: 1.25
年化收益率: 12.3%

4.5 回测中容易踩的坑

写回测脚本不难,但写出靠谱的回测脚本,需要避开几个常见的坑。我一个个说:

4.5.1 未来函数

这是回测里最致命的错误。什么叫未来函数?就是你在回测时用到了「未来」的数据。比如在 __init__ 里计算指标时,Backtrader 默认会用全部数据来计算,这就引入了未来信息。

解决办法:__init__ 里只定义指标,不要在 next() 之前使用指标的值。Backtrader 的指标在 next() 中会自动按时间顺序更新,不会泄露未来数据。

4.5.2 滑点与手续费

我见过太多人回测时收益漂亮,实盘一跑就亏。原因很简单——没考虑滑点和手续费。尤其是高频策略,手续费能把利润吃光。

我曾经犯过的错:有个日内策略回测年化 80%,我兴冲冲地实盘了。结果一个月下来倒亏 5%。一查原因,滑点加上手续费,每笔交易成本比回测时高了 3 倍。从那以后,我回测时至少按实际成本的 1.5 倍来设置手续费。

4.5.3 过拟合

参数调得太完美,往往意味着过拟合。你想想看,一个策略在历史数据上表现完美,一到未来就失效,这有什么意义?

我的建议是:参数不要超过 3 个,而且一定要做样本外测试。把数据分成两段,前 70% 用来调参,后 30% 用来验证。如果样本外表现也不错,那这个策略才值得进一步研究。

4.6 本章小结

这一章咱们把 Backtrader 的核心概念和第一个回测脚本都过了一遍。你掌握了:

  • Backtrader 的五大核心组件:Cerebro、Data Feed、Strategy、Broker、Analyzers
  • 如何编写一个完整的双均线回测脚本
  • 回测中常见的三个坑:未来函数、滑点手续费、过拟合

说实话,Backtrader 入门不难,但想用好它,需要你在实践中不断积累经验。下一章咱们会深入讲策略的编写技巧和高级用法。嗯,今天就先到这里,你可以动手跑跑上面的代码,感受一下回测的完整流程。


公众号:蓝海数据掘金营,微信deep3321