2、Backtrader核心概念:Cerebro引擎、Data Feed数据源、Strategy策略、Broker经纪商、Analyzer分析器
说实话,刚接触Backtrader的时候,我也有点懵。一堆英文术语砸过来,Cerebro、Data Feed、Strategy……这都啥跟啥?
别急。咱们一个一个拆开看。
Backtrader的核心,说白了就五个组件。它们像齿轮一样咬合在一起,驱动整个回测流程。我习惯把这五个组件比作一个「交易工厂」:
2.1 Cerebro 引擎——回测的总指挥
Cerebro,西班牙语里是「大脑」的意思。Backtrader 起这个名字,用意很明显——它就是整个回测框架的大脑。
我刚开始用的时候,总觉得 Cerebro 像个黑盒子。你把数据扔进去,策略塞进去,它就开始跑。跑完了给你一堆结果。后来我慢慢理解了,它其实就干三件事:
- 组装:把数据源、策略、经纪商、分析器全部注册进来
- 驱动:按时间顺序一根K线一根K线地往前推
- 输出:跑完后把分析结果吐出来
代码上怎么用?很简单:
import backtrader as bt
# 创建引擎
cerebro = bt.Cerebro()
# 添加数据、策略、分析器(后面会讲)
cerebro.adddata(data)
cerebro.addstrategy(MyStrategy)
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
# 启动回测
results = cerebro.run()
# 查看结果
print(results[0].analyzers.sharpe.get_analysis())
2.2 Data Feed 数据源——回测的原材料
没有数据,策略就是空中楼阁。Data Feed 就是 Backtrader 里用来加载和管理行情数据的模块。
Backtrader 支持的数据格式挺多的:CSV、Pandas DataFrame、甚至直接从 Yahoo Finance 拉数据。我个人最常用的是 Pandas DataFrame,因为清洗数据方便。
来看一个加载 CSV 的例子:
import backtrader as bt
# 从 CSV 加载数据
data = bt.feeds.GenericCSVData(
dataname='./data/000300.csv',
dtformat='%Y-%m-%d',
datetime=0,
open=1,
high=2,
low=3,
close=4,
volume=5,
openinterest=-1 # -1 表示没有该字段
)
cerebro.adddata(data)
data.start() 可以快速检查。
你想想看,如果数据里有一根K线的开盘价写成了收盘价,策略跑出来可能就完全变味了。数据质量,永远是回测的第一道关。
2.3 Strategy 策略——你的交易逻辑
Strategy 是 Backtrader 里最核心的部分,也是你花时间最多的地方。说白了,你在这里写的就是「什么时候买、什么时候卖」的规则。
一个策略类需要继承 bt.Strategy,然后实现两个关键方法:
__init__():初始化指标和变量next():每根K线都会调用一次,在这里写买卖逻辑
看个最简单的双均线策略:
class SmaCross(bt.Strategy):
params = (('pfast', 10), ('pslow', 30),)
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()
next() 方法里,self.data 指向当前K线。self.data.close[0] 是当前收盘价,self.data.close[-1] 是上一根K线的收盘价。这个索引方式我一开始老搞混,后来记住「0是当前,负数是过去」就顺了。
2.4 Broker 经纪商——模拟真实交易环境
Broker 负责模拟真实的交易环境:佣金、滑点、保证金、持仓管理……这些都由它来管。
Backtrader 默认自带一个 Broker,但你可以自定义。我一般会做这几件事:
# 设置初始资金
cerebro.broker.setcash(100000.0)
# 设置佣金(万二)
cerebro.broker.setcommission(commission=0.0002)
# 设置滑点(0.1%)
cerebro.broker.set_slippage_perc(perc=0.001)
嗯,这里要注意。很多新手回测时跑出漂亮的曲线,一上实盘就亏钱。为什么?因为回测里没考虑滑点和佣金。我曾经见过一个策略,回测年化30%,加上千一的佣金和0.1%的滑点后,直接变成15%。
2.5 Analyzer 分析器——回测结果的体检报告
策略跑完了,结果怎么样?总得有个评价标准吧。Analyzer 就是干这个的。
Backtrader 内置了不少分析器:
| 分析器名称 | 作用 | 常用指标 |
|---|---|---|
| SharpeRatio | 夏普比率 | 风险调整后收益 |
| DrawDown | 回撤分析 | 最大回撤、回撤时长 |
| TradeAnalyzer | 交易统计 | 胜率、盈亏比、交易次数 |
| AnnualReturn | 年化收益 | 每年收益率 |
| SQN | 系统质量指数 | 策略稳定性 |
用法也很直接:
# 添加分析器
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
# 运行
results = cerebro.run()
# 提取结果
strat = results[0]
print('夏普比率:', strat.analyzers.sharpe.get_analysis())
print('最大回撤:', strat.analyzers.drawdown.get_analysis()['max']['drawdown'])
print() 出来看看结构,再写提取代码。别凭记忆写,容易踩坑。
2.6 五个组件如何协同工作?
现在我们把五个组件串起来,看看完整的回测流程:
- Data Feed 把历史数据喂给 Cerebro
- Cerebro 按时间顺序逐根K线调用 Strategy 的
next()方法 - Strategy 根据指标判断买卖,通过
self.buy()/self.sell()发出指令 - Broker 接收指令,模拟成交、扣佣金、更新持仓
- 回测结束后,Analyzer 统计所有交易数据,生成报告
整个过程,Cerebro 就像个项目经理,协调各方干活。你只需要把各个组件「注册」进去,剩下的交给它。
next() 里直接修改了全局变量,导致多次回测结果不一致。记住,Backtrader 的回测是确定性的,同样的输入必须得到同样的输出。如果你发现两次跑的结果不一样,大概率是代码里有「状态残留」。
好了,五个核心概念讲完了。你可能会觉得有点多,但别担心。后面每个章节我们都会深入一个组件,手把手带你写代码。先把这张图刻在脑子里,后面就顺了。