BackTrader 中文文档(五)(3)

简介: BackTrader 中文文档(五)

BackTrader 中文文档(五)(2)https://developer.aliyun.com/article/1489255

策略参考

原文:www.backtrader.com/docu/strategy-reference/

内置策略的参考

MA_CrossOver

别名:

* SMA_CrossOver

这是一个仅限开多头的策略,基于移动平均线交叉

注意:

* Although the default

买入逻辑:

* No position is open on the data
* The `fast` moving averagecrosses over the `slow` strategy to the
  upside.

卖出逻辑:

* A position exists on the data
* The `fast` moving average crosses over the `slow` strategy to the
  downside

订单执行类型:

* Market

线:

* datetime

参数:

* fast (10)
* slow (30)
* _movav (<class ‘backtrader.indicators.sma.SMA’>)

SignalStrategy

这个Strategy的子类旨在使用信号自动运行。

信号通常是指标,预期输出值为:

  • > 0是一个long指示
  • < 0是一个short指示

有 5 种信号类型,分为 2 组。

主要组

  • LONGSHORT:此信号的longshort指示都会被采纳
  • LONG
  • long指示被认为是开多头
  • short指示被认为是用于关闭多头仓位。但是:
  • 如果系统中存在LONGEXIT(见下文)信号,则会用于退出多头
  • 如果有SHORT信号可用且没有LONGEXIT可用,则会用于在开启short之前关闭long
  • SHORT
  • short指示被认为是开空头
  • long指示被认为是用于关闭空头仓位。但是:
  • 如果系统中存在SHORTEXIT(见下文)信号,则会用于退出空头
  • 如果有LONG信号可用且没有SHORTEXIT可用,则会用于在开启long之前关闭short

退出组

这两个信号旨在覆盖其他信号并提供退出long/short仓位的标准。

  • LONGEXITshort指示被认为是用于退出long仓位
  • SHORTEXITlong指示被认为是用于退出short仓位

订单发出

订单执行类型为Market,有效性为None直到取消为止

参数:

  • signals(默认:[]):一个列表/元组的列表/元组,允许实例化信号并分配到正确的类型
    预计通过cerebro.add_signal来管理此参数
  • _accumulate(默认:False):允许进入市场(多头/空头),即使已经在市场中
  • _concurrent(默认:False):允许即使有订单正在等待执行,也可以发出订单
  • _data(默认:None):如果系统中存在多个数据,这是订单的目标。这可以是
  • None:系统中的第一个数据将被使用
  • 一个int:表示在该位置插入的数据
  • 一个str:在创建数据时给定的名称(参数name)或在使用cerebro.adddata(..., name=)将其添加到 cerebro 时给定的名称
  • 一个data实例

线:

* datetime

参数:

* signals ([])
* _accumulate (False)
* _concurrent (False)
* _data (None)

指标

使用指标

原文:www.backtrader.com/docu/induse/

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

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

指标的作用

  1. Indicators始终在Strategy中的__init__期间实例化
  2. next期间使用/检查指标值(或其派生的值)

有一个重要的公理需要考虑:

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

让我们来看一下差异和操作模式。

__init__next

事情的工作方式如下:

  • __init__期间涉及lines对象的任何操作都会生成另一个lines对象
  • next期间涉及lines对象的任何操作都会产生常规的 Python 类型,如浮点数和布尔值。

__init__期间

__init__期间操作的示例:

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

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

很明显,对于数据源的每个条数据,它包含了高低之间的差异。

这也适用于将简单的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__期间生成一个buy信号:

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运算符不能被重载,迫使平台定义自己的And。其他构造也是如此,比如OrIf

显然,__init__期间的“声明式”方法将next的膨胀(其中实际策略工作发生)最小化。

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

注意

当逻辑变得非常复杂并涉及多个操作时,通常更好的做法是将其封装在一个Indicator内部。

一些注意事项

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

  • 声明的Indicators既不会得到一个parent参数(就像它们被创建的策略一样),也不会调用任何类型的“register”方法/函数。
    尽管如此,策略仍将触发 Indicators 的计算和因操作而生成的任何 lines 对象(如 sma - ema
  • ExponentialMovingAverage 在没有 self.data 的情况下被实例化
    这是故意的。如果没有传递 data,则将自动传递父级的第一个数据(在本例中,正在创建的策略)

指标绘图

首先和最重要的是:

  • 声明的 Indicators 将自动绘制(如果调用了 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,则绘图通常会显示相应绘制线的名称“SimpleMovingAverage”。这是“Indicator”的名称,而不是实际绘制的线的名称。
    这种默认行为是有意义的,因为用户通常希望看到使用 RSI 创建了 SimpleMovingAverage。
    如果将值设置为 True,则将使用 SimpleMovingAverage 中线的实际名称。
  • plotymargin(默认值:0.0
    要在指标的顶部和底部留下的边距量(0.15 -> 15%)。有时 matplotlib 绘图会延伸到轴的顶部/底部,可能希望有一些边距
  • plotyticks(默认值:[]
    用于控制绘制的 y 轴刻度
    如果传递了一个空列表,“y ticks”将自动计算。对于像随机指标这样的指标,将其设置为已知的行业标准可能是有意义的,例如:[20.0, 50.0, 80.0]
    一些指标提供诸如upperbandlowerband之类的参数,实际上用于操作 y ticks。
  • plothlines(默认值:[]
    用于控制沿指标轴绘制水平线。
    如果传递了一个空列表,将不会绘制任何水平线。
    对于像随机指标这样的指标,绘制已知的行业标准线可能是有意义的,例如:[20.0, 80.0]
    一些指标提供诸如upperbandlowerband之类的参数,实际上用于操作水平线。
  • plotyhlines(默认值:[]
    用于同时使用单个参数控制 plotyticks 和 plothlines。
  • plotforce(默认值:False
    如果由于某种原因你认为某个指标应该绘制但却没有绘制……将此设置为True作为最后的手段。

指标开发

原文:www.backtrader.com/docu/inddev/

如果除了一个或多个获胜策略之外,还必须开发其他内容,则此内容是自定义指标。

根据作者的说法,平台内的这种开发是简单的。

需要以下内容:

  • 从指标派生的类(直接或从已经存在的子类)
  • 定义它将保持的线条
    指标必须至少有一条线。如果是从现有指标派生的,则可能已经定义了线条(们)
  • 可选地定义可以更改行为的参数
  • 可选地提供/自定义一些元素,以便合理地绘制指标
  • __init__中提供完全定义的操作,并将其绑定(分配)到指标的线条(们)上,否则提供next和(可选)once方法
    如果可以在初始化期间使用逻辑/算术运算完全定义指标,并将结果分配给线条:完成
    如果不是这种情况,至少必须提供一个next,在该方法中指标必须将一个值分配给索引为 0 的线条(们)
    可以通过提供once方法来实现对runonce模式(批量操作)的计算优化。

重要提示:幂等性

指标为它们接收的每个柱状图生成输出。不必假设同一柱状图将被发送多少次。操作必须是幂等的。

其背后的原理是:

  • 同一柱状图(索引)可以多次发送,其值会发生变化(即变化的值是收盘价)

这使得例如,“重播”每日会话,但使用可能由 5 分钟柱状图组成的股票数据成为可能。

这也可以使平台从实时数据源获取值。

一个虚拟(但功能性的)指标

所以它可以是:

class DummyInd(bt.Indicator):
    lines = ('dummyline',)
    params = (('value', 5),)
    def __init__(self):
        self.lines.dummyline = bt.Max(0.0, self.params.value)

完成!指标将始终输出相同的值:如果大于 0.0,则为 0.0 或 self.params.value。

相同的指标,但使用了下一个方法:

class DummyInd(bt.Indicator):
    lines = ('dummyline',)
    params = (('value', 5),)
    def next(self):
        self.lines.dummyline[0] = max(0.0, self.params.value)

完成!相同的行为。

注意

注意在__init__版本中如何使用bt.Max将其分配给 Line 对象self.lines.dummyline

bt.Max返回一个lines对象,该对象会自动为传递给指标的每个柱状图迭代。

如果使用max,则赋值将毫无意义,因为指标将具有具有固定值的成员变量,而不是线条。

next过程中,直接使用浮点值进行工作,并且可以使用标准的max内置函数

让我们回顾一下self.lines.dummyline是长格式的,可以缩写为:

  • self.l.dummyline

甚至可以:

  • self.dummyline

后者仅在代码没有将其与成员属性混淆时才可能存在。

第三个版本提供了一个额外的once方法来优化计算:

class DummyInd(bt.Indicator):
    lines = ('dummyline',)
    params = (('value', 5),)
    def next(self):
        self.lines.dummyline[0] = max(0.0, self.params.value)
    def once(self, start, end):
       dummy_array = self.lines.dummyline.array
       for i in xrange(start, end):
           dummy_array[i] = max(0.0, self.params.value)

更有效,但开发once方法已迫使深入挖掘。实际上已经查看了内部情况。

__init__ 版本无论如何都是最好的:

  • 一切都限于初始化
  • nextonce(都经过优化,因为 bt.Max 已经包含了它们)会自动提供,无需操作索引和/或公式

如果需要开发,指标还可以覆盖与 nextonce 关联的方法:

  • prenextnexstart
  • preonceoncestart

手动/自动最小周期

如果可能的话,平台会计算,但可能需要手动操作。

这是一个简单移动平均的潜在实现:

class SimpleMovingAverage1(Indicator):
    lines = ('sma',)
    params = (('period', 20),)
    def next(self):
        datasum = math.fsum(self.data.get(size=self.p.period))
        self.lines.sma[0] = datasum / self.p.period

尽管看起来合理,但平台并不知道最小周期是多少,即使参数被命名为“period”(名称可能会误导,并且一些指标接收到多个具有不同用途的“period”)

在这种情况下,next将被调用,已经为第 1 个柱,一切都将爆炸,因为 get 不能返回所需的 self.p.period

在解决这种情况之前,必须考虑以下事项:

  • 提供给指标的数据源可能已经具有最小周期

例如,可以对样本SimpleMovingAverage进行处理:

  • 一个常规数据源
    这有一个默认的最小周期为 1(只需等待进入系统的第 1 个柱形图)
  • 另一个移动平均……而这又已经有一个周期
    如果这是 20,再次我们的示例移动平均值也是 20,我们最终会得到一个最小周期为 40 个柱的周期
    实际上,内部计算说 39 …… 因为一旦第一个移动平均产生了一个柱,这就计为下一个移动平均,从而创建了一个重叠的柱,因此需要 39 个。
  • 其他也携带周期的指标/对象

BackTrader 中文文档(五)(4)https://developer.aliyun.com/article/1489266

相关文章
|
5月前
|
索引
BackTrader 中文文档(六)(2)
BackTrader 中文文档(六)
79 0
|
5月前
|
Unix 索引 Python
BackTrader 中文文档(一)(2)
BackTrader 中文文档(一)
114 0
|
5月前
|
存储 编解码 API
BackTrader 中文文档(四)(1)
BackTrader 中文文档(四)
64 1
|
5月前
|
存储 缓存 Shell
BackTrader 中文文档(二)(3)
BackTrader 中文文档(二)
108 0
|
5月前
|
存储 编解码 网络架构
BackTrader 中文文档(二)(4)
BackTrader 中文文档(二)
86 0
|
5月前
|
测试技术 索引 Python
BackTrader 中文文档(二)(2)
BackTrader 中文文档(二)
61 0
|
5月前
|
Python
BackTrader 中文文档(六)(3)
BackTrader 中文文档(六)
81 0
|
5月前
|
数据可视化
BackTrader 中文文档(三)(3)
BackTrader 中文文档(三)
45 0
|
5月前
|
Python
BackTrader 中文文档(一)(3)
BackTrader 中文文档(一)
54 0
|
5月前
|
存储
BackTrader 中文文档(四)(4)
BackTrader 中文文档(四)
42 0