混合时间框架的指标#

版本1.3.0.92 增加了从不同时间框架混合数据(来自数据源和/或指标)的可能性。

背景: 指标是智能但愚蠢的对象。

  • 它们是智能的,因为它们可以进行复杂的计算。

  • 它们是愚蠢的,因为它们在进行计算时不了解数据的来源。

因此:

  • 如果提供值的数据源具有不同的时间框架,在 Cerebro 引擎内有不同的长度,则指标将无法正常工作。

以下是一个计算示例,其中 data0 的时间框架为天,而 data1 的时间框架为

pivotpoint = btind.PivotPoint(self.data1) sellsignal = self.data0.close < pivotpoint.s1

在这里,当收盘价低于 s1 线(第一个支撑位)时,寻找一个 卖出信号 。.. 注意:: “PivotPoint”根据定义适用于较长的时间范围

此过程将在过去发生以下错误:

return self.array[self.idx + ago]
IndexError: array index out of range

原因如下: self.data.close 提供了从第一个瞬间开始的值,但是 PivotPoint (以及 s1 行)只有在过去了一个 完整的月份 后才会提供值,这大约等于 self.data0.close 的22个值。在这22个 closes 期间, s1 还没有值,并且尝试从底层数组中获取它将会失败。

Lines 对象支持 (ago) 操作符( Python__call__ 特殊方法),用于产生自身的延迟版本:

close1 = self.data.close(-1)

在此示例中,当通过 [0] 访问对象 close1 时,它始终包含由 close 提供的前一个值( -1 )。借助这种语法可以适应不同的时间范围。让我们重新编写上述的 pivotpoint 片段:

pivotpoint = btind.PivotPoint(self.data1)
sellsignal = self.data0.close < pivotpoint.s1()

注意到 () 在没有参数的情况下执行(背景中提供了 None )。以下是正在发生的事情:

  • pivotpoint.s1() 返回一个内部的 LinesCoupler 对象,该对象遵循较大范围的节奏。该耦合器用最新的实际 s1 值(默认值为 NaN )填充自身但是要使魔法起作用,还需要一些额外的东西。 Cerebro 必须使用以下方式创建:

`python cerebro = bt.Cerebro(runonce=False) `

或者使用以下方式执行:

`python cerebro.run(runonce=False) `

在这种模式下,指标和延迟评估的自动“line”对象会逐步执行,而不是在紧密循环中执行。这使得整个操作变慢,但却使其成为了可能。

底部的示例脚本现在可以正常运行:

`bash $ ./mixing-timeframes.py `

输出如下:

`plaintext 0021,0021,0001,2005-01-31,2984.75,2935.96,0.00 0022,0022,0001,2005-02-01,3008.85,2935.96,0.00 ... 0073,0073,0003,2005-04-15,3013.89,3010.76,0.00 0074,0074,0003,2005-04-18,2947.79,3010.76,1.00 ... `

在第74行发生了第一个 close < s1 的情况。脚本还提供了一种额外的可能性: 将指标的所有行链接起来 。以前我们写的是:

self.sellsignal = self.data0.close < pp.s1()

现在的替代方法是:

pp1 = pp()
self.sellsignal = self.data0.close < pp1.s1

现在整个 PivotPoint 指标都被链接在一起,可以访问其中的任何一行(即 p、` r1 、`r2s1 `、`s2 )。脚本只对`s1`感兴趣,可以直接访问。:

$ ./mixing-timeframes.py --multi

输出结果:

0021,0021,0001,2005-01-31,2984.75,2935.96,0.00
0022,0022,0001,2005-02-01,3008.85,2935.96,0.00
...
0073,0073,0003,2005-04-15,3013.89,3010.76,0.00
0074,0074,0003,2005-04-18,2947.79,3010.76,1.00
...

这里没有什么意外。和以前一样。甚至可以绘制”链接”的对象:

$ ./mixing-timeframes.py --multi --plot.. thumbnail:: indicators-mixing-timeframes.png

完整的耦合语法#

对于包含多条线的 lines 对象(例如像 PivotPoint 这样的 Indicators ):

  • obj(clockref=None, line=-1)

    • clockref 如果 clockrefNone ,则周围的对象(在这个示例中是 Strategy )将作为引用,用于将较大的时间框架(例如: Months )调整到较小/更快的时间框架(例如: Days

      如果需要,可以使用其他引用

    line
    • 如果使用默认的 -1 ,那么所有的 lines 都会被耦合。

    • 如果使用其他整数(例如, 01 ),那么只有一条线会被耦合并通过索引(从 obj.lines[x] )获取

    • 如果传入一个字符串,将会通过名称获取该线。

      在示例中,也可以这样做:

      coupled_s1 = pp(line=’s1’)

对于只有一条线的 lines 对象(例如来自指标 PivotPoint 的线 s1 ): - obj(clockref=None) (参见上方的 clockref

结论#

在常规的 () 语法中,可以将不同时间框架的数据源混合在指标中,但需要注意需要实例化 cerebro 或者使用 runonce=False 创建。

脚本代码和用法#

可在 backtrader 源代码中找到示例。用法:

$ ./mixing-timeframes.py –help usage: mixing-timeframes.py [-h] [–data DATA] [–multi] [–plot]

Sample for pivot point and cross plotting

optional arguments:
-h, --help

显示帮助信息并退出

--data DATA

要读取的数据(默认:../../datas/2005-2006-day-001.txt)

--multi

将指标的所有线连接起来(默认:False)

--plot

绘制结果(默认:False)

代码:

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse

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


class St(bt.Strategy):
    params = dict(multi=True)

    def __init__(self):
        self.pp = pp = btind.PivotPoint(self.data1)
        pp.plotinfo.plot = False  # deactivate plotting

        if self.p.multi:
            pp1 = pp()  # couple the entire indicators
            self.sellsignal = self.data0.close < pp1.s1
        else:
            self.sellsignal = self.data0.close < pp.s1()

    def next(self):
        txt = ','.join(
            ['%04d' % len(self),
             '%04d' % len(self.data0),
             '%04d' % len(self.data1),
             self.data.datetime.date(0).isoformat(),
             '%.2f' % self.data0.close[0],
             '%.2f' % self.pp.s1[0],
             '%.2f' % self.sellsignal[0]])

        print(txt)


def runstrat():
    args = parse_args()

    cerebro = bt.Cerebro()
    data = btfeeds.BacktraderCSVData(dataname=args.data)
    cerebro.adddata(data)
    cerebro.resampledata(data, timeframe=bt.TimeFrame.Months)

    cerebro.addstrategy(St, multi=args.multi)

    cerebro.run(stdstats=False, runonce=False)
    if args.plot:
        cerebro.plot(style='bar')


def parse_args():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for pivot point and cross plotting')

    parser.add_argument('--data', required=False,
                        default='../../datas/2005-2006-day-001.txt',
                        help='Data to be read in')

    parser.add_argument('--multi', required=False, action='store_true',
                        help='Couple all lines of the indicator')

    parser.add_argument('--plot', required=False, action='store_true',
                        help=('Plot the result'))

    return parser.parse_args()


if __name__ == '__main__':
    runstrat()