2021年11月27日 星期六

用FB Prophet預測美股未來行情

最近覺得自己美股轉換標的的時機都不是很好, 抄底常抄在半山腰, 就想說來研究一下預測趨勢好了, 雖然知道要預測市場趨勢基本上不可能, 變因變數太多, 不過就當作小參考也好, 至少能大概知道從過去來看目前大致的趨勢是向上或向下也不錯~。

決定好要做之後就先隨便google一下, 剛好看到FB有發布一個簡易使用的時序預測模型, 基本公式如下:


從公式看可以知道主要是以趨勢跟週期的變化去擬合未來的預測結果, 詳細介紹可以參考這篇。 基本上單針對行情預測這件事來說, 最重要的就是趨勢的預測, 年週期的部分則是對淡旺季有所幫助, 節假日的部分或許也或多或少有幫助 (e.g. 感恩節, 黑色星期五等?)。

這篇介紹有含簡單跑模型的sample code

再來關於股價資料這塊, 我之前做的Norn-StockScreener有設定好Github Action每天自動抓美股6000多檔的近一年OHLCV日報價, 這樣就可以直接開工了, 第一版做好的Github如下:

https://github.com/zmcx16/stock-forecast

再來跑跑看效果如何, 因為FB Prophet基本上是單變數模型, 所以先把近一年的資料拆成9:1, 前10.8個月當訓練資料, 後1.2個月當測試資料, 再來評估模型的效果, 先來跑AT&T測試看看:


上面黑色的點是真實資料的點價位, 藍色的線則是模型訓練完後模擬出的結果, 淺藍色的區塊則是預測線的上限及下限, 可以當成模型認為可能的最大誤差, 至於Y軸的值為什麼跟股價不一樣, 是因為有先對資料做對數轉換, 把資料變得更平穩並且預測的趨勢能更趨於線性, 想更瞭解可以參考下面這兩篇:

https://pythondata.com/forecasting-time-series-data-with-prophet-part-1/

https://people.duke.edu/~rnau/411log.htm



第二張圖則是Prophet其他component的圖, 上面那張是趨勢預測圖, 基本上前大半部分會跟這一年的股價差不多擬合, 而最後面那一段有淡藍色區塊的則是實際預測的部分。 而下面那張圖是用每週循環的預測, 基本上意義不大就不看了~。




最後第三張圖則是在把資料做對數反轉換, 還原回原本的股價資料, 光從AT&T本身的股價趨勢就覺得會繼續往下跌, 模型預測出的結果也就是符合他近期的下跌趨勢, 在加一些怪怪的小循環震盪, 比較有意思的地方是, 預測的股價下限趨勢差不多是維持一樣的斜率, 可是上限則是往上持平, 應該可以想成模型認為股價之後可能會繼續用差不多的速度往下跌, 或是轉為盤整趨勢。 再來最後我們可以用測試資料跟預測資料算一下平均誤差跟均方誤差:

MSE: 1.2798056068411054   MAE: 1.0764610288441487


單純用平均誤差看, 跟真實股價平均差一塊錢左右, 以25塊的股價算差不多是4%, 1.8個月的預測資料跟真實資料落差4%價格, 這樣的結果是好或不好, 其實我也不太能把握, 畢竟AT&T的beta本來就小, 如果是波動大的個股, 只落差4%的話我是會覺得很厲害...。

跑完上面的模型, 在來重新思考一下, 上面的模型只是單純用近10.2個月的收盤價, 去預測後面1.8個月的收盤價, 就只是個單變數的模型, 可是股票市場的變數何其多, 單用收盤價就去預測未來資訊太少了, 能準確預測反而才奇怪。 

關於這點, Prophet本身是可以接受添加附加的回歸量來訓練模型的, 只不過必須在訓練資料跟預測資料上都有想加的回歸量才行, 可是未來的資料不可能會有, 只能用某些方法才能生成出來, 關於這問題我是直接參考這篇:

https://stackoverflow.com/questions/54544285/is-it-possible-to-do-multivariate-multi-step-forecasting-using-fb-prophet/58781257#58781257

簡單作法就是, 先把你想加的回歸量都做一遍預測, 然後拿這些預測的回歸量當作你真正要預測的模型的附加回歸量, 這樣就可以只依靠訓練資料就能做多變數預測模型了, 在這邊我額外加入了Open High Low Volume作為附加的回歸量, 並來預測未來收盤價的結果:

 





恩....看第一張圖跟第三張圖, 可以發現預測線跟上下限極度貼合實際的股價, 其實這結果不太意外, 因為附加的回歸量有開盤價跟最高價以及最低價, 而這三種資料跟收盤價本來就是極度相近的, 所以畫出來的會長這樣我倒是不太意外...。

再來看最後一段預測的結果, 趨勢來說還是一樣的, 只是上下限的部分也極度貼合, 這通常就兩種情況, 一是真的精準度很高, 預測神準, 不過想也知道不太可能; 而第二種情況是最有可能的, 就是overfitting過度學習了, 來看看最終測試資料的平均誤差跟均方誤差結果:

MSE: 1.1426736398003121  MAE: 0.9865404828745983

結果還真的比只用收盤價的模型好了近10%, 我是覺得只是運氣好啦... 關於這個問題就先放一邊, 因為上面的預測模型看起來就太overfitting了, 這次附加回歸量我去掉Open High Low, 只用Volume試試看, 因為Volume本身跟價格無關, 應該不會在有上面那種極度擬合的預測圖, 下面是跑完的結果:




MSE: 1.3153719245816298  MAE: 1.0991889378931168

看起來跟只用收盤價預測差不多, 不過均方差跟平均差反而更大了, 其實要說不意外也是不意外, 畢竟交易量大增有可能大漲也有可能大跌, 至於到底往上走還是往下走都是概率的問題, 猜錯方向就是大錯...。


最後再來跑另一檔個股吧, 這次挑AAPL好了, 挑市值最大的公司應該很有參考價值, 首先只用收盤價訓練模型:





MSE: 100.25974199043638   MAE: 9.25843258960275

預測結果整個超差, 畢竟預測值是往下跌的, 而AAPL在10月低點是開始往上漲, 所以模型猜錯方向導致結果超差其實不意外。  

再來看看用Open High Low Volume作為模型附加回歸量的結果:






MSE: 227.09063258654962  MAE: 12.947364146372168

嚇死人, 是怎麼得出這種鬼東西的..., 看起來是把週期性的東西考慮進去了, 可是為什麼下限的波動這麼大, 完全搞不明白, 理所當然的結果當然更差了...。 

最後再來看只用Volume作為附加回歸量的結果:





MSE: 98.57041352599616  MAE: 9.165440570506956

結果只用Volume做附加回歸量反而結果是最好的, 還真是符合預測市場的名言: "沒有絕對賺錢的聖杯..." 

基本上這樣做下來, 我應該會把這個預測作為肉眼&均線看趨勢以外的工具, 拿來當作額外參考用的工具這樣~。

關於FB Prophet的使用經驗大概就分享到這, 後面會繼續分享把這個工具佈署到Github Action跟Google GCP的經驗談, 有想看的在繼續看下去吧~。


既然工具都做好了, 當然會想把它放到自己做好的財經網站norn-stockscreener上啦! 

首先照老樣子, 先試試用Github Action跑跑看, 具體作法是讓github action去拉我每天自動爬好的股價歷史資料, 在對每一檔個股跑上面的預測模型。

結論: 失敗...

因為跑完一檔個股就要花50秒, 跑6000多檔個股要花大約80個小時, 而這個任務必須每天收盤後就跑, 即便我開mulit-thread去跑, Github Action Runner只提供2-core CPU, 根本不可能跑得完, 除非我開大量repo去分批跑這些任務, 可是這樣過度濫用會踩到Github使用條款的點, 嚴重的話搞不好不只repo被封, 連帳號都可能被鎖也說不定, 所以只能放棄用Github Action了...。

下一個想到能用的, 就是雲端平台上的Serverless function了, 基本上Azure / AWS / GCP都有提供自家Serverless function免費的扣打, 在一定的CPU / 記憶體 / 呼叫次數 / 頻寬上是可以免費的, 我目前財經網站的資料也都是靠Azure function去產生, 其中因為Azure的部分頻寬費我已經超標, 每個月都要付300~400塊的頻寬費, 所以這次就不考慮用Azure了。 

至於要選AWS還是GCP, 比較了一下AWS只給免費1G的頻寬, 而GCP給5G免費頻寬, 就決定選GCP了, 關於GCP Cloud Function的計價方式, 可以參考這:



決定好以後就來佈上GCP吧, 基本上就是refactor一下原本的code, 多一個讓GCP Cloud Function呼叫的function進入點, 之後就讓Github Action一口氣開一堆thread (開的這些thread主要都是I/O bound不是CPU bound, 照理說不會花太多github上的資源, 應該是不會踩到使用者條款濫用), 以下是測試的結果:

16 Thread:   2h 40m 10s
32 Thread:   1h 21m 57s
64 Thread:   39m 23s

因為GCP Cloud Function可以同時跑幾千個instance都沒問題, 所以就算繼續往上拉高thread也沒問題, 直到bottleneck不在GCP Cloud Function上為止, 結果很成功, 不過我最關心的還是, 這樣算下來會不會超標免費的扣打, 結果...才跑三批就超標了T.T




上圖是我分別跑三次預測6000檔個股的使用情況, 可以看到同時運行的instance分別大約為25, 50, 100, 平均記憶體使用大約是500MB, 而每個函數平均執行的時間則是50秒, 所以總共花的CPU time是50 * 6000 * CPU GHz sec , Memory則是0.5 * 50 * 6000 GB sec (略估)。 

再來看看實際帳單:




才跑三次就有112塊的花費, 在扣掉免費額度的扣打, 我跑三次就超標要付台幣41塊了...。 GCP Cloud Function的免費額度如下:

Memory: 400000 GB 秒
CPU:       200000 GHz 秒
頻寬:       5GB

所以主要是CPU超標了, 畢竟一個Function就要跑50秒真的超耗CPU..., 至於Memory只要再跑三次也一樣超標, 如果我真的要提供這個功能, 那我一個月至少要跑22次, 隨便算一下可能要800~1000塊每個月, 所以最終決定只能放棄上架這個功能到GCP... 我應該最多就整合到自己的個人網站, 每天只跑個人持股的部分吧。

這次分享就到這邊, 之後有空打算再研究其他的時序預測模型, 加減玩玩看~~~。


沒有留言:

張貼留言