原文:
www.backtrader.com/
開發一個指標
原文:
www.backtrader.com/blog/posts/2015-07-18-developing-an-indicator/developing-an-indicator/
在對 backtrader 進行了大量的微調之後(因為它已經執行了一段時間),我決定不僅透過 GitHub 分享它,還要告訴世界它的存在,並在"Reddit"上釋出了關於其存在的帖子。
在關於為什麼交易/演算法交易平臺會突然出現的評論和關於平臺是否支援多個同時交易的實時交易的私人問題之後,我得出了結論,我的孩子應該有自己的部落格。
這就是我們所在的地方。但讓我們專注於業務。
backtrader
旨在讓我快速嘗試想法,並檢查我的眼睛可能告訴我的是否可能存在機會。
backtrader
(目前)完全是關於回測的,尚未連線到任何實時交易平臺,甚至可能不會(儘管我確實相信技術實現是允許的)
當我使用表示式"嘗試想法"時,我的意思是兩件事:
-
能夠快速起草一個指標,並能夠直觀評估其行為
-
無論如何,都要參與開發圍繞該指標或與其他指標組合的潛在策略
我個人的交易是 100%判斷性的,沒有一個決定是由自動化系統做出的。但我會看看指標所說的內容。無論"指標"是否真的是一個訊號,都留給了我有缺陷的人類思維。
但讓我們來嘗試一下。在第一次釋出到 Reddit 後,我新增了一個著名的指標:
- Trix
Stockcharts 在這裡有關於 Trix 的很好的討論:ChartSchool - Trix
讓我們嘗試如何做到,用盡可能少的行:
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import backtrader as bt
import backtrader.indicators as btind
class MyTrix(bt.Indicator):
lines = ('trix',)
params = (('period', 15),)
def __init__(self):
ema1 = btind.EMA(self.data, period=self.p.period)
ema2 = btind.EMA(ema1, period=self.p.period)
ema3 = btind.EMA(ema2, period=self.p.period)
self.lines.trix = 100.0 * (ema3 - ema3(-1)) / ema3(-1)
Trix 指標已經投入使用。看著這一切,作為該平臺的作者,我真的相信我的目標是能夠快速地輕鬆嘗試新的想法……已經實現了。
開發的詳細過程:
-
lines = (‘trix’,)
這個元組定義了指標的輸出線(在本例中只有一個)。類宣告開始時的這個語句在類建立和物件例項化過程中產生了大量的背景操作。
可以說,該物件具有一個名為"lines"的屬性,其中包含"trix"。
作為額外的獎勵,如果指標本身沒有使用名為"trix"的名稱,則可以透過"self.trix"來獲得"line"。但為了清晰起見,我更喜歡"self.lines.trix"
附加訪問方法:
-
self.l.trix
-
self.lines[0] … 作為索引對應於元組中的位置
-
-
params = ((‘period’, 15),)
這個元組的元組(也可以是 dict 或 OrderedDict)定義了指標接受的引數並宣告瞭預設值。
解析 kwargs 的負擔從使用者的肩上卸下來了。
引數可以透過“self.params.xxxxx”表示法或透過速記“self.p.xxxxx”訪問。
-
計算(其中 EMA 代表指數移動平均)
-
ema1 = btind.EMA(self.data, period=self.p.period)
新的獎金顯示出來了…“self.data”。這似乎突然冒出來,但這又是指標背後進行的預處理。
傳遞給指標進行計算的任何“資料”都會被攔截並放置在一個
self.datas
陣列中,通常可以使用self.datas[0]
來訪問第一個資料。縮寫確實存在,看起來像:self.data 和 self.data0 用於陣列中的第一個資料。從那時起,self.data1,self.data2。
Trix 只需要一個資料
-
ema2 = btind.EMA(ema1, period=self.p.period)
沒有太多可說的。EMA 使用 ema1 作為輸入資料。
-
ema3 = btind.EMA(ema2, period=self.p.period)
更少的話可說
-
self.lines.trix = 100.0 * (ema3 - ema3(-1)) / ema3(-1)
首先,首先進行了簡單的 1 週期百分比差值計算。
魔術 ema3(-1)是一種表示:ema 的上一個值的記法。
計算的結果被分配給了在類建立過程中定義的輸出“line”“trix”。
-
輕而易舉。但是如果我沒有得到 Trix 正在做什麼的視覺反饋(即使 Stockcharts 上有一篇很好的文章),我就不會進行“實驗”。
注意
實際的 Trix 實現具有一些額外的花哨功能,主要用於美化繪圖,對於本帖子沒有相關性。
假設我們已經將MyTrix
指標放在了一個 mytrix.py 檔案中。
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import backtrader as bt
import backtrader.feeds as btfeeds
from mytrix import MyTrix
class NoStrategy(bt.Strategy):
params = (('trixperiod', 15),)
def __init__(self):
MyTrix(self.data, period=self.p.trixperiod)
if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro()
# Add a strategy
cerebro.addstrategy(NoStrategy, trixperiod=15)
# Create a Data Feed
datapath = ('../datas/2006-day-001.txt')
data = bt.feeds.BacktraderCSVData(dataname=datapath)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Run over everything
cerebro.run()
# Plot the result
cerebro.plot()
並且視覺輸出如下(在新視窗/標籤中開啟圖表以獲取全尺寸影像),希望顯示出指標可以多快地建立和透過backtrader
進行視覺評估的簡單性。
食譜/資源
介紹
原文:
www.backtrader.com/recipes/intro/
本節包含可直接應用於backtrader的配方和資源,例如指標或第三方商店、經紀人或資料來源。
指標
簡介
原文:
www.backtrader.com/recipes/indicators/intro/
本節收集了一些指標實現,它們不是backtrader核心的一部分。
使用它們:
-
將程式碼複製到你選擇的檔案中或直接放在你的策略上方
-
如果複製到檔案中,請從檔案中匯入它
from myfile import TheIndicator`
-
並將其納入你的策略中
class MyStrategy(bt.Strategy): def __init__(self): self.myind = TheIndicator(self.data, param1=value1, ...)`
絕對強度直方圖
原文:
www.backtrader.com/recipes/indicators/ash/ash/
這是一個看起來起源於外匯世界的指標,可能在 fxcodebase.com
,但似乎很難追蹤實際起源是什麼。
潛在地首先在 LUA 中實現:
fxcodebase.com/code/viewtopic.php?f=17&t=3836
同樣還提到,更新和修訂的 LUA 版本可在以下位置找到:
fxcodebase.com/code/viewtopic.php?f=17&t=2987&p=6869&hilit=Absolute+Strength#p6869
同一站點託管了一個 MQL4
版本:
fxcodebase.com/code/viewtopic.php?f=38&t=61541
另一個 MQL5
版本可以在這裡找到:
www.mql5.com/en/code/21429
所有版本都似乎很複雜,由於語言和平臺實現的原因,MQL5
版本有 227 行程式碼(當然包括一些註釋),並且有輕微的差異,因此有必要確定一個合適的定義。在檢視不同版本後的虛擬碼定義:
# p0 is the current price and p1 is the previous price
if mode is RSI:
bulls = 0.5 * abs(p0 - p1) + p0 - p1
bears = 0.5 * abs(p0 - p1) - p0 + p1
elif mode is STOCH:
bulls = p0 - lowest(period)
bears = highest(period - p1
avgbulls = moving_average(bulls, period)
avgbears = moving_average(bears, period)
smoothedbulls = moving_average(bulls, smoothing_period) / pointsize
smoothedbears = moving_average(bears, smoothing_period) / pointsize
ash = smoothedbulls - smoothedbears
RSI
/ STOCH
命名是原始實現者選擇的,pointsize
也被稱為 pipsize
,反映了其 外匯 起源。目前尚不清楚 平滑平均值 是否必須與以前的移動平均值相同,一些版本似乎使用已經平均化的價格而不是標準價格。移動平均線是使用 整數 選擇的。
一些決定
-
該指標將使用資料提供的原始價格進行計算。如果使用者希望在平均價格上執行指標,可以傳遞它,而不是傳遞原始價格。
-
RSI
模式中的 0.5 倍乘數將作為引數 -
移動平均線不會被任何型別的 整數 選擇。在 backtrader 中,可以將實際所需的移動平均線作為引數傳遞。
-
平滑移動平均線,除非作為引數指定,否則將與指標中已使用的移動平均線相同
-
除非使用者將值指定為引數,否則將不使用
pointsize
。
這裡是實現
class ASH(bt.Indicator):
alias = ('AbsoluteStrengthOscilator',)
lines = ('ash', 'bulls', 'bears',) # output lines
# customize the plotting of the *ash* line
plotlines = dict(ash=dict(_method='bar', alpha=0.33, width=0.66))
RSI, STOCH = range(0, 2) # enum values for the parameter mode
params = dict(
period=9,
smoothing=2,
mode=RSI,
rsifactor=0.5,
movav=bt.ind.WMA, # WeightedMovingAverage
smoothav=None, # use movav if not specified
pointsize=None, # use only if specified
)
def __init__(self):
# Start calcs according to selected mode
if self.p.mode == self.RSI:
p0p1 = self.data - self.data(-1) # used twice below
half_abs_p0p1 = self.p.rsifactor * abs(p0p1) # used twice below
bulls = half_abs_p0p1 + p0p1
bears = half_abs_p0p1 - p0p1
else:
bulls = self.data - bt.ind.Lowest(self.data, period=self.p.period)
bears = bt.ind.Highest(self.data, period=self.p.period) - self.data
avbulls = self.p.movav(bulls, period=self.p.period)
avbears = self.p.movav(bears, period=self.p.period)
# choose smoothing average and smooth the already averaged values
smoothav = self.p.smoothav or self.p.movav # choose smoothav
smoothbulls = smoothav(avbulls, period=self.p.smoothing)
smoothbears = smoothav(avbears, period=self.p.smoothing)
if self.p.pointsize: # apply only if it makes sense
smoothbulls /= self.p.pointsize
smoothbears /= self.p.pointsize
# Assign the final values to the output lines
self.l.bulls = smoothbulls
self.l.bears = smoothbears
self.l.ash = smoothbulls - smoothbears
這裡展示了指標的工作方式
康納斯 RSI
原文:
www.backtrader.com/recipes/indicators/crsi/crsi/
Google 提供的此指標參考文獻:
-
Nirvana Systems - 建立“終極”指標 - 康納斯 RSI
-
TradingView - 康納斯 RSI
兩個來源對公式都表示同意,儘管術語不同(見下文)。 應按以下方式計算 Connors RSI:
CRSI(3, 2, 100) = [RSI(3) + RSI(Streak, 2) + PercentRank(100)] / 3
注意
TradingView 表示 ROC
("變動率")必須用於 PctRank
("百分比排名")的位置,但從 TradingView 自身提供的定義來看,顯然是錯誤的。
Streak
是一個非標準術語,需要定義,讓我們從源(在 TradingView 行話中稱為 "UpDown") 中參考它。
-
每日價格收盤比前一日高/低的連續天數
-
如果某日收盤價與前一日相同,則連續數重置為
0
-
向上連續產生正值,向下連續產生負值
手頭有公式,理解需要使用 PercentRank
以及對 Streak
(或 UpDown
)有清晰定義,建立 ConnorsRSI
指標應該輕而易舉。
class Streak(bt.ind.PeriodN):
'''
Keeps a counter of the current upwards/downwards/neutral streak
'''
lines = ('streak',)
params = dict(period=2) # need prev/cur days (2) for comparisons
curstreak = 0
def next(self):
d0, d1 = self.data[0], self.data[-1]
if d0 > d1:
self.l.streak[0] = self.curstreak = max(1, self.curstreak + 1)
elif d0 < d1:
self.l.streak[0] = self.curstreak = min(-1, self.curstreak - 1)
else:
self.l.streak[0] = self.curstreak = 0
class ConnorsRSI(bt.Indicator):
'''
Calculates the ConnorsRSI as:
- (RSI(per_rsi) + RSI(Streak, per_streak) + PctRank(per_rank)) / 3
'''
lines = ('crsi',)
params = dict(prsi=3, pstreak=2, prank=100)
def __init__(self):
# Calculate the components
rsi = bt.ind.RSI(self.data, period=self.p.prsi)
streak = Streak(self.data)
rsi_streak = bt.ind.RSI(streak, period=self.p.pstreak)
prank = bt.ind.PercentRank(self.data, period=self.p.prank)
# Apply the formula
self.l.crsi = (rsi + rsi_streak + prank) / 3.0
這裡展示了指標的工作原理,還包括Streak
輔助指標,以便視覺上驗證實際連續數的輸出。
Donchian 通道
原文:
www.backtrader.com/recipes/indicators/donchian/donchian/
class DonchianChannels(bt.Indicator):
'''
Params Note:
- `lookback` (default: -1)
If `-1`, the bars to consider will start 1 bar in the past and the
current high/low may break through the channel.
If `0`, the current prices will be considered for the Donchian
Channel. This means that the price will **NEVER** break through the
upper/lower channel bands.
'''
alias = ('DCH', 'DonchianChannel',)
lines = ('dcm', 'dch', 'dcl',) # dc middle, dc high, dc low
params = dict(
period=20,
lookback=-1, # consider current bar or not
)
plotinfo = dict(subplot=False) # plot along with data
plotlines = dict(
dcm=dict(ls='--'), # dashed line
dch=dict(_samecolor=True), # use same color as prev line (dcm)
dcl=dict(_samecolor=True), # use same color as prev line (dch)
)
def __init__(self):
hi, lo = self.data.high, self.data.low
if self.p.lookback: # move backwards as needed
hi, lo = hi(self.p.lookback), lo(self.p.lookback)
self.l.dch = bt.ind.Highest(hi, period=self.p.period)
self.l.dcl = bt.ind.Lowest(lo, period=self.p.period)
self.l.dcm = (self.l.dch + self.l.dcl) / 2.0 # avg of the above
資金流動指標
原文:
www.backtrader.com/recipes/indicators/mfi/mfi/
參考
school.stockcharts.com/doku.php?id=technical_indicators:money_flow_index_mfi
class MFI(bt.Indicator):
lines = ('mfi',)
params = dict(period=14)
alias = ('MoneyFlowIndicator',)
def __init__(self):
tprice = (self.data.close + self.data.low + self.data.high) / 3.0
mfraw = tprice * self.data.volume
flowpos = bt.ind.SumN(mfraw * (tprice > tprice(-1)), period=self.p.period)
flowneg = bt.ind.SumN(mfraw * (tprice < tprice(-1)), period=self.p.period)
mfiratio = bt.ind.DivByZero(flowpos, flowneg, zero=100.0)
self.l.mfi = 100.0 - 100.0 / (1.0 + mfiratio)
這是指標運作的一個視角
隨機指標(通用)
原文:
www.backtrader.com/recipes/indicators/stochastic/stochastic/
backtrader已經包含了一個Stochastic
指標(包括一種顯示三條計算線而不僅僅是通常的兩條%k
和%d
線的變體)
但這種指標假設用於計算的資料來源具有high
、low
和close
元件。這是因為原始定義使用了這些元件。
如果想要使用不同的元件,一個首要的方法可能是建立一個資料來源,將不同的元件儲存在資料來源的high
、low
和close
行中。
但一個更直接的方法是擁有一個通用Stochastic
指標,它接受三(3)個資料元件,並將它們用作如果它們是high
、low
和close
元件。
下面的程式碼執行此操作,並透過允許自定義第二次平滑的移動平均值新增了一個不錯的功能。
class Stochastic_Generic(bt.Indicator):
'''
This generic indicator doesn't assume the data feed has the components
``high``, ``low`` and ``close``. It needs three data sources passed to it,
which whill considered in that order. (following the OHLC standard naming)
'''
lines = ('k', 'd', 'dslow',)
params = dict(
pk=14,
pd=3,
pdslow=3,
movav=bt.ind.SMA,
slowav=None,
)
def __init__(self):
# Get highest from period k from 1st data
highest = bt.ind.Highest(self.data0, period=self.p.pk)
# Get lowest from period k from 2nd data
lowest = bt.ind.Lowest(self.data1, period=self.p.pk)
# Apply the formula to get raw K
kraw = 100.0 * (self.data2 - lowest) / (highest - lowest)
# The standard k in the indicator is a smoothed versin of K
self.l.k = k = self.p.movav(kraw, period=self.p.pd)
# Smooth k => d
slowav = self.p.slowav or self.p.movav # chose slowav
self.l.d = slowav(k, period=self.p.pdslow)
當然,需要驗證當給定相同的輸入集時,該指標是否確實產生與標準指標相同的結果。下面的圖表是使用這組指令建立的。
# Generate 3 data feeds
d0 = bt.ind.EMA(self.data.high, period=14)
d1 = bt.ind.EMA(self.data.low, period=14)
d2 = bt.ind.EMA(self.data.close, period=14)
Stochastic_Generic(d0, d1, d2) # customized stochastic
# These two have generate the same results
Stochastic_Generic(self.data.high, self.data.low, self.data.close)
bt.ind.Stochastic(self.data)
這裡是指標如何工作的檢視
絕對強度柱
原文:
www.backtrader.com/recipes/indicators/stochrsi/stochrsi/
Stockcharts 和 Investopedia 關於此指標有文獻。
-
StockCharts - StochRSI (ChartSchool)
-
Investopedia - Stochastic RSI - StochRSI 定義
公式如下:
StochRSI = (RSI - min(RSI, period)) / (max(RSI, period) - min(RSI, period))
理論上,用於計算RSI
的週期與隨後用於查詢RSI
的最小值和最大值的週期相同。這意味著如果選擇的週期是14
(事實上的標準)用於RSI
,則指標的總回溯期將為28
注意
實際回溯期將稍長,因為 14
期RSI
的有效回溯期為 15
,因為需要比較前兩個週期的收盤價格來啟動計算
在任何情況下,backtrader 會自動計算所有必需的回溯期和預熱期。
考慮到以下是 backtrader 中的內建指標:
-
RSI
-
Lowest
(又稱MaxN
) -
Highest
(又稱MinN
)
根據上述公式開發StochRSI
是直接的。
class StochRSI(bt.Indicator):
lines = ('stochrsi',)
params = dict(
period=14, # to apply to RSI
pperiod=None, # if passed apply to HighestN/LowestN, else "period"
)
def __init__(self):
rsi = bt.ind.RSI(self.data, period=self.p.period)
pperiod = self.p.pperiod or self.p.period
maxrsi = bt.ind.Highest(rsi, period=pperiod)
minrsi = bt.ind.Lowest(rsi, period=pperiod)
self.l.stochrsi = (rsi - minrsi) / (maxrsi - minrsi)
這裡是指標工作原理的一瞥
商店/經紀人/資料來源
介紹
原文:
www.backtrader.com/recipes/storesbrokersdata/intro/
這一部分託管了一系列第三方儲存(或單獨的經紀人 / 資料來源)的實現,它們不是backtrader核心的一部分。
bt-ccxt-store
原文:
www.backtrader.com/recipes/storesbrokersdata/bt-ccxt-store/bt-ccxt-store/
bt-ccxt-store
作者 Dave Vallance
倉庫連結:
github.com/Dave-Vallance/bt-ccxt-store
Metaquotes MQL 5 - API
原文:
www.backtrader.com/recipes/storesbrokersdata/mql5/mql5/
首次由使用者 Nikolai
在這裡宣佈。
- Backtrader 社群 - Metaquotes MQL 5 - API
倉庫連結:
-
github.com/khramkov/MQL5-JSON-API
-
github.com/khramkov/Backtrader-MQL5-API
NorgateData
原文:
www.backtrader.com/recipes/storesbrokersdata/norgatedata/norgatedata/
包的連結:
pypi.org/project/norgatedata/
Oanda v20
原文:
www.backtrader.com/recipes/storesbrokersdata/oandav20/oandav20/
由使用者 ftomassetti
倉庫連結:
github.com/ftomassetti/backtrader-oandav20
TradingView
原文:
www.backtrader.com/recipes/storesbrokersdata/tradingview/tradingview/
使用者為 Dave-Vallance
倉庫連結:
github.com/Dave-Vallance/tv2bt