指标是困难的野兽。不是因为它们通常难以编码,而是主要是因为名称具有误导性,人们对指标是什么有不同的期望。

让我们尝试至少定义什么是反向交易者 生态系统中的指标。

它是一个定义至少一个输出的对象,可以定义影响其行为的参数,并将一个或多个数据馈送作为输入。

为了使指标尽可能通用,选择了以下设计原则:

  • 输入数据馈送可以是任何看起来像数据馈送的东西,这带来了直接的优势:因为其他指标看起来像数据馈送,所以可以将指标作为输入传递给其他指标
  • 不携带datetime 线路有效载荷。之所以如此,是因为输入datetime本身可能没有要同步的有效负载。与一般系统范围同步datetime可能是不正确的,因为指标可能使用每周时间范围内的数据,而系统时间可能以为单位,因为这是几个数据馈送中的最低分辨率之一。
  • 操作必须是幂等的,即:如果使用相同的输入调用两次且参数不变,则输出必须相同。

    考虑到可以要求指标在同一时间点以相同的输入多次执行操作。虽然这似乎不需要,但如果系统支持数据重放(即:从较小的时间帧实时构建较大的时间帧)

  • 最后:一个指标将其输出值写入当前时刻,即: index 0。如果不是,它将命名为Study. AStudy 将查找模式并写入过去的输出值。

一旦定义(在反向交易者生态系统中)明确,让我们尝试看看我们如何实际编写动态指标。看起来我们不能,因为从前面提到的设计原则来看,指标的操作过程或多或少是……不可变的。

最高的……因为……

通常启动的一个指标是Highest(别名 MaxN),用于在给定时期内获得最高的东西。如在

import backtrader as bt

class MyStrategy(bt.Strategy)
    def __init__(self):
        self.the_highest_high_15 = bt.ind.Highest(self.data.high, period=15)

    def next(self):
        if self.the_highest_high_15 > X:
            print('ABOUT TO DO SOMETHING')

在这个片段中,我们实例化Highest以跟踪过去 15 个时期的最高点。如果最高的高度大X于某事会做的事情。

这里的问题:

  • period固定在15

让它充满活力

有时,我们需要指标是动态的并改变其行为以对实时条件做出反应。我们当然不知道何时会打开/关闭头寸,并且将 设置period为固定值是15没有意义的。让我们看看我们如何做到这一点,将所有内容打包在一个指标中

动态参数

我们将首先使用我们将在指标生命周期内更改的参数,从而实现动态。

import backtrader as bt

class DynamicHighest(bt.Indicator):
    lines = ('dyn_highest',)
    params = dict(tradeopen=False)

    def next(self):
        if self.p.tradeopen:
            self.lines.dyn_highest[0] = max(self.data[0], self.dyn_highest[-1])

class MyStrategy(bt.Strategy)
    def __init__(self):
        self.dyn_highest = DynamicHighest(self.data.high)

    def notify_trade(self, trade):
        self.dyn_highest.p.tradeopen = trade.isopen

    def next(self):
        if self.dyn_highest > X:
            print('ABOUT TO DO SOMETHING')

瞧!我们拥有它,到目前为止,我们还没有违反为我们的指标制定的规则。我们来看看指标

  • 它定义了一个名为的输出dyn_highest
  • 它有一个参数tradeopen=False
  • (是的,它需要数据馈送,仅仅是因为它是子类Indicator
  • 如果我们next总是用相同的输入调用,它总是会返回相同的值

唯一的事情:

  • 如果参数的值改变了,输出就会改变(上面的规则说只要参数不改变,输出就保持不变)

我们用它notify_trade来影响我们的DynamicHighest

  • 我们使用 notify 的值isopen作为trade标志来知道我们是否必须记录输入数据的最高点
  • trade收盘时, 的值isopen将是False并且我们将停止记录最高值

简单的!!!

使用方法

有些人会反对修改作为param指标声明的一部分的 a,它应该只在实例化期间设置。

好吧,我们来找个方法。

import backtrader as bt

class DynamicHighest(bt.Indicator):
    lines = ('dyn_highest',)

    def __init__(self):
        self._tradeopen = False

    def tradeopen(self, yesno):
        self._tradeopen = yesno

    def next(self):
        if self._tradeopen:
            self.lines.dyn_highest[0] = max(self.data[0], self.dyn_highest[-1])

class MyStrategy(bt.Strategy)
    def __init__(self):
        self.dyn_highest = DynamicHighest(self.data.high)

    def notify_trade(self, trade):
        self.dyn_highest.tradeopen(trade.isopen)

    def next(self):
        if self.dyn_highest > X:
            print('ABOUT TO DO SOMETHING')

差别不大,但现在指标有一些额外的样板文件 __init__和方法tradeopen(self, yesno)。但我们的动力 DynamicHighest是一样的。

奖励:让它通用

让我们恢复params并制作一个可以应用不同功能的指标,而不仅仅是max

import backtrader as bt

class DynamicFn(bt.Indicator):
    lines = ('dyn_highest',)
    params = dict(fn=None)

    def __init__(self):
        self._tradeopen = False
        # Safeguard for not set function
        self._fn = self.p.fn or lambda x, y: x

    def tradeopen(self, yesno):
        self._tradeopen = yesno

    def next(self):
        if self._tradeopen:
            self.lines.dyn_highest[0] = self._fn(self.data[0], self.dyn_highest[-1])

class MyStrategy(bt.Strategy)
    def __init__(self):
        self.dyn_highest = DynamicHighest(self.data.high, fn=max)

    def notify_trade(self, trade):
        self.dyn_highest.tradeopen(trade.isopen)

    def next(self):
        if self.dyn_highest > X:
            print('ABOUT TO DO SOMETHING')

说完了!我们添加了:

  • params=dict(fn=None)

    收集最终用户想要使用的功能

  • 如果用户未传递特定函数,则使用占位符函数的保护措施:
# Safeguard for not set function
self._fn = self.p.fn or lambda x, y: x

我们使用函数(或占位符)进行计算:

self.lines.dyn_highest[0] = self._fn(self.data[0], self.dyn_highest[-1])

在我们的(现在命名的)DynamicFn指标的调用中说明我们想要使用哪个函数…… max (这里没有惊喜):

self.dyn_highest = DynamicHighest(self.data.high, fn=max)

评论被关闭。