您好,登錄后才能下訂單哦!
如何利用平均趨向指數輔助MACD策略,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
前言
“趨勢是你的朋友”這是每一個交易者都耳熟能詳的箴言。但做過交易的朋友可能會有體會,趨勢總是在毫無預警地開始并突然結束。那么在CTA策略中,如何抓住趨勢并過濾震蕩行情,是許多主觀和量化交易者孜孜不倦的追求。在本節課程中,我們將以平均趨向指數(ADX)為濾網,分析在它量化交易中的應用。
什么是平均趨向指數
平均趨向指數是衡量趨勢的技術工具,簡稱ADX(average directional indicator),它是由韋爾斯·懷爾德在1978年提出,與其他技術分析工具不同的是,ADX并不能判斷多空方向,更不能提示精確的買賣點位,它只是衡量當前趨勢的強弱。
ADX的默認周期參數是14,通常在K線圖的副圖中顯示。它的值是在0~100之間,數值越大說明上漲或下跌趨勢越強力,通常當ADX的值大于40時,說明趨勢強力,此時使用趨勢交易才具有最大的回報潛力;當ADX的值小于20時,說明趨勢疲軟,并警告交易者不要使用趨勢跟蹤交易策略。
ADX的計算方式
ADX的計算方式比較復雜,它涉及到了:價格正向移動距離(+DM)、價格負向移動距離(-DM)、真是波動幅度(TR)、正向方向性指數(+DI), 負向方向性指數(-DI)等很多中間變量:
計算動向變化
up:今天的最高價 – 昨天的最高價
down:昨天的最低價 – 今天的最低價
+DM:如果up大于max(down, 0),則+DM等于up,否則等于零
-DM:如果down大于max(up, 0),則-DM等于down,否則等于零
計算真實波幅
TR:max(今天最高價與今天最低價的差值,今天最高價與昨天收盤價差值的絕對值,今天最低價與昨天收盤價差值的絕對值)
計算動向指數
+DI(14):+DM(14)/TR(14)*100
-DI(14):-DM(14)/TR(14)*100
計算ADX
DX:((+DI14)- (-DI14)/(+DI14)+(-DI14))*100
ADX:MA(DX,14)
雖然ADX的計算比較復雜,但其邏輯還是比較清晰的:up和down分別代表了價格正向和負向移動距離;+DI和-DI分別代表用波動率修正后上漲和下跌趨勢。不管趨勢是上漲還是下跌,只要存在明顯的趨勢行情,那么+DI和-DI中總有一個是較大的,因此DX的值會隨著趨勢的強弱指示在0~100之間;最后ADX則是DX的14天平均線。
當+ DI高于-DI時,表明價格處于上升趨勢。 相反,當-DI高于+ DI時,價格處于下降趨勢。 交易者可以通過檢查同一時間點的ADX值來確定上升趨勢或下降趨勢的強度。
策略邏輯
在前幾節中,我們使用MACD指標創建了一個簡單的策略,雖然該策略在趨勢行情中表現還可以,但是在震蕩行情入不敷出,甚至在長期的震蕩行情中資金回撤比較大。因此我們將在本節中將之前的MACD策略加入ADX濾網,我們來看下效果到底如何?
原策略邏輯
多頭開倉:DIF大于零軸
空頭開倉:DIF小于零軸
多頭平倉:DIF向下突破DEA
空頭平倉:DIF向上突破DEA
改進后的策略邏輯
多頭開倉:DIF大于零軸,并且ADX大于20
空頭開倉:DIF小于零軸,并且ADX大于20
多頭平倉:DIF向下突破DEA,或者ADX下降
空頭平倉:DIF向上突破DEA,或者ADX下降
我們在原策略邏輯基礎之上,對開倉和平倉分別加入ADX濾網,控制在行情進入震蕩時期的開倉次數。在開倉的時候ADX的數值必須大于指定的數值;當開倉之后一旦ADX下降就平倉出局。整個策略邏輯就設計成一個嚴進寬出的模式,以此來控制震蕩時期的回撤幅度。
策略編寫
原始策略
# 回測配置 '''backtest start: 2015-02-22 00:00:00 end: 2019-10-17 00:00:00 period: 1h exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] ''' mp = 0 # 定義一個全局變量,用于控制虛擬持倉 # 判斷數組是否上升 def is_up(arr): arr_len = len(arr) if arr[arr_len - 1] > arr[arr_len - 2] and arr[arr_len - 2] > arr[arr_len - 3]: return True # 判斷數組是否下降 def is_down(arr): arr_len = len(arr) if arr[arr_len - 1] < arr[arr_len - 2] and arr[arr_len - 2] < arr[arr_len - 3]: return True # 判斷兩根兩個數組是否金叉 def is_up_cross(arr1, arr2): if arr1[len(arr1) - 2] < arr2[len(arr2) - 2] and arr1[len(arr1) - 1] > arr2[len(arr2) - 1]: return True # 判斷兩根兩個數組是否死叉 def is_down_cross(arr1, arr2): if arr1[len(arr1) - 2] > arr2[len(arr2) - 2] and arr1[len(arr1) - 1] < arr2[len(arr2) - 1]: return True # 程序主函數 def onTick(): exchange.SetContractType("rb000") # 訂閱期貨品種 bar_arr = exchange.GetRecords() # 獲取K線數組 if len(bar_arr) < long + m + 1: # 如果K線數組長度太小,就不能計算MACD,所以直接返回跳過 return all_macd = TA.MACD(bar_arr, short, long, m) # 計算MACD值,返回的是一個二維數組 dif = all_macd[0] # 獲取DIF的值,返回一個數組 dif.pop() # 刪除DIF數組最后一個元素 dea = all_macd[1] # 獲取DEA的值,返回一個數組 dea.pop() # 刪除DEA數組最后一個元素 last_close = bar_arr[len(bar_arr) - 1]['Close'] # 獲取最新價格(賣價),用于開平倉 global mp # 全局變量,用于控制虛擬持倉 # 開多單 if mp == 0 and dif[len(dif) - 1] > 0: exchange.SetDirection("buy") # 設置交易方向和類型 exchange.Buy(last_close, 1) # 開多單 mp = 1 # 設置虛擬持倉的值,即有多單 # 開空單 if mp == 0 and dif[len(dif) - 1] < 0: exchange.SetDirection("sell") # 設置交易方向和類型 exchange.Sell(last_close - 1, 1) # 開空單 mp = -1 # 設置虛擬持倉的值,即有空單 # 平多單 if mp == 1 and is_down_cross(dif, dea): exchange.SetDirection("closebuy") # 設置交易方向和類型 exchange.Sell(last_close - 1, 1) # 平多單 mp = 0 # 設置虛擬持倉的值,即空倉 # 平空單 if mp == -1 and is_up_cross(dif, dea): exchange.SetDirection("closesell") # 設置交易方向和類型 exchange.Buy(last_close, 1) # 平空單 mp = 0 # 設置虛擬持倉的值,即空倉 def main(): while True: onTick() Sleep(1000)
根據上面更改的策略邏輯,我們可以直接在原始策略的基礎之上把ADX濾網加入進去,雖然ADX的計算方法比較復雜,但可以借助talib庫只需要幾行代碼就可以把ADX的值計算出來。因為計算ADX需要用帶talib,而計算talib庫又需要用到numpy.array數據類型,所以我們需要在代碼開頭導入talib庫和numpy庫。
import talib import numpy as np
在使用talib庫計算ADX的時候,一共需要4個參數:最高價、最低價、收盤價、周期參數。所以我們還需要寫一個get_data函數,這個函數的目的是從K線數組中提取出最高價、最低價、收盤價。
# 把K線數組轉換成最高價、最低價、收盤價數組,用于轉換為numpy.array類型數據 def get_data(bars): arr = [[], [], []] for i in bars: arr[0].append(i['High']) arr[1].append(i['Low']) arr[2].append(i['Close']) return arr
然后我們使用numpy庫把普通的數組轉換為numpy.array類型數據,最后使用talib庫就可以計算出ADX的值,具體的寫法可以看下面代碼中的注釋:
np_arr = np.array(get_data(bar_arr)) # 把列表轉換為numpy.array類型數據,用于計算ADX的值 adx = talib.ADX(np_arr[0], np_arr[1], np_arr[2], 20); # 計算ADX的值
在策略邏輯中,需要判斷ADX的大小和是否上升下降。判斷大小很簡單,只需要把ADX具體某一天的值提取出來就可以了,跟判斷MACD一樣,我們只取倒數第二根K線的ADX值;但判斷時候上升下降則需要只取倒數第二根和第三根K線的ADX值。
adx1 = adx_arr[len(adx_arr) - 2] # 倒數第二根K線的ADX值 adx2 = adx_arr[len(adx_arr) - 3] # 倒數第三根K線的ADX值
最后修改下單邏輯:
# 開多單 if mp == 0 and dif[len(dif) - 1] > 0 and adx1 > 40: exchange.SetDirection("buy") # 設置交易方向和類型 exchange.Buy(last_close, 1) # 開多單 mp = 1 # 設置虛擬持倉的值,即有多單 # 開空單 if mp == 0 and dif[len(dif) - 1] < 0 and adx1 > 40: exchange.SetDirection("sell") # 設置交易方向和類型 exchange.Sell(last_close - 1, 1) # 開空單 mp = -1 # 設置虛擬持倉的值,即有空單 # 平多單 if mp == 1 and (is_down_cross(dif, dea) or adx1 < adx2): exchange.SetDirection("closebuy") # 設置交易方向和類型 exchange.Sell(last_close - 1, 1) # 平多單 mp = 0 # 設置虛擬持倉的值,即空倉 # 平空單 if mp == -1 and (is_up_cross(dif, dea) or adx1 < adx2): exchange.SetDirection("closesell") # 設置交易方向和類型 exchange.Buy(last_close, 1) # 平空單 mp = 0 # 設置虛擬持倉的值,即空倉
完整策略代碼
# 回測配置 '''backtest start: 2015-02-22 00:00:00 end: 2019-10-17 00:00:00 period: 1h exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] ''' # 導入庫 import talib import numpy as np mp = 0 # 定義一個全局變量,用于控制虛擬持倉 # 把K線數組轉換成最高價、最低價、收盤價數組,用于轉換為numpy.array類型數據 def get_data(bars): arr = [[], [], []] for i in bars: arr[0].append(i['High']) arr[1].append(i['Low']) arr[2].append(i['Close']) return arr # 判斷兩根兩個數組是否金叉 def is_up_cross(arr1, arr2): if arr1[len(arr1) - 2] < arr2[len(arr2) - 2] and arr1[len(arr1) - 1] > arr2[len(arr2) - 1]: return True # 判斷兩根兩個數組是否死叉 def is_down_cross(arr1, arr2): if arr1[len(arr1) - 2] > arr2[len(arr2) - 2] and arr1[len(arr1) - 1] < arr2[len(arr2) - 1]: return True # 程序主函數 def onTick(): exchange.SetContractType("rb000") # 訂閱期貨品種 bar_arr = exchange.GetRecords() # 獲取K線數組 if len(bar_arr) < long + m + 1: # 如果K線數組長度太小,就不能計算MACD,所以直接返回跳過 return all_macd = TA.MACD(bar_arr, short, long, m) # 計算MACD值,返回的是一個二維數組 dif = all_macd[0] # 獲取DIF的值,返回一個數組 dif.pop() # 刪除DIF數組最后一個元素 dea = all_macd[1] # 獲取DEA的值,返回一個數組 dea.pop() # 刪除DEA數組最后一個元素 np_arr = np.array(get_data(bar_arr)) # 把列表轉換為numpy.array類型數據,用于計算ADX的值 adx_arr = talib.ADX(np_arr[0], np_arr[1], np_arr[2], 20); # 計算ADX的值 adx1 = adx_arr[len(adx_arr) - 2] # 倒數第二根K線的ADX值 adx2 = adx_arr[len(adx_arr) - 3] # 倒數第三根K線的ADX值 last_close = bar_arr[len(bar_arr) - 1]['Close'] # 獲取最新價格(賣價),用于開平倉 global mp # 全局變量,用于控制虛擬持倉 # 開多單 if mp == 0 and dif[len(dif) - 1] > 0 and adx1 > 40: exchange.SetDirection("buy") # 設置交易方向和類型 exchange.Buy(last_close, 1) # 開多單 mp = 1 # 設置虛擬持倉的值,即有多單 # 開空單 if mp == 0 and dif[len(dif) - 1] < 0 and adx1 > 40: exchange.SetDirection("sell") # 設置交易方向和類型 exchange.Sell(last_close - 1, 1) # 開空單 mp = -1 # 設置虛擬持倉的值,即有空單 # 平多單 if mp == 1 and (is_down_cross(dif, dea) or adx1 < adx2): exchange.SetDirection("closebuy") # 設置交易方向和類型 exchange.Sell(last_close - 1, 1) # 平多單 mp = 0 # 設置虛擬持倉的值,即空倉 # 平空單 if mp == -1 and (is_up_cross(dif, dea) or adx1 < adx2): exchange.SetDirection("closesell") # 設置交易方向和類型 exchange.Buy(last_close, 1) # 平空單 mp = 0 # 設置虛擬持倉的值,即空倉 def main(): while True: onTick() Sleep(1000)
以上貼出本節已經改進過的策略完整源代碼,或者你也可以到 https://www.fmz.com/strategy/174672 發明者量化官網的策略中復制下載,無需配置直接在線回測。
看完上述內容,你們掌握如何利用平均趨向指數輔助MACD策略的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。