You are here:  Home » 量化交易与机器学习 » backtrader » 平台Concepts – backtrader中文教程

平台Concepts

This是一些平台的概念的集合。它试图收集其可以在使用platform.

Before Starting

All微型代码示例是有用的信息比特假定以下进口可用:

import backtrader as bt
import backtrader.indicators as btind
import backtrader.feeds as btfeeds

Note

用于访问象子模块的备选句法indicatorsfeeds

import backtrader as bt

然后:

thefeed = bt.feeds.OneOfTheFeeds(...)
theind = bt.indicators.SimpleMovingAverage(...)

数据源 – 传递他们的工作around

The基础与平台将与Strategies完成。和这些都将获得通过数据Feeds。该平台最终用户并不需要关心有关接收他们:

数据馈送中被自动地设置成员变量的策略数组和快捷键的策略派生类的声明和运行的平台的阵列positions

Quick预览的形式:

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

    def __init__(self):

        sma = btind.SimpleMovingAverage(self.datas[0], period=self.params.period)

    ...

cerebro = bt.Cerebro()

...

data = btfeeds.MyFeed(...)
cerebro.adddata(data)

...

cerebro.addstrategy(MyStrategy, period=30)

...

注意下列事项:

  • 没有*args**kwargs正在接受战略的__init__方法(它们仍然可以用于)
  • 成员变量self.datas存在其是阵列/列表/迭代保持至少一个项目(希望否则一个将引发异常)

原来是这样。Data Feeds被添加到该平台的时候,就会出现内部在它们被加入到system.

Note

This的顺序的策略也适用于Indicators,应终端用户发展自己自定义指标或在看看的源代码时,现有的一些指标Reference

Shortcuts用于数据Feeds

The self.datas阵列项可以与附加的自动被直接访问成员变量:

  • self.data目标self.datas[0]
  • self.dataX目标self.datas[X]

然后,该示例:

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

    def __init__(self):

        sma = btind.SimpleMovingAverage(self.data, period=self.params.period)

    ...

省略上面的数据Feeds

The例子可进一步简化于:

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

    def __init__(self):

        sma = btind.SimpleMovingAverage(period=self.params.period)

    ...

self.data已经完全从调用删除SimpleMovingAverage。如果这样做,指示器(在这种情况下,SimpleMovingAverage)接收对象的第一数据,其中是被创建(在Strategy),其是self.data(又名self.data0self.datas[0]

几乎所有的东西是一个Data Feed

不仅数据馈送的数据,并可以被传递。Indicators和的Operations也data.

In前面的例子的结果SimpleMovingAverage是接收self.datas[0]作为输入进行操作。与操作和额外的一个例子指标:

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

    def __init__(self):

        sma1 = btind.SimpleMovingAverage(self.datas[0], period=self.p.period1)

        # This 2nd Moving Average operates using sma1 as "data"
        sma2 = btind.SimpleMovingAverage(sma1, period=self.p.period2)

        # New data created via arithmetic operation
        something = sma2 - sma1 + self.data.close

        # This 3rd Moving Average operates using something  as "data"
        sma3 = btind.SimpleMovingAverage(something, period=self.p.period3)

        # Comparison operators work too ...
        greater = sma3 > sma

        # Pointless Moving Average of True/False values but valid
        # This 4th Moving Average operates using greater  as "data"
        sma3 = btind.SimpleMovingAverage(greater, period=self.p.period4)

    ...

基本上一切都被转化成其可被用作一个对象一旦它被upon.

Parameters

Mostly操作数据馈送每隔class在平台支持的概念parameters.

  • 用默认值一起参数被声明为类属性(元组或类似字典的对象的元组)
  • 关键词ARGS(**kwargs)被扫描用于匹配参数,除去他们从**kwargs如果发现和值分配给相应的parameter
  • And参数可以在该类的实例通过访问最终使用成员变量self.params(简写:self.p

以前的快速预览战略中已经包含一个参数的例子,但冗余的缘故,再次,只注重的参数。使用tuples

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

    def __init__(self):
        sma = btind.SimpleMovingAverage(self.data, period=self.p.period)

而且使用了dict

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

    def __init__(self):
        sma = btind.SimpleMovingAverage(self.data, period=self.p.period)

Lines

Again大多是在平台上所有其他对象是Lines启用宾语。从一个终端用户点。这意味着:

  • 它可以容纳更多的线路串联的一个,作为一个线系列的值的数组物的值的图表中放在一起就会形成的line.

A好的例子的line(或lineseries)是由闭合形成的线股票的价格。这实际上是的一个公知的图表表示价格的演变(被称为Line on Close

经常使用的平台是只关心accessing lines。该以前的迷你策略实例,轻轻地伸出,就派上用场了一次:

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

    def __init__(self):

        self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)

    def next(self):
        if self.movav.lines.sma[0] > self.data.lines.close[0]:
            print("Simple Moving Average is greater than the closing price")

两个物体lines已经暴露:

  • self.data它有一个lines属性,它包含一个close属性在turn
  • self.movav这是一个SimpleMovingAverage指示器它有一个lines属性包含了sma在turn

Note

It属性应该从这种明显的,那lines命名。他们能也可以以下声明顺序依次访问,但是这只能在使用Indicatordevelopment

And两个lines,即closesma可以查询一个点(index 0)比较values.

Shorthand访问线路确实存在:

  • xxx.lines可缩短至xxx.l
  • xxx.lines.name可缩短至xxx.lines_name
  • 像战略和指标的复杂对象提供快速访问数据的lines
    • self.data_name<>100<>self.data.lines.name
    • 这也适用于编号的数据变量:self.data1_name-> self.data1.lines.name

此外,该行的名称与直接访问:

  • self.data.closeself.movav.sma

    但符号不作与前一个如果lines是那样清晰实际上是accessed.

Not

Setting /Assigning这两个符号后面的线是不supported

Lines declaration

If的Indicator正在开发中,lines该指示器具有绝是declared.

Just与params这发生作为一个阶级属性这个时候ONLY如一个元组。字典中不支持,因为他们不存储的东西下面插入order.

For简单移动平均会做这样的:

class SimpleMovingAverage(Indicator):
    lines = ("sma",)

    ...

Note

Thecomma下面的声明是必要的,如果元组你过关了一个字符串的元组或者字符串中的每个字母会解释为一个项目被添加到元组。的可能的一个其中Python的语法得到它wrong.

As在前面的例子中所看到的几个景点此声明创建一个sma在线路Indicator可在策略的逻辑稍后访问(也可能由其他指标来创建更复杂的指标)

对于发展有时是有益的访问通用非评为线方式而这正是编号访问就派上用场了:

  • self.lines[0]self.lines.sma

有更多的线路被确定,他们将与指数1,2进行访问,当然higher.

And,额外的简写确实存在:

  • self.lineself.lines[0]
  • self.lineXself.lines[X]
  • self.line_Xself.lines[X]

内正在接收对象datas feeds的下面这些数据线饲料也可以通过数字来快速访问:

  • self.dataYself.data.lines[Y]
  • self.dataX_Yself.dataX.lines[X]这是一个完整的shorthard的self.datas[X].lines[Y]

在访问lines`Data Feeds

里面data feeds`的lines也可以访问省略lines。这使得它更自然的工作认为像closeprices.

For例如:

data = btfeeds.BacktraderCSVData(dataname="mydata.csv")

...

class MyStrategy(bt.Strategy):

    ...

    def next(self):

        if self.data.close[0] > 30.0:
            ...

这似乎比也有效更自然:if self.data.lines.close[0] >
30.0:
。同样不适用于Indicators与推理之中:

  • 一个Indicator可以有一个属性close,其持有的中间计算,这就是后来交付给实际lines 也被命名close

对于数据Feed,不会进行任何计算,因为它只是一个数据源。

Lineslen

Lines有一组点和执行过程中动态地扩展,因此的长度可以在任何时间通过调用标准Python来测量lenfunction.

This适用于例如:

  • 的数据Feeds
  • Strategies
  • Indicators

An附加属性适用于Data Feeds当数据为preloaded

  • 方法buflen

该方法返回的条的Data Feed具有实际数目之间available.

The区别lenbuflen

  • len报告多少条已processed
  • buflen报告已加载的酒吧总数数据Feed

If都返回相同的值,或者没有数据已加载或酒吧的处理占用了所有预装的棒材(除非系统连接到一个活饲料,这将意味着处理结束)

线和Params

A一种元语言的继承是在以支持的声明ParamsLines。我们已经尽力了,使得它与标准Python兼容继承rules.

Params inheritance

Inheritance应该按预期工作:

  • 多重继承是supported
  • Params从基类inherited
  • 如果有多个基类中定义相同的PARAM最后的默认值在继承列表类是used
  • If相同参数是在子类中重新定义,新的默认值取在该基class

Lines Inheritance

  • Multiple继承的是从所有基类supported
  • Lines继承。作为named线将有只能是线的一个版本,如果相同的名字已经不止一次使用在碱classes

Indexing:0和-1

Lines如图之前是线串联,并有一组点符合一当拉在一起线(沿所有收盘价结合在一起的时候喜欢时间轴)

要访问常规代码的那些点,选择是使用一个0基于方法为当前get/setinstant.

Strategies只能做get值。指标也做setvalues.

From前面的快捷策略实例,其中next方法进行了简单看出:

def next(self):
    if self.movav.lines.sma[0] > self.data.lines.close[0]:
        print("Simple Moving Average is greater than the closing price")

的逻辑是getting移动平均和的电流的电流值通过应用指数收盘价0.

Note

Actually索引0和应用逻辑/算术运算符时该比较可以直接制成为在:

if self.movav.lines.sma > self.data.lines.close:
    ...

以后查看文档中为operators.

Setting说明旨在被用过的显影时,例如,指示器,因为当前的输出值必须由indicator.

A SimpleMovingAverage设置可以为当前获取/设置点被计算为如下:

def next(self):
  self.line[0] = math.fsum(self.data.get(0, size=self.p.period)) / self.p.period

访问以前的设置点是仿照下面定义的Python使得用于-1访问数组时/ iterable

  • It指向array

The平台考虑当前实际的get / set前的最后一组项目(点)是-1.

这样比较当前closeprevious close0VS-1事情。在策略,例如:

def next(self):
    if self.data.close[0] > self.data.close[-1]:
        print("Closing price is higher today")

当然和逻辑,价格set-1将有-2,
-3, ...
.

Slicing

backtrader访问没有按对于lines对象,这是一个设计继[0][-1]索引方式决定。通过定期可转位Python对象,你会做这样的事情:

myslice = self.my_sma[0:]  # slice from the beginning til the end

但请记住,与0……它实际上是当前让渡价值,有后罢了。另外:

myslice = self.my_sma[0:-1]  # slice from the beginning til the end

再次…… 0是当前值,-1是最新(先前)交付的值。这就是为什么0-> -1中的片段反向交易者生态系统中毫无意义的 原因

myslice = self.my_sma[:0]  # slice from current point backwards to the beginning

要么:

myslice = self.my_sma[-1:0]  # last value and current value

或:

myslice = self.my_sma[-3:-1]  # from last value backwards to the 3rd last value

获得一个slice

与最新的值的数组仍然可以得到。语法:

myslice = self.my_sma.get(ago=0, size=1)  # default values shown

这将返回了ARRY与1值(size=1)与当前此刻0作为起始点看backwards.

To时间从当前点得到10个值(即:在过去的10个值):

myslice = self.my_sma.get(size=10)  # ago defaults to 0

当然数组有你所期望的顺序。最左边的值一个最古老和最右边的值是最新的(这是一个普通的蟒阵列而不是一个lines对象)

为了得到最后的10个值仅跳过当前点:

myslice = self.my_sma.get(ago=-1, size=10)

线:DELAYED indexing

[]运算符的语法是否有提取期间的各个值next逻辑相。Lines对象支持一个额外的符号来解决通过值的delayed lines object__init__phase.

Let的说,在逻辑的兴趣是比较以前的close值到的simple moving average实际值`。而不是做手工在每个next迭代预罐装lines可以生成对象:

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")

这里(delay)正在使用的符号:

  • 这也提供了close价格的翻版,而是由-1.

    和比较延迟self.data.close(-1) > self.sma生成另一lines对象返回任一1如果条件为True0如果False

线Coupling

The操作者()可作为以上那样,按照delay值,以提供的linesobject.

If被使用的语法WITHOUT提供delay值,则一个延迟版本LinesCoupler lines返回的对象。这是为了建立上操作的指标之间的耦合datas具有不同timeframes.

Data订阅与不同的时间范围具有不同的lengths,并且指标对它们操作复制数据的长度。例如:

  • 每日数据馈送具有每year
  • A每周数据馈送围绕250巴具有每year

Trying 52条创建一个操作(例如),其比较2simple moving
averages
,每个上面引述会打破DATAS操作。这将是目前还不清楚如何从日常的时间内完成250条匹配的52条每周timeframe.

The读者可以想像在后台date对比正在发生找出了一天 – 一周对应,但:

  • Indicators只是数学公式,并没有datetimeinformation

    They一无所知的环境中,如果数据也是这样做的足够的值,计算可以采取place.

The()(空呼叫)表示法来救援:

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

    def __init__(self):

        # data0 is a daily data
        sma0 = btind.SMA(self.data0, period=15)  # 15 days sma
        # data1 is a weekly data
        sma1 = btind.SMA(self.data1, period=5)  # 5 weeks sma

        self.buysig = sma0 > sma1()

    def next(self):
        if self.buysig[0]:
            print("daily sma is greater than weekly sma1")

这里较大时间范围指示器,sma1coupled日常时间表与sma1()。这将返回一个对象,它是与所述兼容较大的数字的sma0和复制由sma1产生的值`酒吧,有效地传播在日常bars

Operators 250的52层的酒吧,使用天然constructs

为了实现“易用性”目标平台允许(内的Python约束)使用运营商的。为了进一步加强这方面的目标,利用运营商已破两个stages.

Stage 1 – 操作员创建Objects

An例如已经看到即使没有明确的意思这一点。中像指示器对象的初始化阶段(__init__方法)和策略,运营商创建一个可以在操作中,分配或存放物品作为在战略的评估阶段以后使用参考logic.

Once又一个潜在的实施SimpleMovingAverage的,进一步细分下入SimpleMovingAverage指标内steps.

The代码__init__可能是这样的:

def __init__(self):
    # Sum N period values - datasum is now a *Lines* object
    # that when queried with the operator [] and index 0
    # returns the current sum

    datasum = btind.SumN(self.data, period=self.params.period)

    # datasum (being *Lines* object although single line) can be
    # naturally divided by an int/float as in this case. It could
    # actually be divided by anothr *Lines* object.
    # The operation returns an object assigned to "av" which again
    # returns the current average at the current instant in time
    # when queried with [0]

    av = datasum / self.params.period

    # The av *Lines* object can be naturally assigned to the named
    # line this indicator delivers. Other objects using this
    # indicator will have direct access to the calculation

    self.line.sma = av

更完整的用例是一个战略的初始化期间所示:

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma = btind.SimpleMovinAverage(self.data, period=20)

        close_over_sma = self.data.close > sma
        sma_dist_to_high = self.data.high - sma

        sma_dist_small = sma_dist_to_high < 3.5

        # Unfortunately "and" cannot be overridden in Python being
        # a language construct and not an operator and thus a
        # function has to be provided by the platform to emulate it

        sell_sig = bt.And(close_over_sma, sma_dist_small)

经过上述操作已经发生,sell_sigLines对象可在策略的逻辑在以后使用,表明如果条件满足或not.

阶段2-忠于自然的经营者

让我们首先要记住,一个战略有next方法,该方法被称为用于每小节的系统进程。这是运营商实际上是在阶段2模式。构建前面的例子:

class MyStrategy(bt.Strategy):

    def __init__(self):

        self.sma = sma = btind.SimpleMovinAverage(self.data, period=20)

        close_over_sma = self.data.close > sma
        self.sma_dist_to_high = self.data.high - sma

        sma_dist_small = sma_dist_to_high < 3.5

        # Unfortunately "and" cannot be overridden in Python being
        # a language construct and not an operator and thus a
        # function has to be provided by the platform to emulate it

        self.sell_sig = bt.And(close_over_sma, sma_dist_small)

    def next(self):

        # Although this does not seem like an "operator" it actually is
        # in the sense that the object is being tested for a True/False
        # response

        if self.sma > 30.0:
            print("sma is greater than 30.0")

        if self.sma > self.data.close:
            print("sma is above the close price")

        if self.sell_sig:  # if sell_sig == True: would also be valid
            print("sell sig is True")
        else:
            print("sell sig is False")

        if self.sma_dist_to_high > 5.0:
            print("distance from sma to hig is greater than 5.0")

不是一个非常有用的策略,只是一个例子。在第2个阶段运营商返回预期值(Boolean如果检验真理和浮若比较它们彩车),也算术运算do.

Note

Notice是比较实际未使用[]操作。这个意在进一步简化things.

if self.sma > 30.0: …比较self.sma[0]30.0(1st线和电流值)

if self.sma > self.data.close:…比较self.sma[0]self.data.close[0]

一些非重写运营商/ functions

Python不会允许覆盖一切,从而提供了一些功能应对cases.

Note

只是意味着第一阶段期间使用,到后来创建对象提供values.

运营商:

  • and -> And
  • or -> Or

逻辑控制:

  • if -> If

功能:

  • any -> Any
  • all -> All
  • cmp -> Cmp
  • max -> Max
  • min -> Min
  • sum -> Sum

    Sum实际math.fsum用作基础操作,因为平台使用浮点数,并且应用规则sum 可能会影响精度。

  • reduce -> Reduce

这些公用事业运营商/功能上iterables操作。在元素iterables可以常规的Python数值类型(整型,浮点型,…),还与对象Lines.

产生非常愚蠢的买入信号的示例:

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma1 = btind.SMA(self.data.close, period=15)
        self.buysig = bt.And(sma1 > self.data.close, sma1 > self.data.high)

    def next(self):
        if self.buysig[0]:
            pass  # do something here

很明显,如果sma1比高更高,它必须是高较密切。但有一点是说明使用bt.And.

使用bt.If

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma1 = btind.SMA(self.data.close, period=15)
        high_or_low = bt.If(sma1 > self.data.close, self.data.low, self.data.high)
        sma2 = btind.SMA(high_or_low, period=15)

故障:

  • 产生了SMAdata.close`period=15
  • 和then
    • bt.Ifsmaclose,回报low,否则返回high

    请记住,正在返回任何实际价值的时候bt.If正在调用。它返回一个Lines对象,它就像一个SimpleMovingAverage.

    的值将被计算以后,当系统runs

  • 所生成的bt.If Lines然后对象被馈送到2nd SMA其有时会使用low价格有时high价格在calculation

Thosefunctions还采用数字值。相同的例子具有修饰:

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma1 = btind.SMA(self.data.close, period=15)
        high_or_30 = bt.If(sma1 > self.data.close, 30.0, self.data.high)
        sma2 = btind.SMA(high_or_30, period=15)

现在2nd移动平均使用任一30.0high的价格进行计算,这取决于smaVSclose

Note

The值30在内部变换成一个伪迭代其中的逻辑状态总是返回30