前面的3篇文章我们分别介绍了Cerebro、Data相关类、Strategy类,基本上Backtrader的框架已经具备,从这篇文章开始我们介绍Backtrader的重要部件,从最重要额Indicator开始。
老规矩,从家谱图开始。
家谱图

是不是很眼熟,对了,和Strategy/Data继承关系基本就是一样的,所以从面向对象来说,他和Strategy/Data基本上就是亲兄弟。在文章2中就明确说了,在Backtrader中,一切都是数据。
另外,看了前面几篇文章,对Backtrader的元类套路很熟悉了吧,后面就不细讲了,有兴趣的话可以对照Strategy来读代码(元类的继承关系以及代码是一样的),后面只讲特殊的处理。
Indicator的实例化和初始化
大家还记得Indicator什么时候实例化的?在Strategy初始化的时候,请参见咱们Strategy代码解读中的MyCustomStrategy的__init__函数:
# 增加MovingAverageSimple指标
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.maperiod)
然后又是元类MetaBase的一整套动作(doprenew、donew、dopreinit、doinit和dopostinit),具体对照Strategy的代码解读对照,说明下大概:
-
在MetaLineSeries的donew中,初始化一个LineBuffer存储在lines里面,可以看出,Indicator本质上上也是数据源,拥有Lines(家谱图中也可以看到)。初始化的时候只有一个LineBuffer.
-
在MetaLineIterator的donew中,会将参数中输入的数据(例子中的self.datas[0])加入datas容器(既然是复数,那么可以输入多个数据,如果有的指标需要多个数据的话)中,这个数据是后续计算Indicator的基本数据。如果参数不输入数据咋办呢?就会将这个Indicator的owner的缺省data加入。Indicator的owner是谁?是MyCustomStrategy,因为是它初始化了这个Indicator。那么MyCustomStrategy的缺省data,也就是是MyCustomStrategy的第一个数据(self.datas[0])。那MyCustomStrategy的第一个数据是谁?就是Cerebro加入的第一个数据。所以不输入这个参数数据也可以(文章2有说明,代码原理在这里)。
-
以上实例化完成,就开始初始化了__init__了,首先就进入到MovingAverageSimple的__init__了:
def __init__(self): # Before super to ensure mixins (right-hand side in subclassing) # can see the assignment operation and operate on the line self.lines[0] = Average(self.data, period=self.p.period) super(MovingAverageSimple, self).__init__()- 首先就是在实例化一个Average,各种均线指标是非常重要也使用广泛的指标。Average是非常基本的一个类,提供的是简单的算术平均结果,这个记住如何使用就行了,具体代码就不看了。这里记住,第一个line就是Average实例化的时候返回的一个LineBuffer实例,也就是具体的数据在Average中计算。可以看出,数据的计算是在初始化的时候。
- 调用父类的__init__,没做啥。
-
下面就是dopostinit了,在MetaLineIterator的dopostinit中,将自己(初始化后的SimpleMovingAverage实例)传递给自己的owner(MyCustomStrategy),MyCustomStrategy就可以使用这个Indicator了。
至此初始化完成。
Indicator的使用
Indicator的代码流程
前面一再强调,Indicator逻辑(继承关系)上和Data是一样的,所以它的使用基本和Data类是一样的。区别在于:Data的数据通过从元素数据(比如PandaData中 获取),而Indicator的数据来自于计算(比如本例中计算的移动平均值)。有了数据之后,我们循着它代码来看,这些数据是如何使用的。
在Cerebro以及Strategy的代码解读中,我们可以看出Indicator的使用轨迹,从Cerebro的runstrategies->_runonce–>Strategy的_oncepost,代码:
def _oncepost(self, dt):
for indicator in self._lineiterators[LineIterator.IndType]:
if len(indicator._clock) > len(indicator):
indicator.advance()
首先要调用Indicator的Advance:
def advance(self, size=1):
# Need intercepting this call to support datas with
# different lengths (timeframes)
if len(self) < len(self._clock):
self.lines.advance(size=size)
可以看出,直接就到Indicator所有line的advance了,line(LineBuffer)的advance干啥了,PandasData类详解的时候说明过:所有line索引idx后移,长度计数增加。这里只改索引,不涉及底层数据。也就是当前数据移到下一个了。这个数据不断后移,移到大于最小周期之后,就进入MyCustomStrategy的next了,在这个next中,可以直接对Indicator的指标数据应用进行处理:
def next(self):
# 记录下本次的close和sma值
self.log('Close:%.3f' % self.data.close[0])
self.log('sma:%.3f' % self.sma[0])
# 检查是否还有订单未处理,有的话等等
if self.order:
return
#Check if we are in the market
if not self.position:
# 大于均线就买
if self.dataclose[0] > self.sma[0]:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
else:
if self.dataclose[0] < self.sma[0]:
# 小于均线卖卖卖!
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2n

本文深入探讨了Backtrader库中的Indicator组件,包括其初始化过程、如何在Strategy中使用以及如何开发自定义指标。Indicator在Strategy的__init__函数中实例化,计算值在初始化时确定,使用时在next函数中应用。文中详细解析了Indicator的生命周期,如LineBuffer的管理、数据计算和存储,并提供了实例代码展示Indicator的创建、使用和绘图控制。此外,还介绍了如何处理不同时间粒度的数据以及混合操作。文章最后给出了一个完整的示例,展示了如何定义和使用一个自定义的OverUnderMovAv指标。
最低0.47元/天 解锁文章
1007





