# 輕鬆學會程式交易 | Chapter 5 | 建立自己的交易系統 [TOC] # 5-4 Ta-lib ## 下載步驟 請依序嘗試以下三種方法 1. windows或Mac作業系統可直接在Ipython Console中下載 ``` pip install TA-Lib ``` 2. Mac可以使用brew下載,不過需注意要加上`!` ``` !brew install ta-lib ``` 3. windows還可以直接使用wheel檔下載: (a) 請點[網址](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib) (b) 找尋適合的版本的檔案 ex: TA_Lib‑0.4.17‑cp37‑cp37m‑win_amd64.whl (cp右邊的是python版本,‑win_amd右邊是windows位元) ``` 如何看python版本 1.spyder視窗上方 2.IPython console顯示 ``` ``` 如何看windows位元 1.點選左下角搜尋 2.搜尋'系統資訊' 3.一堆項目中找到系統類型 ``` ## 測試 ```python= import talib import pandas as pd tech = pd.Series([1,2,3,4,5,6,7,8]) print(talib.MA(tech,5)) ``` ## github、文檔 (1)英文github https://github.com/mrjbq7/ta-lib 文檔 https://mrjbq7.github.io/ta-lib/doc_index.html (2)中文翻譯github https://github.com/HuaRongSAO/talib-document # 5-5 技術指標 ## 呼叫套件 ```python= import talib import pandas_datareader as pdr import numpy as np ``` ## 取得數據 ```python= data = pdr.DataReader('0050.TW', 'yahoo') # 從yahoo數據庫得到數據 close = data['Close'] # 股票收盤價 # 印出來看看! ma5 = talib.MA(close,5) # 5日均線 print(ma5) ``` ## 缺失值處理 :warning: talib遇到缺失值,後面算出來得都會是缺失值 先做實驗,將倒數第10個值改成缺失值,並填補空缺 > 補充 numpy的`np.nan`在pandas中視為`None`(空)值。 不過使用`bool()函數`轉型,python視為`True` > ```python= # 將倒數第10個值改成缺失值 close.iloc[-10] = np.nan # np.nan類似None # 查看是不是變成缺失值了 print(close.tail(11)) # tail可以看尾端的數據 ma5 = talib.MA(close,5) print(ma5.tail(11)) close = close.ffill() # 將缺失值變成前一個數 data = data.ffill() # 或者將每一欄的缺失值變成前一個數 # 查看是不是變成缺失值了 print(close.tail(11)) ma5 = talib.MA(close,5).tail(11) print(ma5) ``` ## RSI `RSI`是經典的轉折指標。傳統將`RSI>70`視為行情過熱,`RSI<30`視為行情過冷。 ```python= rsi = talib.RSI(close,14) #印出來看看! print(rsi) ``` ## KD `KD`需要用到高低收三種資料,詳細KD原理可以參考[文章](https://www.cmoney.tw/notes/note-detail.aspx?nid=6460) 傳統方法是將`K`黃金交叉(向上交叉)`D`,視為買進訊號,反之為賣出訊號。 ```python= high = data['High'] low = data['Low'] close = data['Close'] slowk, slowd = talib.STOCH(high, low, close, slowk_period=5, slowd_period=5, fastk_period=9) #印出來看看! print(slowk) print(slowd) ``` # 5-6 繪製技術指標圖 ## matplotlib > `matplotlib`是一個簡單好用的python視覺化繪圖庫。 後面的範例我們會直接使用pandas搭配其中的功能 ### 安裝 ```python pip install matplotlib ``` ### qt5 試著在IPyton console 輸入 %matplotlib qt5, 可以切換圖形介面 而用完之可以輸入 %matplotlib inline 換回原本圖形介面 ## MA 一次執行兩行,輕鬆繪出美美的圖 ```pyhton= close.plot() ma5.plot() ``` ![](https://i.imgur.com/fbqt5xl.png) ## RSI ### 畫子圖方法一 使用pandas DataFrame的`plot`功能繪圖,可輕鬆達到效果 ```python= ploter = pd.DataFrame({'Close':close,'RSI':rsi}) ploter.plot(subplots=True) # subplots 功能可以將內容分開成不同圖片(子圖) ``` ![](https://i.imgur.com/ZEHnKiZ.png) 加上水平線 ```python=+ ax = ploter.plot(subplots=True) print(ax) ax[1].axhline(70) ax[1].axhline(30) ``` ![](https://i.imgur.com/cFLuLRD.png) ### KD ### 畫子圖方法二 較正統,也比較靈活 ```python= # 製作兩個畫板 fig, ax = plt.subplots(2, 1) # 2*1的子圖 # -----畫第一張圖---- # close.plot(ax=ax[0], sharex=True) # 加上標題與圖例 # close.plot(title='Close',legend=True,ax=ax[0]) # -----畫第二張圖----- # # k,d要先fillna(將缺失值填補)x軸才不會錯呦! slowk.fillna(0).plot(ax=ax[1], sharex=True) slowd.fillna(0).plot(ax=ax[1], sharex=True) # 加上標題與圖例 # slowk.fillna(0).plot(sharex=True,title='KD',legend=True,ax=ax[1]) # slowd.fillna(0).plot(sharex=True,legend=True,ax=ax[1]) ax[1].axhline(30) # 30水平線 ax[1].axhline(70) # 70水平線 plt.tight_layout() # 加上這個可以調整成舒適的格式 ``` ![](https://i.imgur.com/aN3eBpl.png) # 5-7 建立條件(condition) 撰寫策略時,策略的邏輯核心就是`條件`。 條件最終都會以`True`或`False`的方式呈現。 由於這些條件是可以重複拿出來被使用的,所以我們可以用DataFrame整理寫過的條件。 ## 比較法 將數值互相比大小,即為比較法。或者達到 ![](https://i.imgur.com/LiNPZiN.png) ## 設定條件庫的步驟 1. 建立空的DataFrame名叫“condition” 2. 取名並設定條件 ![](https://i.imgur.com/nyQ2IyX.png) 其中命名以簡單清楚為主,也可以使用參數作為命名以利最佳化 # 5-8 建立條件 程式碼,以黃金交叉為例 ```python= condition = pd.DataFrame([]) # 將數值放進condition中並命名 condition['股價>均線5'] = close > ma5 condition['標準差>1.5'] = close.rolling(10).std() > 1.5 # 黃金交叉做法 a1 = k b1 = d a2 = a1.shift(1) b2 = b1.shift(1) crossover = (a1>a2) & (a1>b1) & (b2>a2) condition['KD黃金交叉'] = crossover ``` 以下為黃金交叉及死亡交叉函數,可直接取用 ```python def crossover(over,down): a1 = over b1 = down a2 = a1.shift(1) b2 = b1.shift(1) crossover = (a1>a2) & (a1>b1) & (b2>a2) return crossover # 死亡交叉 def crossunder(down,over): a1 = down b1 = over a2 = a1.shift(1) b2 = b1.shift(1) crossdown = (a1<a2) & (a1<b1) & (b2<a2) return crossdown ``` # 5-9 為什麼要建立「訊號」 > 信號是將各種條件濃縮成進場和出場的唯一條件 ## 交易方法種類 - 進場 1. 買進(buy) 2. 放空(sellshort) - 出場 1. 賣出(sell) 2. 回補(buytocover) # 5-10 建立進出場訊號 ## 建立`條件` ```pyhton= condition = pd.DataFrame([]) condition['KD黃金交叉'] = crossover(k,d) condition['KD死亡交叉'] = crossunder(k,d) ``` ## 生成訊號 建立一個空的訊號DataFrame表,存放買進及賣出訊號 ```python= signal = pd.DataFrame([]) # 各項條件用'&'方式處理 # '*1'是為了轉成數字訊號方便大家看 signal['買進'] = (condition['KD黃金交叉'] & condition['D值低檔']) * 1 signal['賣出'] = condition['KD死亡交叉'] * -1 ``` ### 信號處理 交易策略:買進一單位,並續抱到直到賣出訊號出現為止 這邊的操作是讓買進與賣出訊號變成一進一出(pair)的訊號 此操作可有可無,供學員大家參考 ```pyhton= signal['持有'] = signal['買進'] + signal['賣出'] signal['持有'] = signal['持有'].replace(0,method='ffill') signal['持有'] = signal['持有'].replace(-1, 0) def tool(x): if x[0]==1 and x[1]==0: return 1 else: return x[-1] signal['持有'] = signal['持有'].rolling(2).apply(tool, raw=True).fillna(0) ``` # 5-12 買賣權益生成 ## 回測+記錄的資訊 建立一個總資料表`research`將所需資訊放在一起 ```python= research = pd.DataFrame([]) research = research.join([data['Open']],how='outer') # 開盤價資訊 research = research.join([close],how='outer') # 收盤價資訊 research = research.join([signal],how='outer') # 訊號 research['權益'] = np.nan # 拿來紀錄權益用的欄位 ``` ## 回測變數 ```python=+ # 計入初始資金 capital = 100000 # 記錄權益(目前現金+部位價值) equity = capital # 記錄部位狀況 marketposition=0 # 記錄買入股數 share=0 # 記錄入場價得 entryprice = 0 """ research = research.to_dict('record') 使用這行會加快速度很多。 原理是把dataframe轉成成list+字典形式, 不過在下面for要改成: for values in research: """ ``` ## 策略邏輯撰寫 ```python=+ for index,values in research.iterrows(): '''step3,賣出''' if marketposition == 1 and values['賣出'] == -1: # sell this bar at close # 賣在什麼價格 exitprice = values['Close'] # 帳戶處理:收款 capital += exitprice * share * 1000 # share就是上一次買進的股數 marketposition=0 share = 0 '''step1,買進''' if marketposition == 0 and values['買進'] == 1: #buy this bar at close #買了多少股 share = capital // (values['Close']*1000) #買在什麼價格 entryprice = values['Close'] #帳戶處理:扣款 capital -= entryprice * share * 1000 # 將狀態改為"買進" marketposition=1 '''step2,記錄權益狀況''' if marketposition == 1: #權益就是假設直接賣出可以獲得多少錢(目前現金+部位價值) equity = capital + (share * values['Close'] * 1000) else: equity = capital # 在research中記錄權益 research.loc[index,'權益'] = equity ``` ## 繪圖 ```python=+ # 將訊號產生的地方塗上顏色 hold = close.copy() hold[signal['持有']==0] = np.nan fig, ax = plt.subplots(4, 1)# 4*1的子圖 ax[0].set_title('Equity') # 將0號圖標上標題 research['權益'].plot(ax=ax[0]) # 繪出權益 ax[1].set_title('Close') # 將1號圖標上標題 close.plot(ax=ax[1]) # 繪出股價 hold.plot(ax=ax[1]) # 將買進的區域塗上顏色 #k,d要先fillna(將缺失值填補)x軸才不會錯呦! ax[2].set_title('KD') # 將2號圖標上標題 k.fillna(0).plot(sharex=True,ax=ax[2]) # 繪出K值 d.fillna(0).plot(sharex=True,ax=ax[2]) # 繪出D值 ax[3].set_title('Signal') # 將3號圖標上標題 signal['持有'].plot(sharex=True,ax=ax[3]) # 繪出訊號 plt.tight_layout() # 將畫板調成適合的大小 ``` # 5-13 第三次作業 ## 制定一個策略,與大盤比較績效,並產生權益圖 請說明比大盤績效好或壞的原因 試說明,有沒有讓績效更好的做法