指针是困难的野兽。不是因为它们通常难以编码,而是主要是因为名称具有误导性,并且人们对指针是什么有不同的期望。
让我们尝试至少定义什么是反向交易者生态系统中的指针。
它是一个定义至少一个输出行的对象,可以定义影响其行为的参数,并将一个或多个数据馈送作为输入。
为了使指针尽可能通用,选择了以下设计原则:
输入数据馈送可以是任何看起来像数据馈送的东西,这带来了直接的优势:因为其他指针看起来像数据馈送,所以可以将指针作为输入传递给其他指针
不携带
datetime
时间行有效负载。之所以如此,是因为输入本身可能没有要同步的datetime
时间有效负载。与一般系统范围的datetime
时间同步可能是不正确的,因为该指针可能使用每周时间范围内的数据,而系统时间可能以秒为单位,因为这是几个数据馈送中最低的分辨率之一。操作必须是幂等的,即:如果使用相同的输入调用两次且参数不变,则输出必须相同。
考虑到可以要求指针在同一时间点以相同的输入多次运行操作。虽然这似乎不需要,但如果系统支持数据重放(即:从较小的时间帧实时构建较大的时间帧)
最后:指针将其输出值写入当前时刻,即:索引
0
。如果不是,它将命名为Study
。一项Study
将寻找模式并写入过去的输出值。例如,参见Backtrader社区 - ZigZag
一旦定义(在反向交易者生态系统中)明确,让我们尝试看看我们如何实际编写动态指针。看起来我们不能,因为从前面提到的设计原则来看,指针的操作过程或多或少是……不可变的。
最高的……因为……
通常启动的一个指针是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
让它充满活力
有时,我们需要指针是动态的并改变其行为以对实时条件做出反应。例如,请参阅backtrader社区中的这个问题: Highest high since position was open
我们当然不知道何时开仓/平仓,将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
我们使用通知
trade
的值isopen
作为一个标志来知道我们是否必须记录输入数据的最高点当
trade
结束时,isopen
的值为False
,我们将停止记录最高值
如需参考,请参阅: Backtrader文档交易
简单的!!!
使用方法
有些人会反对修改作为指针声明一部分的param
,并且只应在实例化期间设置。
好吧,我们来找个方法。
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)
今天所剩无几……尽情享受吧!!!