股票量化交易进阶003_回测框架backtrader(三)


前言

通过一些技能更深一步掌握backtrader框架


一、技能要点

1.如何获取一个line的长度

有两种不同的line的长度,一种是len,这个是backtrader已经处理过的数据的长度;一种是buflen(),这个是backtrader预先加载的数据的长度。

len(line):计算出backtrader已经处理(运行)的长度,如prenext,next等运行的长度
line.buflen()计算的是backtrader加载的line的总的数目
一般情况下,是buflen大于len,如果两者想等了,可能是这个bar是最后一个bar;也有可能是某些情况下的实盘交易造成的。

可以自行添加打印代码,看下实际情况:

 def next(self):
        self.bar_all = self.datas[0].close.buflen()
        self.bar_executed = len(self)
        print('===总共处理%s条bar,目前已处理了%s条' % (self.bar_all, self.bar_executed))

打印结果:
===总共处理1555条bar,目前已处理了1553条
2022-05-25, Close, 14.39
===总共处理1555条bar,目前已处理了1554条
2022-05-26, Close, 14.19
===总共处理1555条bar,目前已处理了1555条
2022-05-27, Close, 14.18
Final Portfolio Value: 513.35

2. line的延迟索引

self.data[0].close[-1] 操作符用于在next逻辑阶段提取值。
self.data[0].close(-1) 还代表什么呢?
Lines支持通过延迟索引来访问延时的Lines对象。
例如我们考虑一个策略,它对比前一收盘价和当前简单移动平均线值。除了可以在next迭代中手动执行,还可以在__init__阶段生成预处理的线对象:

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)
        self.cmpval = self.data.close(-1) > self.sma

    def next(self):
        if self.cmpval[0]:
            print('Previous close is higher than the moving average')

这里就用到了Lines的延迟索引,通过self.data.close(-1)将close进行复制,并且做延时-1,生成了1个延迟的Lines对象。

3. backtrader的运算符

python的逻辑运算符都被Backtrader覆盖了,使其可以直接作用于线对象整体,进行矢量化运算
在这里插入图片描述
tips:
cmp(x,y) 函数用于比较2个对象,如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1。

class MyStrategy(bt.Strategy):
    params = dict(period1=20, period2=25, period4=10, period3=5)

    def __init__(self):
        self.sma1 = bt.ind.MovingAverageSimple(
            self.data.close,
            period=self.params.period1
        )

        self.sma2 = bt.ind.MovingAverageSimple(
            self.sma1,
            period=self.params.period2
        )

        # 通过算数计算创建新指标线
        self.something = self.sma2 - self.sma1 + self.data.close

        self.sma3 = bt.ind.MovingAverageSimple(
            self.something,
            period=self.params.period3
        )
        self.something2 = self.sma3 - self.sma2 + self.data.close
        self.sma4 = bt.ind.MovingAverageSimple(
            self.something2,
            period=self.params.period4
        )
        self.greater = self.sma3 > self.sma1

        self.buysign = bt.And(self.sma1 > self.data.close, self.sma1 > self.data.high)
        self.high_or_low = bt.If(self.sma1 > self.data.close, self.data.low, self.data.high)
        self.high_or_30 = bt.If(self.sma1 > self.data.close, 30.0, self.data.high)
        # 生成开盘价除以收盘价形成的线,若某天的收盘价为0,则最终指标该日取 99999
        self.testIndicator = bt.DivByZero(self.data.open, self.data.close, zero=99999)
        # 若分母为0,则取8888;若分子分母都为0,取99999
        self.testIndicator2 = bt.DivZeroByZero(self.data.open, self.data.close, 8888, 99999)

    def next(self):
        if self.sma1[0] > 6.0:
            print('sma is greater than 6.0')

        if self.sma1[0] > self.data.close:
            print('sma is above the close price')

        if self.buysign[0]:
            print('buy sig is True')
        else:
            print('buy sig is False')

        if self.high_or_low[0] > 5.0:
            print('high_or_low is greater than 5.0')

打印结果:

sma is greater than 6.0
sma is above the close price
buy sig is False
high_or_low is greater than 5.0

图表:
在这里插入图片描述

4.添加绘图指标

正常我们只需要在调用cerebro.run()后,再添加一行代码cerebro.plot(),就可以实现默认的图表展示。

现在我们要添加更多的绘图指标,怎么做?

除了默认显示的Broker,Trades,Volume,SMA等绘图指标,backtrader还添加了如下指标:

加权移动均值(WeightedMovingAverage)
慢速随机指标(StochasticSlow)
MACD
RSI
应用于RSI的简单移动均值(SimpleMovingAverage)
真实波动幅度均值(AverageTrueRange )

如果要显示,只需要在代码策略def __init__(self)中添加下面代码就可以

		# 绘图显示的指标
        bt.indicators.ExponentialMovingAverage(self.datas[0], period = 25)
        bt.indicators.WeightedMovingAverage(self.datas[0], period = 25,
                                            subplot = True)
        bt.indicators.StochasticSlow(self.datas[0])
        bt.indicators.MACDHisto(self.datas[0])
        rsi = bt.indicators.RSI(self.datas[0])
        bt.indicators.SmoothedMovingAverage(rsi, period = 10)
        bt.indicators.ATR(self.datas[0], plot = False)

添加后的图表展示:
在这里插入图片描述

5.绘图报错

可能在backtrader回测添加绘图cerebro.plot()时,会碰到这个错误:

遇到 ImportError: cannot import name ‘warnings‘ from ‘matplotlib.dates‘

如果碰到也不用过于担心,可以根据报错信息,找到对应报错文件,进行修改
在这里插入图片描述
打开locator.py文件按以下修改:
在这里插入图片描述
再次运行程序,就能正常展示绘图了。

总结

有些技能要点,实际回测中不常用到的可以先不用细看,知道有这么回事就知道,待实际需要用到再细嚼。