# 輕鬆學會程式交易 | 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 第三次作業
## 制定一個策略,與大盤比較績效,並產生權益圖
請說明比大盤績效好或壞的原因
試說明,有沒有讓績效更好的做法