基於Python預測股價的那些人那些坑,請認真看完!
編輯部:
前幾天我們已經看過此文,@愛可可發過一次,今天AI科技大本營又翻譯了此文。這篇文章真的很不錯,不是在於模型的好壞,而是在於作者對預測股價的一些心得和體會,編輯部覺得大家應該好好的看一下這篇文章,它告訴我們所有高大上的東西,想在資本市場去賺錢,還是有難度的,預測股價的方法千奇百出,但是要正真理市場解去做,這才是最重要的。
不要跟風,不要盲目,不要虛榮。
B是給別人裝的!錢是給自己掙的!
對資料科學家來說,預測證券市場走勢是一項非常有誘惑力的工作,當然,他們這樣做的目的很大程度上並不是為了獲取物質回報,而是為了挑戰自己。證券市場起起伏伏、變幻莫測,試想一下,如果在這個市場裡存在一些我們或者我們的模型可以學習到的既定模式,讓我們可以打敗那些商科畢業的操盤手,將是多麼美妙。當然,當我一開始使用加性模型(additive model)來做時間序列預測時,我不得不先用模擬盤來驗證我的模型在股票市場上的表現。
一眾挑戰者們都希望在每日收益率上能夠跑贏市場,但是大多數都失敗了,我也未能倖免。不過,在這個過程中也學到了大量Python相關知識,包括面向物件程式設計、資料處理、建模、以及視覺化等等。同時,我也認清了一個道理,不要在每日收益率上錙銖必較,學會容忍適當的短期虧損,放長線才能釣大魚。
一天與三十年對比結果:你寧願把錢投在哪裡?
在任何任務中(不只是資料科學),當我們沒有取得立竿見影的成效時,我們都有三個選擇:
1. 調整結果,讓我們看起來像是成功了
2. 隱藏結果,所以沒有人會注意到
3. 公開我們所有的結果和方法,以便其他人(以及我們自己)可以從中吸取經驗和教訓
顯然,不管站在個人還是社會層面,方案三都是最佳選擇,但它同時也是最需要勇氣去實踐的。我可以選擇性地公佈結果,比如當我的模型能夠帶來豐厚的利潤回報時,我也可以掩蓋失敗的事實,假裝自己從來沒有在這項工作上花過時間。這似乎是很天真的想法!我們之所以能夠進步是因為不斷重複失敗——學習這個過程,而不僅僅是之前的成功。而且,為有難度的任務編寫Python程式碼而付出的努力也並不應該白費!
這篇文章記錄了我使用Python開發的“stock explorer”工具——Stocker的預測功能。此前,我曾展示瞭如何使用Stocker進行分析,並且將完整的程式碼貼在GitHub上,以方便大家。
Github程式碼地址:
https://github.com/WillKoehrsen/Data-Analysis/tree/master/stocker
▌實現預測的Stocker工具
Stocker是一款用於探索股票情況的Python工具。一旦我們安裝了所需的庫(檢視文件),我們可以在指令碼的同一資料夾中啟動一個Jupyter Notebook,並匯入Stocker類:
from stocker import Stocker
現在可以訪問這個類了。我們通過傳遞任一有效的股票程式碼(粗體是輸出)來建立一個Stocker類的物件:
amazon = Stocker('AMZN')
AMZN Stocker Initialized. Data covers 1997-05-16 to 2018-01-18.
根據上面的輸出結果,我們有20年的亞馬遜每日股票資料可以用來探索! Stocker物件是建立在Quandl金融庫上,而且擁有3000多隻股票可以使用。我們可以使用plot_stock函式來繪製一個簡單的歷史股價圖:
amazon.plot_stock()
Maximum Adj. Close = 1305.20 on 2018-01-12.
Minimum Adj. Close = 1.40 on 1997-05-22.
Current Adj. Close = 1293.32.
Stocker的分析功能可以用來發現資料中的整體趨勢和模式,但我們將重點關注預測股票未來的價格上。Stocker中的預測功能是使用一個加性模型來實現的,該模型將時間序列視為季節性(如每日、每週和每月)的整體趨勢組合。Stocker使用Facebook開發的智慧軟體包進行加性建模,用一行程式碼就可以建立模型並進行預測:
model, model_data = amazon.create_prophet_model(days=90)
Predicted Price on 2018-04-18 = $1336.98
注意,表示預測結果的綠線包含了相對應的置信區間,這代表在模型預測的不確定性。在這種情況下,如果將置信區間寬度設定為80%,這意味著我們預計這個範圍將包含實際值的可能性為80%。置信區間將隨著時間進一步擴大,這是因為隨著預測時間距離現有資料的時間越來越遠,預測值將面臨更多的不確定性。任何時候我們做這樣的預測,都必須包含一個置信區間。儘管大多數人傾向於一個確定的值,但我們的預測結果必須反映出我們生活在一個充滿不確定性的世界!
任何人都可以做股票預測:簡單地選擇一個數字,而這就是你的估測(我可能是錯的,但我敢肯定,這是華爾街所有人都會做的)。為了讓我們的模型具有可信度,我們需要評估它的準確性。Stocker工具中有許多用於評估模型準確度的方法。
▌評估預測結果
為了計算準確率,我們需要一個測試集和一個訓練集。我們需要知道測試集的答案,也就是實際的股價,所以我們將使用過去一年的歷史資料(本例中為2017年)。訓練時,我們不選用2014-2016的資料來作為訓練集。監督學習的基本思想是模型從訓練集中學習到資料中的模式和關係,然後能夠在測試資料上正確地重現結果。
我們需要量化我們的準確率,所以我們使用了測試集的預測結果和實際值,我們計算的指標包括測試集和訓練集的美元平均誤差、正確預測價格變化趨勢的時間百分比、以及實際價格落在預測結果80%置信區間內的時間百分比。所有這些計算都由Stocker自動完成,而且視覺化效果很好:
amazon.evaluate_prediction()
Prediction Range: 2017-01-18 to 2018-01-18.
Predicted price on 2018-01-17 = $814.77.
Actual price on 2018-01-17 = $1295.00.
Average Absolute Error on Training Data = $18.21.
Average Absolute Error on Testing Data = $183.86.
When the model predicted an increase, the price increased 57.66% of the time.
When the model predicted a decrease, the price decreased 44.64% of the time.
The actual value was within the 80% confidence interval 20.40% of the time.
可以看到,預測結果真是糟糕透了,還不如直接拋硬幣。如果我們根據這個預測結果來投資,那麼我們最好是買買彩票,這樣比較明智。但是,不要放棄這個模型,第一個模型通常比較糟糕,因為我們使用的是預設引數(稱為超引數)。如果我們最初的嘗試不成功,那麼我們可以調整這些引數來獲得一個更好的模型。在Prophet模型中有許多不同的引數設定需要調整,最重要的是變點先驗尺度(changepoint
prior scale),它控制著模型在資料趨勢上的偏移量。
▌變點先驗(Changepoint Prior)的選擇
變點代表時間序列從增加到減少,或者從緩慢增加到越來越快(反之亦然)。它們出現在時間序列變化率最大的地方。變點先驗尺度表示在模型中給予變點的偏移量。這是用來控制過度擬合與欠擬合的(也被稱為偏差與方差間的權衡)。
一個更高的先驗能創造一個更多變點權重和更具彈性的模型,但這可能會導致過擬合,因為該模型將嚴格遵守訓練資料的規律,而不能將它泛化到新的測試資料中。降低先驗會減少模型的靈活性,而這又可能會導致相反的問題:欠擬合,當我們的模型沒有完全遵循訓練資料,而沒有學習到底層模式時,這種情況就會發生。如何找出適當的引數以達到正確的平衡,這更多的是一個工程問題而不是理論問題,在這裡,我們只能依靠經驗結果。Stocker類有兩種不同的方式來選擇適當的先驗:視覺化和量化。 我們可以從視覺化方法開始:
amazon.changepoint_prior_analysis(changepoint_priors=[0.001, 0.05, 0.1, 0.2])
在這裡,我們使用三年的資料進行訓練,然後顯示了六個月的預測結果。我們沒有量化這裡的預測結果,因為我們只是試圖去理解變點先驗值的作用。這個圖表很好地說明了過擬合與欠擬合!代表最小先驗的藍線與代表訓練資料的黑線值並不是非常接近,就好像它有自己的一套模式,並在資料的附近隨便選了一條路線。相比之下,代表最大先驗的黃線,則與訓練觀察結果非常貼近。變點先驗的預設值是0.5,它落在兩個極值之間的某處。
我們還要注意先驗值不同帶來的不確定性(陰影區間)方面的差異。最小的先驗值在訓練資料上表現有最大的不確定性,但在測試資料上的不確定性卻是最小。相比之下,最大的先驗值在訓練資料上具有最小的不確定性,但在測試資料上卻有最大的不確定性。先驗值越高,對訓練資料的擬合就越好,因為它緊跟每次的觀察值。但是,當使用測試資料時,過擬合模型就會因為沒有任何資料點來定位而迷失掉。由於股票具有相當多的變化性,我們可能需要比預設模型更靈活的模型,這樣才能夠捕捉儘可能多的模式資訊。
現在我們對先驗值帶來的影響有了一個概念,我們可以使用訓練集和驗證集對數值進行評估:
amazon.changepoint_prior_validation(start_date='2016-01-04',
end_date='2017-01-03', changepoint_priors=[0.001, 0.05, 0.1, 0.2])
Validation Range 2016-01-04 to 2017-01-03.
cps train_err train_range test_err test_range
0 0.001 44.475809 152.600078 149.373638 152.564766
1 0.050 11.203019 35.820696 152.033810 139.505624
2 0.100 10.722908 34.593207 152.903481 172.654255
3 0.200 9.725255 31.895204 127.604543 324.376524
在這裡,我們必須注意到,我們的驗證集和測試集是不一樣的資料。如果它們是一樣的,那麼我們會得到在測試資料上效果最好的模型,但是它只是在測試資料上過擬合了,而我們的模型也不能用於現實世界的資料。總的來說,就像在資料科學中通常所做的那樣,我們正在使用三組不同的資料:訓練集(2013-2015)、驗證集(2016)和測試集(2017)。
我們用四個指標來評估四個先驗值:訓練誤差、訓練範圍(置信區間)、測試誤差和測試範圍(置信區間),所有的值都以美元為單位。正如我們在圖中看到的那樣,先驗值越高,訓練誤差越低,訓練資料的不確定性越低。我們也可以看到,更高的先驗能降低我們的測試錯誤。為了在測試集上獲得更高的準確率,作為交換,隨著先驗的增長,我們在測試資料上得到了更大範圍的不確定性。
Stocker先驗驗證還可以通過兩條線來闡述這些點:
基於不同變點先驗尺度下,訓練和測試準確性曲線和不確定性曲線
既然最高的先驗值產生了最低的測試誤差率,我們應該嘗試再增加先驗值來看看是否能得到更好的結果。我們可以通過在驗證中加入其它值的方法來優化我們的搜尋:
amazon.changepoint_prior_validation(start_date='2016-01-04',
end_date='2017-01-03', changepoint_priors=[0.15, 0.2, 0.25,0.4, 0.5, 0.6])
改進後的訓練和測試曲線
當先驗值為0.5時,測試集的錯誤率將最小化。因此我們將重新設定Stocker物件的變點先驗值。
amazon.changepoint_prior_scale = 0.5
我們可以調整模型的其他引數,比如我們期望看到的模式,或者模型使用的訓練資料。找到最佳組合只需要重複上述過程,並使用一些不同的值。請隨意嘗試任意的引數!
▌評估改進的模型
現在我們的模型已經優化好了,我們可以再次評估它:
amazon.evaluate_prediction()
Prediction Range: 2017-01-18 to 2018-01-18.
Predicted price on 2018-01-17 = $1160.43.
Actual price on 2018-01-17 = $1295.00.
Average Absolute Error on Training Data = $10.21.
Average Absolute Error on Testing Data = $99.99.
When the model predicted an increase, the price increased 56.90% of the time.
When the model predicted a decrease, the price decreased 44.00% of the time.
The actual value was within the 80% confidence interval 95.20% of the time.
現在看起來好多了! 這顯示了模型優化的重要性。使用預設值可以提供第一次合理猜測,但是我們需要確定,我們正在使用正確的模型“設定”,就像我們試圖通過調整平衡和淡入淡出來優化立體聲的聲音那樣(很抱歉引用了一個過時的例子)。
▌玩轉股票市場
股票預測是一個有趣的實踐,但真正的樂趣在於觀察這些預測結果在實際市場中會發揮多好的作用。使用evaluate_prediction函式,我們可以在評估期間使用我們的模型“玩一玩”股票市場。我們將使用模型預測給出的策略,與我們在整個期間簡單地購買和持有股票的策略進行一個對比。
我們的策略規則很簡單,如下:
1、當模型預測股價會上漲的那一天,我們開始買入,並在一天結束時賣出。當模型預測股價下跌時,我們就不買入任何股票;
2、如果我們購買股票的價格在當天上漲,那麼我們就把股票上漲的幅度乘以我們購買的股票的數量;
3、如果我們購買的股票價格下跌,我們就把下跌的幅度乘以股票的數量,計作我們的損失。
在整個評估期間,也就是2017年,我們每天以這樣的方式進行股票操作。將股票的數量新增進模型回饋裡面,Stocker就會以數字和圖表顯示的方式告訴我們這個策略是如何進行的:
amazon.evaluate_prediction(nshares=1000)
You played the stock market in AMZN from 2017-01-18 to 2018-01-18 with 1000 shares.
When the model predicted an increase, the price increased 57.99% of the time.
When the model predicted a decrease, the price decreased 46.25% of the time.
The total profit using the Prophet model = $299580.00.
The Buy and Hold strategy profit = $487520.00.
Thanks for playing the stock market!
上圖告訴了我們一個非常寶貴的策略:買入並持有!雖然我們可以在策略上再作出相當大的調整,但更好的選擇是長期投資。
我們可以嘗試其他的測試時間段,看看有沒有什麼時候我們的模型給出的策略能勝過買入和持有的方法。我們的策略是比較保守的,因為當我們預測市場下跌的時候我們不進行操作,所以當股票下跌的時候,我們期待有比持有策略更好的方法。
一直用虛擬貨幣實驗
我就知道我們的模型可以做到這一點!不過,我們的模型只有在已經有了當天的資料時才能戰勝市場,也就是說還只是事後諸葛亮。
▌對股票未來價格的預測
現在我們有了一個像樣的模型,然後就可以使用predict_future()函式來對股票未來價格的進行預測。
amazon.predict_future(days=10)
amazon.predict_future(days=100)
預測接下來10天和100天的股票價格趨勢
這個模型和大多數“專業人士”一樣,總體上看好Amazon這支股票。另外,我們按照預期做出的估計,不確定性會進一步增加。實際上,如果我們使用這個模型策略進行交易,那我們每天都可以訓練一個新的模型,並且提前預測最多一天的價格。
雖然我們可能沒有從Stocker工具中獲得豐厚的收益,但是重點在於開發過程而不是最終結果! 在我們嘗試之前,我們實際上不知道自己是否能解決這樣一個問題,就算最終失敗,也好過從不嘗試!任何有興趣檢查程式碼或使用Stocker工具的人,都可以在GitHub上找到程式碼。(https://github.com/WillKoehrsen/Data-Analysis/tree/master/stocker)
作者 | William Koehrsen
原文 | https://towardsdatascience.com/stock-prediction-in-python-b66555171a2