使用TA-Lib在vn.trader上開發CTA交易策略
使用TA-Lib在vn.trader上開發CTA交易策略
TA-Lib簡介
作為一套被業界廣泛應用的開源技術分析庫(包含技術指標計算和K線模式識別等),TA-Lib自2001年發布以來已經有了十多年的歷史。TA-Lib中一共包含大約125個技術指標的計算函數,同時提供了包括C/C++、Java、Perl、Python等多種語言的API。
有什麼用
簡單來說TA-Lib就是提供了一堆經過長期實踐檢驗的技術指標計算函數。基於現成的計算函數,開發新策略雛形、快速驗證某個靈感的時間可以大幅縮短,否則想像一下每開發個策略都要自己實現要用的技術指標,未免太浪費時間。
但是除此以外,TA-Lib還可以有一些其他的用法,舉兩個例子。
百科全書
堅持每天收盤後選一個自己沒用過的指標,輸入數據,畫個圖、跑個回測,開發量化策略很很多其他的技術一樣都是熟能生巧。另外,所有的技術指標在被開發出來的時候,背後都有一定的金融邏輯原理(行為金融學)的支撐,生搬硬套固然不可取,但是放著前人經驗完全不看,整天憑自己的空想就弄個機器學習演算法在數據上瞎折騰豈不是更浪費時間?
Alpha庫
很大一部分CTA類的策略可以總結為幾個簡單的邏輯框架,比如趨勢策略通常可以分解成以下部分:趨勢信號(通常是基於某幾個參數計算出來的指標值超過某個閾值)、信號過濾(和趨勢信號類似)、出場方案(固定點數/百分比的止盈和止損,移動止損)。因此把邏輯框架的代碼搭好後,就可以通過機器學習演算法來實現一種自動的策略開發方式:
從TA-Lib中選取兩個指標分別作為趨勢信號和信號過濾,結合止損、止盈方案,生成一個策略;
基於某一組歷史數據(如股指的1分鐘行情),通過遺傳演算法來對以上的參數進行光與優化;
兩個指標的參數加起來通常不會超過10個,再加上止盈、止損、移動止損的參數,總參數不會超過15個,在一組高達十幾萬個數據點的時間序列上進行回測,過度擬合的可能性不大;
現在雲伺服器價格也不貴,租一個核多一點的,把演算法和數據丟上去7×24小時的跑,Alpha值達到一定標準的策略存下來;
把上一步中保存下來的策略作為雛形,研究員再來進行針對性的有效性驗證和更精細化的策略改進,把策略開發變成有的放矢,而不是盲人摸象。
這種策略開發方式使用傳統的商業軟體(如TB、MC等)幾乎不可能實現,而Python這類開源軟體就成為了最好的選擇,用戶可以自行決定幾乎所有的演算法(指標如何選擇、遺傳演算法優化參數時如何迭代等等)。
怎麼安裝
儘管TA-Lib原生提供了基於SWIG封裝的Python API,但是由於性能和編譯不方便的原因,作者推薦Github上的一位開發者mrjbq7基於Cython封裝的版本。
安裝過程:
在這裡下載TA_Lib-0.4.9-cp27-none-win32.whl放到桌面上,也就是vn.py建議的運行環境Anaconda 2.7 32位
在桌面上按住Shift點擊滑鼠右鍵後,選擇在此處打開命令窗口打開cmd
安裝wheel包,在cmd中運行:
pip install wheel
安裝TA-Lib,在cmd中運行:
pip install TA_Lib-0.4.9-cp27-none-win32.whl
打開Python,運行:
import talib
沒有報錯則說明安裝成功
一定要嘗試自己編譯的用戶,可以根據該項目的網站上的教程來安裝。作者的兩台電腦,一台直接安裝成功,另一台安裝了MinGW的電腦則報GCC編譯錯誤(其實自己編譯沒有任何意義,感謝加州大學歐文分校打包的whl文件,省去了很多麻煩)。
Linux下的安裝建議使用Anaconda的conda工具:
conda install -c quantopian ta-lib=0.4.9
具體可以參考這裡:https://anaconda.org/Quantopian/ta-lib
DEMO
下面這個策略DEMO可以直接在vn.trader的CTA模塊中使用(回測、模擬交易),請不要用於實盤!
# encoding: UTF-8nnimport talib as tanimport numpy as npnnfrom ctaBase import *nfrom ctaTemplate import CtaTemplatennn########################################################################nclass TalibDoubleSmaDemo(CtaTemplate):n """基於Talib模塊的雙指數均線策略Demo"""nn className = TalibDoubleSmaDemon author = uideaplatnn # 策略參數n fastPeriod = 5 # 快速均線參數n slowPeriod = 20 # 慢速均線參數n initDays = 5 # 初始化數據所用的天數nn # 策略變數n bar = Nonen barMinute = EMPTY_STRINGnn closeHistory = [] # 緩存K線收盤價的數組n maxHistory = 50 # 最大緩存數量nn fastMa0 = EMPTY_FLOAT # 當前最新的快速均線數值n fastMa1 = EMPTY_FLOAT # 上一根的快速均線數值nn slowMa0 = EMPTY_FLOAT # 慢速均線數值n slowMa1 = EMPTY_FLOATnnn # 參數列表,保存了參數的名稱n paramList = [name,n className,n author,n vtSymbol,n fastPeriod,n slowPeriod]nn # 變數列表,保存了變數的名稱n varList = [inited,n trading,n pos,n fastMa0,n fastMa1,n slowMa0,n slowMa1] nn # ----------------------------------------------------------------------n def __init__(self, ctaEngine, setting):n """Constructor"""n super(TalibDoubleSmaDemo, self).__init__(ctaEngine, setting)nn # ----------------------------------------------------------------------n def onInit(self):n """初始化策略(必須由用戶繼承實現)"""n self.writeCtaLog(u雙SMA演示策略初始化)nn initData = self.loadBar(self.initDays)n for bar in initData:n self.onBar(bar)nn self.putEvent()nn # ----------------------------------------------------------------------n def onStart(self):n """啟動策略(必須由用戶繼承實現)"""n self.writeCtaLog(u雙SMA演示策略啟動)n self.putEvent()nn # ----------------------------------------------------------------------n def onStop(self):n """停止策略(必須由用戶繼承實現)"""n self.writeCtaLog(u雙SMA演示策略停止)n self.putEvent()nn # ----------------------------------------------------------------------n def onTick(self, tick):n """收到行情TICK推送(必須由用戶繼承實現)"""n # 計算K線n tickMinute = tick.datetime.minutenn if tickMinute != self.barMinute:n if self.bar:n self.onBar(self.bar)nn bar = CtaBarData()n bar.vtSymbol = tick.vtSymboln bar.symbol = tick.symboln bar.exchange = tick.exchangenn bar.open = tick.lastPricen bar.high = tick.lastPricen bar.low = tick.lastPricen bar.close = tick.lastPricenn bar.date = tick.daten bar.time = tick.timen bar.datetime = tick.datetime # K線的時間設為第一個Tick的時間nn # 實盤中用不到的數據可以選擇不算,從而加快速度n # bar.volume = tick.volumen # bar.openInterest = tick.openInterestnn self.bar = bar # 這種寫法為了減少一層訪問,加快速度n self.barMinute = tickMinute # 更新當前的分鐘nn else: # 否則繼續累加新的K線n bar = self.bar # 寫法同樣為了加快速度nn bar.high = max(bar.high, tick.lastPrice)n bar.low = min(bar.low, tick.lastPrice)n bar.close = tick.lastPricenn # ----------------------------------------------------------------------n def onBar(self, bar):n """收到Bar推送(必須由用戶繼承實現)"""n # 把最新的收盤價緩存到列表中n self.closeHistory.append(bar.close)nn # 檢查列表長度,如果超過緩存上限則移除最老的數據n # 這樣是為了減少計算用的數據量,提高速度n if len(self.closeHistory) > self.maxHistory:n self.closeHistory.pop(0)n # 如果小於緩存上限,則說明初始化數據尚未足夠,不進行後續計算n else:n returnnn # 將緩存的收盤價數轉化為numpy數組後,傳入talib的函數SMA中計算n closeArray = np.array(self.closeHistory)n fastSMA = ta.SMA(closeArray, self.fastPeriod)n slowSMA = ta.SMA(closeArray, self.slowPeriod)nn # 讀取當前K線和上一根K線的數值,用於判斷均線交叉n self.fastMa0 = fastSMA[-1]n self.fastMa1 = fastSMA[-2]n self.slowMa0 = slowSMA[-1]n self.slowMa1 = slowSMA[-2]nn # 判斷買賣n crossOver = self.fastMa0>self.slowMa0 and self.fastMa1<self.slowMa1 # 金叉上穿n crossBelow = self.fastMa0<self.slowMa0 and self.fastMa1>self.slowMa1 # 死叉下穿n n # 金叉和死叉的條件是互斥n if crossOver:n # 如果金叉時手頭沒有持倉,則直接做多n if self.pos == 0:n self.buy(bar.close, 1)n # 如果有空頭持倉,則先平空,再做多n elif self.pos < 0:n self.cover(bar.close, 1)n self.buy(bar.close, 1)n # 死叉和金叉相反n elif crossBelow:n if self.pos == 0:n self.short(bar.close, 1)n elif self.pos > 0:n self.sell(bar.close, 1)n self.short(bar.close, 1)nn # 發出狀態更新事件n self.putEvent()nn # ----------------------------------------------------------------------n def onOrder(self, order):n """收到委託變化推送(必須由用戶繼承實現)"""n # 對於無需做細粒度委託控制的策略,可以忽略onOrdern passnn # ----------------------------------------------------------------------n def onTrade(self, trade):n """收到成交推送(必須由用戶繼承實現)"""n # 對於無需做細粒度委託控制的策略,可以忽略onOrdern passn
(感謝社區ideaplat用戶貢獻的代碼!作者做了一些小修改。)
將上面的代碼保存到一個talibDemo.py文件中後,參考vn.trader下ctaAlgo文件夾內的ctaBacktesting.py運行回測,也可以通過ctaSetting.py進行配置後,在vn.trader中進行模擬交易。
從上面的DEMO中我們可以看到,talib中的技術指標函數主要接受一個numpy數組作為原始數據及若干個指標演算法中的參數作為輸入,返回的數據也是一個numpy數組,使用起來非常方便。
注意緩存數據時使用的是Python列表而非numpy數組,主要原因是numpy數組的append方法本質是結合原數組中的數據和新的數據生成一個新的數組對象,相對於列表的append開銷要高很多。
推薦閱讀:
※工作四年要不要辭職去讀一個全日制 MBA?
※國際上權威的金融類期刊有那些?
※淺析汽車4S店金融專員的基本素養 | 金融高管會