第二堂課程最近也終於走向穩定了,剛開始有一些的bug、RAM的優化上的問題,還請同學們多多擔待,我們也會持許推出新的介紹,讓大家有更多可以加強機器學習的方式,這一篇文章中,我們要針對以往的labeling方式進行優化,讓訓練出來的模型,可以有更準確的預測。
沒有參加課程的同學,也可以跟我們一起學習,下方的程式碼都是完全公開的!請大家自行拿取玩玩看喔!
機器學習 「features 和 labels」
假如您不知道Labeling是什麼,那我們這邊還是先簡單的介紹一下supervise機器學習(監督式機器學習):
這一類的機器學習模型,可以想像是一個函式 function f,我們想要計算 y = f(x)
,其中 x 就是 features,另外 y 就是 labels,假如我們想要預測天氣,則 x 就是 溫度 濕度 等等,而 y 就是 0 或 1 代表是否會下雨,假如我們想要預測股價,則 x 就是 技術指標、財報等等,而 y 就是股價。
機器學習就是產生這麼一個 function f,可以藉由 x 來推估 y,也就是 y = f(x)
,下方就是機器學習用來訓練的資料(x, y)的示意圖,其中 x 就是一些技術指標,而y就是買賣訊號:
機器學習模型(f)大部分是一個黑盒子,也就是我們不需要知道 f 的公式是什麼
(也不太可能顯示出來,因為太複雜了)
而我們就會先給一些 x 跟 y,並且讓演算法去產生 f,
這個就是 supervise 機器學習模型的基本概念
假如我們要用機器學習來預測股票,就要先有 x 跟 y,也就是 features 跟 labels
假如我們想要預測股價,要怎麼樣來製作 feature 跟 labels 呢?
製作 Features
製作 feature 的資料,可以是一些財報、技術指標、我們課程中也有很多製作這些指標的方式,相信網路上也有很多的介紹,由於不是此篇的重點,所以我就不多說明了XD
製作 Labels
製作 label 其實是非常至關重要的,假如 label 太難預測的話,模型會無法有效的訓練,以往最基本的 label 製作方式是使用 fixed time horizon 來預測 w 個時間單位後的漲跌,下圖就是 fixed time horizon 的 Labeling 方式:
上圖中在 p(t) 的時候,我們希望預測 p(t+w) 的股價,是比較高還是比較低
我們可以使用分類的方式,將股價的漲跌分成三個部分,也就是─1(跌)、0(不漲不跌)、1(漲),這樣子我們就可以讓機器學習來預測,上面圖中,我們可以發現股價比之前的高,所以把它歸類在 1 ,也就是之後會漲。
缺點
然而這種方式有一個缺點,就是當今天模型叫我們買入的時候,一買就是持有 w 個時間單位,不論股價大漲或大跌都必須繼續持有,不能停損停利,這樣就會導致風險無法控制。
當然我們可以在事後回測時加上停損停利,但是這樣就跟模型的初衷有違背,模型產生的label明明就是持有 w 個時間單位,沒有停損停利。
為了解決上述問題,[Prado 2018] Advances in Financial Machine Learning 提出了以下的新方法:
Triple Barrier
乍看之下跟 fixed time horizon 有點類似,不過這個方法將分類的方式做了一些改進,上圖中,用了三種不同顏色的「柵欄」,當股價從 p(t) 開始出發,隨時間不斷延伸,一定會碰到三個柵欄中的其中一個,而這三個柵欄分別代表了不同的意義:
- 1(停利)
- 0(持有 w 個時間單位)
- ─1(停損)
如此一來,我們就可以讓機器學習來預測「包含停損停利獲利狀況」,訓練出來的模型就可以跟回測的設定相符,增加機器學習的可預測性!
雖然概念很好理解,但要寫成程式碼還是需要一點功力,尤其是執行速度快一點的程式碼,下面就分享如何產生此類的label。
程式碼
首先我們可以藉由股價爬蟲這篇裡面介紹的程式碼,來獲得股價,
df = crawl_price("2354.TW")
df.Close.plot()
針對以上的程式碼,
假如想知道 crawl_price
的實做,可以點入股價爬蟲這篇
接下來就可以來製作 labels 了!
在我們的機器學習影音課程中,使用的是 fixed time horizon 的方式,先來看看效果如何:
date = '2017'
df.Close[date].plot()
(df.Close.shift(-20) / df.Close > 1).astype(int)[date].plot(secondary_y=True)
上圖中藍色的為股價,對應到左邊的y軸,黃色的是訊號,對應到右邊的y軸,也就是機器學習要學的 label,其中 1 代表買入,0 代表賣出,看起來效果有些不OK,因為訊號會在短時間內上下跳動,讓我們不知道到底該買還是賣,所以接下來我們就來試試看剛剛所介紹的 triple barrier 產生出來的 label。
下面這段是非常珍貴的程式碼,就連 Advances in Financial Machine Learning 書中附帶的程式碼,都沒有以下的程式碼更簡潔、更完整、更有效率,假如你的 pandas 技巧不夠好的話,請直接拿去使用就可以了XD,不用太深究此 function 中的程式碼!
import numpy as np
import math
def triple_barrier(price, ub, lb, max_period):
def end_price(s):
return np.append(s[(s / s[0] > ub) | (s / s[0] < lb)], s[-1])[0]/s[0]
r = np.array(range(max_period))
def end_time(s):
return np.append(r[(s / s[0] > ub) | (s / s[0] < lb)], max_period-1)[0]
p = price.rolling(max_period).apply(end_price, raw=True).shift(-max_period+1)
t = price.rolling(max_period).apply(end_time, raw=True).shift(-max_period+1)
t = pd.Series([t.index[int(k+i)] if not math.isnan(k+i) else np.datetime64('NaT')
for i, k in enumerate(t)], index=t.index).dropna()
signal = pd.Series(0, p.index)
signal.loc[p > ub] = 1
signal.loc[p < lb] = -1
ret = pd.DataFrame({'triple_barrier_profit':p, 'triple_barrier_sell_time':t, 'triple_barrier_signal':signal})
return ret
ret = triple_barrier(df.Close, 1.07, 0.97, 20)
這個 function 的使用方法,就是將
- 時間序列(程式碼中的
df.Close
) - 停利(程式碼中
1.07
是指7%停利) - 停損(程式碼中
0.97
是指3%停損) - 最大持有時間(20天)
分別丟入 triple_barrier
函式當中,就可以計算出以下的 Dataframe
ret.head()
上圖中有三條序列,其中 index
是日期,另外三條時間序列分別是:
- triple_barrier_profit 當天買入,直到停損停利後,未來的獲利狀況
- triple_barrier_sell_time 當天買入,未來會持有的時間
- triple_barrier_signal 當天買入,未來觸發的是停損停利
我們可以將 triple_barrier_signal
訊號跟股價一起畫出來比較一下:
date = '2017'
df.Close[date].plot()
ret.triple_barrier_signal[date].plot(secondary_y=True)
label 的訊號變得比較乾淨,試試看訓練出更好的模型吧!
可以使用 google colab 線上編輯此代碼