Backtrader中文笔记之Platform Concepts(二次修复)[平台概念]。

This is a collection of some of the concepts of the platform. It tries to gather information bits which can be useful in using the platform.

这里将介绍平台的一些概念的集合。它试图收集在使用该平台时有用的信息位。

Before Starting

在开始之前

All mini-code examples assume the following imports are available:

所有微小代码示例都假设以下导入可用:

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

  注意

An alternative syntax for accessing sub-modules like indicators and feeds:

访问子模块的另外方法比如indicators 和 feeds

import backtrader as bt

And then:

然后

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

Data Feeds - Passing them around 

数据源---传递数据

The basis of the work with the platform will be done with Strategies. And these will get passed Data Feeds. The platform end user does not need to care about receiving them:

平台工作的基础的基础将以策略来完成,而这些都将通过数据源,平台终端用户不需要在意接收它们。

Data Feeds are automagically provided member variables to the strategy in the form of an array and shortcuts to the array positions

数据源以数组的形式自动向策略提供成员变量,并提供指向数组位置的快捷方式

Quick preview of a Strategy derived class declaration and running the platform:

快速预览一个策略派生类声明和运行平台:

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)

...

Notice the following:

请注意以下几点

No *args or **kwargs are being received by the strategy’s __init__ method (they may still be used)

策略的__init__方法没有使用*args与**kwargs接收参数(它们任然是可以用的)

A member variable self.datas exists which is array/list/iterable holding at least one item (hopefully or else an exception will be raised)

self.datas变量对象存在,它至少是数组/列表/可迭代数据的一种(希望如此,否则将引发异常)

So it is. Data Feeds get added to the platform and they will show up inside the strategy in the sequential order in which they were added to the system.

因此,数据源被添加到平台中,它们将按照它们被添加到系统的顺序显示在策略中。

Note

注意

This also applies to Indicators, should the end user develop his own custom Indicator or when having a look at the source code for some of the existing Indicator Reference

这也适用于指标,如果最终用户开发自己的自定义指标,或者在查看一些现有指标引用的源代码时

 

Shortcuts for Data Feeds

快捷的数据提供

The self.datas array items can be directly accessed with additional automatic member variables:

self.datas数组项可直接访问附加的自动成员变量

  • self.data targets self.datas[0]

  • self.data 对应 self.datas[0]

  • self.dataX targets self.datas[X]
  • self.dataX 对应 self.datas[X]

The example then:

示例如下

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

    def __init__(self):

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

    ...

Omitting the Data Feeds

忽略数据传递

The example above can be further simplified to:

上面的示例可以简化如下:

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

    def __init__(self):

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

    ...

self.data has been completely removed from the invocation of SimpleMovingAverage. If this is done, the indicator (in this case the SimpleMovingAverage) receives the first data of the object in which is being created (the Strategy), which is self.data (aka self.data0 or self.datas[0])

self.data已经完全从SimpleMovingAverage调用中删除.如果这么做,指标线(在本例中是SimpleMovingAverage)将接收正在创建策略对象的第一个数据,就是self.data(self.data0或self.datas[0])

 

Almost everything is a Data Feed

几乎所有的东西都可以是数据源

Not only Data Feeds are data and can be passed around. Indicators and results of Operations are also data.

不仅数据源可以称为数据,可以四处传递。指标和操作结果也可以是数据

In the previous example the SimpleMovingAverage was receiving self.datas[0] as input to operate on. An example with operations and extra indicators:

在前面的例子中,最简单的平均数是接收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"
    # 操作指标线1 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 > sma1 # 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) ...

Basically everything gets transformed into an object which can be used as a data feed once it has been operated upon.

基本上,所有的东西都被转换成一个对象,一旦对它进行操作,它就可以用作数据源。

 

 

Parameters

参数

Mostly every other class in the platform supports the notion of parameters.

平台中的大多数其他类都支持参数的概念。

Parameters along with default values are declared as a class attribute (tuple of tuples or dict-like object)

参数和默认值被声明为类属性(元组套元组或类似于dict的对象)

Keywords args (**kwargs) are scanned for matching parameters, removing them from **kwargs if found and assigning the value to the corresponding parameter

将扫描关键字 args(**kwargs)以查找匹配的参数,如果找到,则从**kwargs中删除它们,并将值赋给相应的参数
And parameters can be finally used in instances of the class by accessing the member variable self.params (shorthand: self.p)
参数最终能被类的实例适用,通过self.params或self.p进行访问成员变量

 The previous quick Strategy preview already contains a parameters example, but for the sake of redundancy, again, focusing only on the parameters. Using tuples:

前面的快速策略已经包含了一个参数示例,但为了保险起见,再来一次只关注参数。使用元祖

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

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

And using a dict:

使用字典

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

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

  

Lines

Again mostly every other object in the platform is a Lines enabled object. From a end user point of view this means:

同样,平台中的所有其他对象都是启用了lines的对象(属性)。从最终用户的角度来看,这意味着:

It can hold one of more line series, being a line series an array of values were the values put together in a chart they would form a line.

它可以容纳多个线系列,作为一个线系列,就是数组的各个值联系在一起,在图标中就会形成一条线

A good example of a line (or lineseries) is the line formed by the closing prices of a stock. This is actually a well-known chart representation of the evolution of prices (known as Line on Close)

行(或行序列)的一个很好的例子就是由股票收盘价构成的线。这实际上是一个众所周知的价格演变图表(称为线上收盘价)

Regular use of the platform is only concerned with accessing lines. The previous mini-strategy example, lightly extended, comes in handy again:

该平台的常规使用只关心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):
# self.movav调用出线对象,通过sma取出具体的线,通过[0]取值 if self.movav.lines.sma[0] > self.data.lines.close[0]: print('Simple Moving Average is greater than the closing price')

Two objects with lines have been exposed:

暴露了两个lines对象

self.data It has a lines attribute which contains a close attribute in turn

self.data它有一个lines属性,该属性依次包含一个close属性

self.movav which is a SimpleMovingAverage indicator It has a lines attribute which contains a sma attribute in turn

self.movav是一个简单的平均指标,它有一个lines属性,该属性依次包含一个sma属性

Note

注意

It should be obvious from this, that lines are named. They can also be accessed sequentially following the declaration order, but this should only be used in Indicator development

很明显,行被命名了。也可以按照声明的顺序访问它们,但这只能在指标开发中使用

And both lines, namely close and sma can be queried for a point (index 0) to compare the values.

同时可以查询close和sma这两条线,取一个点(index 0)进行数值比较。

 

Shorthand access to lines do exist:

对线的快速访问也存在:

  • xxx.lines can be shortened to xxx.l

  • xxx.lines短写成xxx.l
  • xxx.lines.name can be shortened to xxx.lines_name

  • xxx.lines.name短些成xxx.lines_name

Complex objects like Strategies and Indicators offer quick access to data’s lines

像策略和指标这样的复杂对象提供了对数据线的快速访问

  • self.data_name offers a direct access to self.data.lines.name

  • self.data_name提供了对self.data.lines.name的直接访问
  • Which also applies to the numbered data variables: self.data1_name -> self.data1.lines.name
  • 这也适用于编号数据变量:self.data1_name - > self.data1.lines.name

 

Additionally the line names are directly accessible with:

另外行名也可以直接在对象上使用:

self.data.close and self.movav.sma

But the notation doesn’t make as clear as the previous one if lines are actually being accessed.

但是如果行被直接访问,这个符号就没有像前一个这么清楚了。

直接数据源访问lines对象内的具体线.

注意

Setting/Assigning the lines with these two later notations is not supported

不支持在lines属性后面进行设置与分配

 

Lines declaration

Lines 声明

If an Indicator is being developed, the lines which the indicator has must be declared.

如果正在开发指标,则必须声明指标所包含的行。

Just as with params this takes place as a class attribute this time ONLY as a tuple. Dictionaries are not supported because they do not store things following insertion order.

与params一样,这一次它作为类属性只作为元组发生。字典不受支持,因为它们不按照插入顺序存储内容。

For the Simple Moving Average it would be done like this:

对于简单的移动平均,可以这样做:

class SimpleMovingAverage(Indicator):
  # lines属性可以调用的名字 lines = ('sma',) ...  

注意

The comma following the declaration is needed in tuples if you pass a single string to the tuple or else each letter in the string would be interpreted as an item to be added to the tuple. Possibly one of the few spots where Python’s syntax got it wrong.

对于简单的移动平均值,可以这样做:如果向元组传递单个字符串,则在元组中需要声明后的逗号,否则字符串中的每个字母将被解释为要添加到元组中的项。这可能是Python语法出错的少数几个地方之一。(元祖不要少了逗号)

As seen in the previous example this declaration creates a sma line in the Indicator that can be later accessed in the Strategy’s logic (and possibly by other indicators to create more complex indicators)

如上例所示,此声明在指标中创建了一个sma线,该线稍后可以在策略逻辑中访问(也可能被其他指标用于创建更复杂的指标)。

For development is sometimes useful to access the lines in a generic non-named manner and this is where numbered access comes in handy:

对于开发来说,用通用的非命名方式访问行有时很有用,这就是编号访问的用处所在

  • self.lines[0] points to self.lines.sma
  • self.lines[0] 指向 self.lines.sma

Had more lines been defined they would be accessed with index 1, 2, and higher.

Had more lines been defined they would be accessed with index 1, 2, and higher.

如果定义了更多的行,就可以通过索引1、2或更高的方式访问它们。

And of course, extra shorthand versions do exist:

当然也有简化访问的方式存在

  • self.line points to self.lines[0]

  • self.lineX point to self.lines[X]

  • self.line_X point to self.lines[X]

 

Inside objects which are receiving datas feeds the lines below these data feeds can also be quickly accessed by number:

在接收数据的对象内部,这些数据的下面的行也可以通过数字快速访问:

  • self.data_Y points to self.data.lines[Y]

  • self.dataX_Y points to self.dataX.lines[X] which is a full shorthard version of self.datas[X].lines[Y]

课程里面这个地方有个小错误

 

Accessing lines in Data Feeds

在数据源中访问lines

Inside data feeds the lines can also be accessed omitting the lines. This makes it more natural to work with thinks like close prices.

在数据源中访问lines可以忽略lines,这使的它更加自然的工作,像收盘价

比如:

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

...

class MyStrategy(bt.Strategy):

    ...

    def next(self):

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

 Which seems more natural than the also valid: if self.data.lines.close[0] > 30.0:.

这样看起来更加自然,你也可以通过这样访问:if self.data.lines.close[0] > 30.0:

 The same doesn’t apply to Indicators with the reasoning being:

这个推断不适用于指标,其理由是:

An Indicator could have an attribute close which holds an intermediate calculation, which is later delivered to the actual lines also named close

指示符可以有一个close属性,该属性保存一个中间计算,然后传递到名为close的实际行

In the case of Data Feeds, no calculation takes place, because it is only a data source.

对于数据源,不需要进行计算,因为他原本一个数据源。

 

Lines len

Lines have a set of points and grow dynamically during execution, therefore the length can be measured at any time by invoking the standard Python len function.

Lines有一组点,在执行期间动态增长,因此可以通过调用标准Python len函数随时测量长度。

This applies to for example:

这适用于例如:

  • Data Feeds

  • Strategies

  • Indicator

An additional property applies to Data Feeds when the data is preloaded:

一个附加的属性适用于数据源时,数据是预先加载:

Method buflen

方法 buflen

The method returns the actual number of bars the Data Feed has available.

该方法返回数据馈送可用的实际条数。

The difference between len and buflen

len和buflen的区别

  • len reports how many bars have been processed

  • len报告有多少bars已经被处理
  • buflen reports the total number of bars which have been loaded for the Data Feed

  • buflen报告了为数据饲料加载的条的总数

self.data的数据源,默认是预加载的,所以就它有buflen的方法,可以看出已经预加载的数据。便于后期进行resimple或者filter使用

If both return the same value, either no data has been preloaded or the processing of bars has consumed all preloaded bars (and unless the system is connected to a live feed, this will mean the end of processing)

如果两者返回相同的值,要么没有预加载数据,要么对条的处理消耗了所有预加载条(除非系统连接到实时提要,否则这将意味着处理结束)

 

Inheritance of Lines and Params

Lines与Params的继承

A kind of metalanguage is in place to support declaration of Params and Lines. Every effort has been made to make it compatible with standard Python inheritance rules.

有一种元语言支持参数和行的声明。为了使它与标准的Python继承规则兼容,我们已经尽了一切努力。

 

Params inheritance

Inheritance should work as expected:

继承应该像预期的那样工作:

  • Multiple inheritance is supported

  • 支持多重继承
  • Params from base classes are inherited

  • 参数来至基类或者继承
  • If multiple base classes define the same param the default value of the last class in the inheritance list is used

  • 如果多个基类定义相同的参数,则使用继承列表中最后一个类的默认值
  • If the same param is redefined in a child class, the new default value takes over that of the base class

  • 如果在子类中重新定义相同的参数,则新的默认值将接管基类的默认值

这个跟Python中类的继承一样,使用了Python的特性

Lines Inheritance

  • Multiple inheritance is supported

  • 支持多重继承
  • Lines from all base classes are inherited. Being named lines there will only be one version of a line if the same name has been used more than once in base classes

  • 所有的lines对象都使从基类中继承的。作为命名行,如果在基类中多次使用同一个名称,则只有一个版本的行

这上面写的这些概念,让我一下感觉很唐突

 

Indexing: 0 and -1

索引:0和-1

Lines as seen before are line series and have a set of points that conform a line when drawn together (like when joining all closing prices together along a time axis)

如前所述,Lines就是一系列的line,line有一组点顺着一条线画在一起(比如在沿着时间轴将所有收盘价合并在一起时)

To access those points in regular code, the choice has been to use a 0 based approach for the current get/set instant.

要通过常规代码访问这些点,可以适用0为基础的方法,对瞬间的状态进行读取或者设置

Strategies do only get values. Indicators do also set values.

策略只能读取值,指标还能进行设置值

From the previous quick strategy example where the next method was briefly seen:

从以前的快速策略例子中,我们可以简要的看到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')

The logic is getting the current value of the moving average and the current closing price by applying index 0.

该逻辑通过索引0将得到当前移动平均线的值与收盘价

注意

Actually for index 0 and when applying logic/arithmetic operators the comparison can be made directly as in:

实际上,对于索引0在应用逻辑/算术运算符时,可以直接进行比较: 

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

See later in the document the explanation for operators.  

请参阅文档后面对操作符的解释。

Setting is meant to be used when developing, for example, an Indicator, because the current output value has to be set by the indicator.

设置是在开发的时候使用的。比如,一个指标,因为指标的输出用来设置另一个指标

A SimpleMovingAverage can be calculated for the current get/set point as follows:

可按如下方式计算当前获取/设置SimpleMovingAverage的点:

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

通过.get的方式取出一段bar周期内的数据

Accessing previous set points has been modeled following the definition Python makes for -1 when accessing an array/iterable

访问以前的设置点是按照Python在访问array/iterable时,使用-1定义的

It points to the last item of the array

这个将指向数组的最后一项

The platform consider the last set item (before the current live get/set point) to be -1.

该平台认为最后一个设置项(当前状态get/set点之前)为-1

As such comparing the current close to the previous close is a 0 vs -1 thing. In a strategy, for example:

因此对比当前close与前一个close就是0与-1的事情。在策略中,可以这样

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

Of course and logically, prices set before -1 will be accessed with -2, -3, ....  

按照这个逻辑,-2,-3能够访问-1之前的价格

 

Slicing(切片)

backtrader doesn’t support slicing for lines objects and this is a design decision following the [0] and [-1] indexing scheme. With regular indexable Python objects you would do things like:

backtrader不支持lines对象的切片,这是遵循[0]和[-1]索引方案的设计决策。使用常规的可索引Python对象,你会做如下事情:

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

But remember that with the choice for 0 … it is actually the currently delivered value, there is nothing after it. Also:

但是请记住,在lines对于0的选择…它实际上是当前交付的价值,在它之后没有任何东西。另外:

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

Again … 0 is the current value and -1 is the latest (previous) delivered value. That’s why a slice from 0 -> -1 makes no sense in the backtrader ecosystem.  

再次…0是当前值,-1是最近(先前)交付的值。这就是为什么从0 -> -1在backtrader的生态系统中毫无意义。

If slicing were ever to be supported, it would look like:

如果切片曾经被支持,它将看起来像:

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

  

前面讲的一堆没用的,下面才是讲到如何切片

 

Getting a slice

(获得切片)

An array with the latest values can still be gotten. The syntax:

仍然可以获得具有最新值的数组。语法:

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

That would have returned an arry with 1 value (size=1) with the current moment 0 as the staring point to look backwards.

这将返回一个带有1个值(size=1)的array(数组),并将当前时刻0作为向后查看的起始点

To get 10 values from the current point in time (i.e.: the last 10 values):

从当前时间获取最近的10个值

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

Of course the array has the ordering you would expect. The leftmost value is the oldest one and the rightmost value is the most current (it is a regular python array and not a lines object)

当然,数组具有您所期望的顺序。最左边的值是最老的,最右边的值是最新的(它是一个常规的python数组,而不是lines对象)

To get the last 10 values skipping only the current point:

要获得最后10个值,只跳过当前点:

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

在取数组对象的时候,要注意,如果价格你需要取前面20个数据,现在还没有这么多数据,你是取不到数据的。

这个跟指标线很像,你定义的指标应该也在取前面的数值,如果取不到数值,你的指标也不会出来。

 

LInes:DELAYED indexing

The [] operator syntax is there to extract individual values during the next logic phase. Lines objects support an additional notation to address values through a delayed lines object during the __init__ phase.

[]操作语法用于在nex方法逻辑中提取单个值。Lines对象支持一种附加的表示法,用于在__init__阶段通过delayed Lines对象寻址值。

Let’s say that the interest in the logic is to compare the previous close value to the actual value of a simple moving average. Rather than doing it manually in each next iteration a pre-canned lines object can be generated:

我们假设,逻辑的兴趣在于比较简单移动平均的实际值和之前的接近值。与其next迭代中手动操作,还不如生成一个预封闭的lines对象:

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

    def __init__(self):

        self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)
      # 前一天的收盘价大于均线 self.data.close(-1)昨天的收盘价 self.cmpval = self.data.close(-1) > self.sma def next(self):
# 取值每个点应该是0或1值,成立输出 if self.cmpval[0]: print('Previous close is higher than the moving average')

Here the (delay) notation is being used:  

这里使用了(延迟)表示法

This delivers a replica of the close prices but delayed by -1.

这提供了一个副本,是延迟-1的

And the comparison self.data.close(-1) > self.sma generates another lines object which returns either 1 if the condition is True or 0 if False

然后,生成一个对照self.data.close(-1) > self.sma的lines对象,如果条件为真返回1,条件为假返回0

延迟用在stratrgy的__init__阶段,通过操作符,创建一个新的indicator来使用。

 

 

Lines Coupling(耦合)

The operator () can be used as shown above with delay value to provide a delayed version of a lines object.

如上所示,适用带有延迟值的操作符()来生成lines对象的延迟版本.

If the syntax is used WITHOUT providing a delay value, then a LinesCoupler lines object is returned. This is meant to establish a coupling between indicators that operate on datas with different timeframes.

如果适用的语法没有提供延迟值,则返回一个LinesCoupler lines对象。这意味着在不同的时间框架内对数据指标的操作进行耦合

Data Feeds with different timeframes have different lengths, and the indicators operating on them replicate the length of the data. Example:

不同时间框架的数据提要有不同的长度,对它们进行操作的指标会复制数据的长度。例子:

  • A daily data feed has around 250 bars per year

  • 每天的数据传输每年大约有250个bars
  • A weekly data feed has 52 bars per year

  • 每周的数据feed每年有52bars

Trying to create an operation (for example) which compares 2 simple moving averages, each operating on the datas quoted above would break. It would be unclear how to match the 250 bars from the daily timeframe to the 52 bars of the weekly timeframe.

尝试创建一个操作(比如)来比较两个简单的移动平均线。在数据上引用,每次操作都会中断。我们不清楚如何将日线框架的250bars与周线的52bars进行匹配。

The reader could imagine a date comparison taking place in the background to find out a day - week correspondence, but:

读者可以想象在在日期比较后台找打了天-周的对应。

Indicators are just mathematical formulas and have no datetime information

指标只是数学公式,没有日期时间信息

They know nothing about the environment, just that if the data provides enough values, a calculation can take place.

他们对环境一无所知,只知道如果数据提供了足够的值,就可以进行计算。

这个()空调用 来拯救我们

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

    def __init__(self):

        # data0 is a daily data
        # 来至文件1
        sma0 = btind.SMA(self.data0, period=15)  # 15 days sma
        # data1 is a weekly data
        # 来至文件2
        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')
    

Here the larger timeframe indicator, sma1 is coupled to the daily timeframe with sma1(). This returns an object which is compatible with the larger numbers of bars of sma0 and copies the values produced by sma1, effectively spreading the 52 weekly bars in 250 daily bars  

这是一个较大 时间框架指标,sma1通过sma1()耦合到日框架。这将返回一个对象,它兼容了sma0中大量的bars,并复制了sma1产生的值,有效的将52的周bars扩展到250个日bars

 

这个需要两个不同的时间框架表导入,指标只有数据,没有时间轴

 

Operators, using natural constructs

操作符,适用自然构造

In order to achieve the “ease of use” goal the platform allows (within the constraints of Python) the use of operators. And to further enhance this goal , the use of operators has been broken in two stages.

为了实现“易于使用”的目标,该平台允许(在Python的约束下)使用操作符。为了进一步提高这一目标,对操作符的使用已分两个阶段打破

 

Stage 1 - Operators Create Objects

阶段1- 操作符创建对象

An example has already been seen even if not explicitly meant for this. During the initialization phase (__init__ method) of objects like Indicators and Strategies, operators create objects that can be operated upon, assigned or kept as reference for later using during the evaluation phase of the Strategy’s logic.

我们已经看到一个这样的示例,即使它没有表明用于此。在对象的初始化阶段(__init__方法)比如Indicators(指标)、Strategies(策略),操作符可以创建可以操作、分配或保留的对象,以供以后在策略逻辑评估阶段适用。

Once again a potential implementation of a SimpleMovingAverage, further broken down into steps.

再一次实现了SimpleMovingAverage,进一步细分为步骤。

The code inside the SimpleMovingAverage indicator __init__ could look like:

代码内部SimpleMovingAverage的指标__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

 

A more complete use case is shown during the initialization of a Strategy:

一个更完整的用例显示在策略的初始化过程中:

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
        # 最高价低于3.5
        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
        
# 通过And要求两个逻辑都成立 sell_sig = bt.And(close_over_sma, sma_dist_small)

After the above operations have taken place, sell_sig is a Lines object which can be later used in the logic of the Strategy, indicating if the conditions are met or not.  

在执行了上述操作之后,sell_sig是一个Lines对象,可以稍后在策略的逻辑中使用,指示条件是否满足。

 

Stage 2 - Operators true to nature

阶段2 操作符真假对象

Let’s first remember that a strategy has a next method which is called for every bar the system processes. This is where operators are actually in the stage 2 mode. Building on the previous example:

首先让我们记住,一个策略有一个next方法,该方法针对系统处理的每个bar调用。这就是操作员实际上处于阶段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')

Not a very useful strategy, just an example. During Stage 2 operators return the expected values (boolean if testing for truth and floats if comparing them to floats) and also arithmetic operations do.  

这不是一个很有用的策略,只是一个例子。在第2阶段,操作符返回期望值(如果测试是否为真,则返回布尔值;如果与浮点数比较,则返回浮点数),算术操作也返回期望值。

注意

Notice that comparisons are actually not using the [] operator. This is meant to further simplify things.

注意,比较实际上没有使用[]操作符。这意味着进一步简化事情。

if self.sma > 30.0: … compares self.sma[0] to 30.0 (1st line and current value)

if self.sma > self.data.close: … compares self.sma[0] to self.data.close[0]

 

Some non-overriden operators/functions

一些没有被重写的操作符方法

 Python will not allow overriding everything and thus some functions are provided to cope with the cases.

Python不允许覆盖所有内容,因此提供了一些函数来处理这种情况。

注意

Only meant to be used during Stage 1, to create objects which later provide values.

仅在阶段1中使用,用于创建以后提供值的对象。

Operators:

  • and -> And

  • or -> Or

Logic Control:

  • if -> If

Functions:

  • any -> Any

  • all -> All

  • cmp -> Cmp

  • max -> Max

  • min -> Min

  • sum -> Sum

Sum实际使用math.fsum作为底层操作,因为平台使用浮点数,并且应用常规和可能会影响精度。

  • reduce -> Reduce

 These utility operators/functions operate on iterables. The elements in the iterables can be regular Python numeric types (ints, floats, …) and also objects with Lines.

这些实用程序操作符/函数对迭代进行操作。迭代器中的元素可以是常规的Python数值类型(int、float、…),也可以是带行的对象。

An example generating a very dumb buy signal:

一个产生笨方法购买信号的例子:

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

It is obvious that if the sma1 is higher than the high, it must be higher than the close. But the point is illustrating the use of bt.And.  

很明显,如果sma1高于高点,那么它一定高于收盘价。但重点是要说明bt.And的用法。

 

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

Breakdown:

分解:

Generate a SMA on data.close of period=15

基于收盘价生成15日均线SMA

And then

然后

  • bt.If the value of the sma is larger than close, return low, else return high
  • bt.If如果收盘价大于15日均线价返回最低价,否则返回最高价。

Remember that no actual value is being returned when bt.If is being invoked. It returns a Lines object which is just like a SimpleMovingAverage.

请记住正在调用bt.if返回的没有实际价格。它返回了一个LINES对象,就像SimpleMovingAverage

The values will be calculated later when the system runs

这些值将在系统运行时计算

The generated bt.If Lines object is then fed to a 2nd SMA which will sometimes use the low prices and sometimes the high prices for the calculation

生成的bt.If Lines对象然后被送入第二SMA,该SMA有时使用低价格,有时使用高价格进行计算

Those functions take also numeric values. The same example with a modification:

这些函数也接受数值。同样的例子有一个修改:

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)

  

Now the 2nd moving average uses either 30.0 or the high prices to perform the calculation, depending on the logic status of sma vs close

现在第二移动平均线使用30.0或高价格执行计算,取决于sma1 vs close的逻辑状态

注意

The value 30 is transformed internally into a pseudo-iterable which always returns 30

值30在内部被转换为一个总是返回30的伪迭代

 

posted @ 2020-08-05 16:33  就是想学习  阅读(1192)  评论(0编辑  收藏  举报