使用指标#

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

  • 在策略内部

  • 在其他指标内部

指标的运作方式#

  1. 指标在*策略*的 __init__ 期间始终被实例化

  2. 指标值(或其派生的值)在 next 期间被使用/检查

有一个重要的原则需要考虑:

  • __init__ 期间声明的任何 指标 (或其派生的值)都会在调用 next 之前进行预计算

让我们来看看不同的操作模式。 __init__ vs next#

__init__next 的区别#

事情的运作如下:

  • __init__ 过程中,涉及到 lines 对象的任何 操作 都会生成另一个 lines 对象。

  • next 过程中,涉及到 lines 对象的任何 操作 都会产生常规的Python类型,如浮点数和布尔值。

__init__#

__init__ 过程中的一个示例操作:

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

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

显然,它包含了数据流中每个条的最高价和最低价之差。

当然,这也适用于混合使用简单的 lines (如self.data数据流中的那些)和复杂的 lines (如指标)的情况: sma = bt.SimpleMovingAverage(self.data.close)

close_sma_diff = self.data.close - sma

现在, close_sma_diff 包含一个 line(线) 对象。

使用逻辑运算符:

close_over_sma = self.data.close > sma

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

next 方法中#

一个操作(逻辑运算符)的示例:

close_over_sma = self.data.close > self.sma

使用等价的数组(索引从 0 开始):

close_over_sma = self.data.close[0] > self.sma[0]在这种情况下, close_over_sma 产生一个布尔值,该值是通过比较两个浮点数值得出的,这两个数值是应用于 self.data.closeself.sma[0] 操作符返回的。

__init__next 的区别是什么?#

简化逻辑(以及随之而来的使用便捷性)是关键。计算和大部分相关逻辑可以在 __init__ 中声明,从而在 next 中将实际操作逻辑最小化。

实际上还有一个附带优点: 速度 (由于之前解释的预计算)。

一个完整的示例,在 __init__ 中生成一个 买入信号

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 = sma - ema

buy_sig = bt.And(close_over_sma, close_over_ema, sma_ema_diff > 0)def next(self):

if buy_sig:

self.buy()

注意

Python的 and 运算符无法重载,强制平台定义自己的 And 。其他结构(如 OrIf )也是如此。

很明显,在 __init__ 中采用“声明性”方法可以将 “next” 中(实际策略工作发生的地方)的膨胀最小化。

(不要忘记还有一个加速因素)

注意

当逻辑变得非常复杂并涉及多个操作时,通常最好将其封装在 Indicator 中。

一些注意事项#

在上面的示例中,与其他平台相比,“backtrader”简化了两个事项:

  • 声明的 Indicators 不会获得 parent 参数(例如创建它们的策略),也不会调用任何类型的 “register” 方法/函数。

    尽管如此,策略仍然会触发对 Indicators 的计算,并且会生成由于操作(如 sma - ema )而生成的任何 lines 对象。- 在实例化 ExponentialMovingAverage 时没有传入 self.data

这是有意的。如果没有传入 data ,将自动在后台传入父级的第一个数据(在这种情况下是创建它的策略)

指标绘图#

首先:

  • 声明的“指标”会自动绘制(如果调用了 cerebro.plot

  • 来自操作的 lines 对象不会被绘制(例如 close_over_sma = self.data.close > self.sma

    有一个辅助的“LinePlotterIndicator”,它绘制这样的操作,如果需要可以使用以下方法:

    close_over_sma = self.data.close > self.sma LinePlotterIndicator(close_over_sma, name=’Close_over_SMA’)

    “name”参数为此指标拥有的 单个 线条命名。

控制绘图 ——————–在开发 Indicator 时,可以添加 plotinfo 声明。它可以是一个元组的元组(2个元素),一个 dict 或一个 OrderedDict 。格式如下所示:

class MyIndicator(bt.Indicator):

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

以后可以访问(和设置)该值(如果需要的话):

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

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

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

subplot=True 将被传递给(幕后)实例化的成员变量 plotinfo ,用于指示器。

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

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

  • subplot (默认: True

    是否在单独的窗口中绘制指标。对于像移动平均一类的指标,默认值更改为 False

  • plotname (默认: ''

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

    例如,DI+指标将声明如下:

    class DIPlus(bt.Indicator):

    plotinfo=dict(plotname=’DI+’)

    使图绘制”更好看”

  • plotabove (默认: False

    通常,指标( subplot=True )在它们操作的数据下方绘制。将其设置为 True 将使指标在数据上方绘制。 - plotlinelabels (默认值: False

    用于”指示器”上的”指示器”。如果计算RSI(相对强弱指数)的简单移动平均线,绘图通常会显示相应绘制线的名称为”SimpleMovingAverage”。这是”指示器”的名称,而不是实际绘制的线条。

    这种默认行为是有意义的,因为用户通常希望看到RSI使用简单移动平均线来计算。

    如果将值设置为 True ,将使用简单移动平均线内部的实际名称。

  • plotymargin (默认值: 0.0

    需要在指标的顶部和底部留出的边距量( 0.15 -> 15%)。有时 matplotlib 绘图会超出轴的顶部/底部,可能需要提供边距

  • plotyticks (默认值: []

    用于控制绘制的y刻度

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

    一些指标提供诸如 upperbandlowerband 之类的参数,实际上是用于操纵y刻度 - plothlines (默认值: []

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

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

    对于像随机指标这样的指标,可能有意绘制一些已知的行业标准,比如: [20.0, 80.0]

    有些指标提供类似于 upperbandlowerband 的参数,实际上用于操作水平线。

  • plotyhlines (默认值: []

    用于同时控制 plotyticksplothlines 的绘制,使用单个参数即可。

  • plotforce (默认值: False

    如果由于某种原因您认为指标应该绘制但却没有绘制… 请将此值设置为 True ,作为最后的手段。