量化交易学习(三十三)backtrader文档——指标的使用

今天这篇是backtrader文档的学习笔记。主要介绍了指标的概念。

官方文档链接:https://www.backtrader.com/docu/induse/

使用指标

指标可以在平台的两个地方使用:

  • 策略内部
  • 其他指标内部

指标的行为

  1. 指标始终在策略类的__init__中实例化
  2. 指标值(或其派生值)在next方法中被使用/查看

有一个重要的公理需要注意:

  • 任何在__init__期间声明的 Indicator(或其派生类)都将在调用next方法之前预先计算。

init VS next

具体细节如下:

  • 在__init__方法中任何涉及线对象的操作都会生成另一个线对象
  • 在next方法中任何涉及line对象的操作都会产生常规的 Python 类型,如浮点数和布尔值。

在__init__期间

在__init__期间运算示例:

1
hilo_diff = self.data.high - self.data.low

变量hilo_diff保存对线对象的引用,该对象在next调用之前预先计算,并且可以使用标准数组表示法通过[]来访问。

显然,它确实包含数据源的每个bar的最高点和最低点之差。

当混合简单的线对象(如 self.data 数据源中的行)和复杂的线对象(如指标)时也适用:

1
2
sma = bt.SimpleMovingAverage(self.data.close)
close_sma_diff = self.data.close - sma

现在close_sma_diff也包含了一个线对象。

使用逻辑运算符:

1
close_over_sma = self.data.close > sma

现在生成的线条对象将包含一个布尔数组。

在next期间

运算示例(逻辑运算符):

1
close_over_sma = self.data.close > self.sma

使用等效数组(基于索引 0 的表示法):

1
close_over_sma = self.data.close[0] > self.sma[0]

在这个例子中,close_over_sma产生一个布尔值,它是两个浮点值比较的结果,该值是由[0]操作符应用于self.data.close和self.sma返回的值

__init__与next方法为什么这样实现

逻辑简化(以及易用性)是关键。计算及大多数相关逻辑可以在__init__期间声明,从而在next中精简实际操作逻辑的代码。

实际上还有一个附带的好处:速度(在开头解释过,可以在使用前预先计算)

一个完整的示例,在以__init__期间生成买入信号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyStrategy(bt.Strategy):

def __init__(self):

sma1 = btind.SimpleMovingAverage(self.data)
ema1 = btind.ExponentialMovingAverage()

close_over_sma = self.data.close > sma1
close_over_ema = self.data.close > ema1
sma_ema_diff = sma1 - ema1

buy_sig = bt.And(close_over_sma, close_over_ema, sma_ema_diff > 0)

def next(self):

if buy_sig:
self.buy()

注意:因为Python 的and运算符无法被覆盖,backtrader定义了自己的And. 这同样适用于其他逻辑运算符,如Or和If

显然,在__init__期间的“声明式”方法可以将next方法(实际策略工作发生的地方)中的代码膨胀保持在最低限度。

注意:当逻辑变得非常复杂并涉及多个运算时,通常最好将其封装在Indicator.

一些笔记

在上面的示例中,与其他平台相比,backtrader把两件事做了简化:

  • 声明Indicators既不获取父参数(如创建它们的策略),也不调用任何类型的“注册”方法/函数。

策略将启动Indicators的计算以及由于运算而生成的任何线对象(例如sma - ema)

  • ExponentialMovingAverage被实例化时不需要self.data参数

这是故意这样写的。如果没有传递data参数,则父级的第一个数据(在本例中是正在创建的策略)将在后台自动传递。

指标绘图

首先也是最重要的:

  • 声明的Indicators将自动绘制(如果在主程序中调用了 cerebro.plot的话)

  • 进行运算的线对象不会被绘制(例如close_over_sma = self.data.close > self.sma)

如果需要,有一个辅助类LinePlotterIndicator可以通过以下方法绘制此类运算结果:

1
2
close_over_sma = self.data.close > self.sma
LinePlotterIndicator(close_over_sma, name='Close_over_SMA')

name参数为该指标所持有的单个线提供名称。

控制绘图

在Indicator开发过程中可以添加plotinfo声明。它可以是元组的元组(2 个元素)、dict或OrderedDict。看起来像:

1
2
3
4
5
class MyIndicator(bt.Indicator):

....
plotinfo = dict(subplot=False)
....

稍后可以按如下方式访问(和设置)该值(如果需要):

1
2
myind = MyIndicator(self.data, someparam=value)
myind.plotinfo.subplot = True

该值甚至可以在实例化期间设置:

1
myind = MyIndicator(self.data, someparams=value, subplot=True)

subplot=True将(在幕后)被传递给指标的实例化成员变量plotinfo。

plotinfo提供以下参数来控制绘图行为:

  • plot(默认:True)是否绘制指标
  • subplot(默认:True)

是否在不同的窗口中绘制指标。对于移动平均线等指标,默认值为False

  • plotname(默认:‘’)

设置要在绘图上显示的绘图名称。为空值意味着将使用指标的规范名称 (class.name )。这有一些限制,因为 Python 标识符不能使用算术运算符等。

像 DI+ 这样的指标将声明如下:

1
2
class DIPlus(bt.Indicator):
plotinfo=dict(plotname='DI+')

能让作出来的图“更好看”

  • plotabove(默认:False)

指标通常绘制在它们所操作的数据下方(带有subplot=True的)。将其设置为True将使指标绘制在数据上方。

  • plotlinelabels(默认:False)

意思是“指标”上的“指标”。如果有人计算 RSI 的 SimpleMovingAverage,作出的图通常会显示相应绘制线的名称“SimpleMovingAverage”。这是“指标”的名称,而不是绘制的实际线条的名称。

此默认行为是有意义的,因为用户通常希望看到已使用 RSI 创建了 SimpleMovingAverage。

如果该值设置为True则将使用 SimpleMovingAverage 内部的实际线名称。

  • plotymargin(默认:0.0)

指标顶部和底部留出的边缘大小 ( 0.15 表示 15%)。有时,matplotlib绘图距离轴的顶部/底部太远,可能需要留出边距

  • plotyticks(默认:[])

用于控制绘制的y轴的刻度

如果传递空列表,将自动计算“y 刻度”。对于像随机指标这样的指标,将其设置为众所周知的行业标准可能是有意义的,例如:[20.0, 50.0, 80.0]

一些指标提供类似 upperband 和 lowerband 这样的参数,这些参数实际上用于操纵 y 刻度

  • plothlines(默认:[])

用于控制沿指示器轴绘制水平线。

如果传递空列表,则不会绘制水平线。

对于像随机指标这样的指标,根据众所周知的行业标准绘制界限可能是有意义的,例如:[20.0, 80.0]

一些指标提供类似 upperband 和 lowerband 的参数,这些参数实际上用于操纵水平线

  • plotyhlines(默认:[])

用于使用单个参数同时控制绘图刻度和绘图线。

  • plotforce(默认:False)

如果出于某种原因,您认为某个指标应该绘制,但它没有绘制……将此设置为True是最后的手段。


这一篇就到这里啦。欢迎大家点赞、转发、私信。还没有关注我的朋友可以关注 江达小记

江达小记