指標是困難的野獸。不是因為它們通常難以編碼,而是主要是因為名稱具有誤導性,並且人們對指標是什麼有不同的期望。
讓我們嘗試至少定義什麼是反向交易者生態系統中的指標。
它是一個定義至少一個輸出行的對象,可以定義影響其行為的參數,並將一個或多個數據饋送作為輸入。
為了使指標盡可能通用,選擇了以下設計原則:
輸入數據饋送可以是任何看起來像數據饋送的東西,這帶來了直接的優勢:因為其他指標看起來像數據饋送,所以可以將指標作為輸入傳遞給其他指標
不攜帶
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)
今天所剩無幾……盡情享受吧!!!