作者:Vivek Palaniappan
編譯:NumberOne
機器學習和深度學習已經成爲定量對衝基金常用的新的有效策略,以最大化其利潤。作爲一名人工智能和金融愛好者,這是令人激動的消息,因爲它結合了我自己感興趣的兩個領域。本文將介紹如何使用神經網絡預測股票市場,特別是股票的價格。這篇文章的源碼在我的GitHub中的python項目,如下:
源碼: https://github.com/VivekPa/NeuralNetworkStocks
▍金融中的神經網絡
金融是高度非線性的,有時股票價格數據甚至看起來完全隨機,沒有任何規律。傳統的時間序列方法(如ARIMA和GARCH模型)僅在靜止時纔有效,這是一個限制性假設,需要通過記錄返回,或其他變換,來對序列進行預處理。然而,主要的問題出現在實時交易系統中這些模型的實現,因爲在添加新數據時無法保證平穩性。
這不需要使用任何平穩性的神經網絡來進行對抗。此外,神經網絡本質上在尋找數據之間的關係和使用它預測(或分類)新數據方面是有效的。
典型的完整數據科學項目可以參考以下工作流程:
- 數據採集 - 提供了特徵
- 數據預處理 - 非常複雜,但是必要,爲了使數據可用
- 開發和模型實現 - 選擇神經網絡和參數的類型
- 回測模型 - 任何交易策略中非常關鍵的一步
- 優化 - 找到合適的參數
神經網絡的輸入數據是過去十天的股價數據,我們用它來預測第二天的股價數據。
▍數據採集
非常幸運,該項目所需的股票價格數據在雅虎財經中都是隨時可用的。可以使用他們的Python API,或直接從他們的網站獲取數據。
▍數據預處理
在我們的案例中,我們需要將數據分解爲十個價格和第二天價格的訓練集。通過定義一個類Preprocessing,將其分解爲訓練和測試數據並定義一個方法get_train(self,seq_len)來完成此操作,該方法將訓練數據(輸入和輸出)作爲numpy數組返回,給定特定長度的窗口(十在我們的情況下)。完整代碼如下:
def gen_train(self, seq_len): """ 生成訓練數據 :param seq_len: 窗口長度 :return: X_train and Y_train """ for i in range((len(self.stock_train)//seq_len)*seq_len - seq_len - 1): x = np.array(self.stock_train.iloc[i: i + seq_len, 1]) y = np.array([self.stock_train.iloc[i + seq_len + 1, 1]], np.float64) self.input_train.append(x) self.output_train.append(y) self.X_train = np.array(self.input_train) self.Y_train = np.array(self.output_train)
對於測試數據,同樣也有返回測試數據 X_test 和 Y_test 的方法。
▍神經網絡模型
對於這個項目而言,我使用了兩種神經網絡模型:多層感知器(MLP)和長短期模型(LSTM)。下面將簡要地介紹這些模型的工作原理,但是要了解MLP的工作原理,而對於LSTM,請查看Jakob Aungiers撰寫的這篇優秀文章。相關鏈接如下:
MLP: https://medium.com/engineer-quant/multilayer-perceptron-4453615c4337
LSTM: https://www.altumintelligence.com/articles/a/Time-Series-Prediction-Using-LSTM-Deep-Neural-Networks
MLP是最簡單的神經網絡形式,其中輸入將被喂到模型中,並且會使用特定權重,值通過隱藏層向前送入以產生輸出。學習來自於通過隱藏層反向傳播以改變每個神經元之間權重的值。 MLP的一個問題是缺乏“記憶”。對以前的訓練數據中發生的事情沒有任何意義,以及這可能會如何影響新的訓練數據。在我們的模型的上下文中,一個數據集中的十天數據與另一個數據集之間的差異可能很重要(例如)但是MLP沒有分析這些關係的能力。
這就是LSTM或一般的遞歸神經網絡(RNN)的用武之地。RNN能夠存儲有關數據的某些信息供以後使用,這擴展了網絡分析股票價格數據之間關係的複雜結構的能力。RNN的一個問題是消失的梯度問題。這是因爲當層數增加時,學習率(值小於1)被乘以幾次,這導致梯度不斷減小。 LSTM對此進行了打擊,使其更有效。
▍實現模型
爲了實現模型,我選擇了keras,因爲它使用了向網絡添加層而不是一次定義整個網絡的想法。這使我們能夠快速更改層數和層類型,這在優化網絡時非常方便。
使用股票價格數據的一個重要步驟是規範化數據。這通常意味着你減去平均值併除以標準偏差,但在我們的情況下,我們希望能夠在一段時間內在實時交易中使用該系統。因此,採用統計時刻可能不是規範化數據的最準確方法。所以我只將整個數據除以200(任意數字使得一切都變小)。雖然似乎標準化是從空氣中汲取的,但它仍然有效地確保神經網絡中的權重不會變得太大。
讓我們從更簡單的MLP開始。在keras中,這是通過製作順序模型並在其上添加密集層來完成的。完整代碼如下:
model = tf.keras.models.Sequential() model.add(tf.keras.layers.Dense(100, activation=tf.nn.relu)) model.add(tf.keras.layers.Dense(100, activation=tf.nn.relu)) model.add(tf.keras.layers.Dense(1, activation=tf.nn.relu)) model.compile(optimizer="adam", loss="mean_squared_error")
這就是keras的優雅所在。只需要僅僅5行代碼,我們就創建了一個帶有兩個隱藏層的MLP模型,每個層都有100個神經元。下面是關於優化器的一些個人看法。目前,Adam優化器在機器學習社區中越來越受歡迎,因爲它與傳統的隨機梯度下降相比,它更爲有效。我們可以觀察隨機梯度下降的另外兩個擴展優點,這樣可以最好地理解這些優點:
- 自適應梯度算法(AdaGrad),保持參數學習速率,改善稀疏梯度問題的性能(例如自然語言和計算機視覺的問題)。
- 均方根傳播(RMSProp)也可以保持參數學習速率,該參數學習速率基於權重的最近梯度的平均值(例如,其變化的速度)進行調整。這意味着該算法在在線和非靜止問題(例如噪聲)上表現良好。
可以說,Adam是結合上述擴展的好處,這也就是爲什麼我選擇使用Adam作爲優化器的原因所在。
現在我們需要用數據去擬合出一個模型(訓練一個模型)。同樣,keras使它變得簡單,只需1行代碼:
model.fit(X_train, Y_train, epochs=100)
訓練了我們的模型之後,我們需要根據測試數據對其進行評估,以瞭解它的性能和運行狀況。通過以下代碼完成:
model.evaluate(X_test, Y_test)
您可以從評估指標中來評估模型預測股票價格的能力。
對於LSTM模型來說,過程類似,代碼如下:
model = tf.keras.Sequential() model.add(tf.keras.layers.LSTM(20, input_shape=(10, 1), return_sequences=True)) model.add(tf.keras.layers.LSTM(20)) model.add(tf.keras.layers.Dense(1, activation=tf.nn.relu)) model.compile(optimizer="adam", loss="mean_squared_error") model.fit(X_train, Y_train, epochs=50) model.evaluate(X_test, Y_test)
需要注意的一點是,keras要求輸入數據具有某些維度,由模型決定,因此使用numpy進行重塑數據是非常重要的。
▍回測模型
現在我們得到了擬合出來的模型,並使用測試數據對其進行了評估。然後,我們可以通過在新數據上回溯測試模型來進一步評估了。代碼如下:
def back_test(strategy, seq_len, ticker, start_date, end_date, dim): """ A simple back test for a given date period :param strategy: the chosen strategy. Note to have already formed the model, and fitted with training data. :param seq_len: length of the days used for prediction :param ticker: company ticker :param start_date: starting date :type start_date: "YYYY-mm-dd" :param end_date: ending date :type end_date: "YYYY-mm-dd" :param dim: dimension required for strategy: 3dim for LSTM and 2dim for MLP :type dim: tuple :return: Percentage errors array that gives the errors for every test in the given date range """ data = pdr.get_data_yahoo(ticker, start_date, end_date) stock_data = data["Adj Close"] errors = [] for i in range((len(stock_data)//10)*10 - seq_len - 1): x = np.array(stock_data.iloc[i: i + seq_len, 1]).reshape(dim) / 200 y = np.array(stock_data.iloc[i + seq_len + 1, 1]) / 200 predict = strategy.predict(x) while predict == 0: predict = strategy.predict(x) error = (predict - y) / 100 errors.append(error) total_error = np.array(errors) print(f"Average error = {total_error.mean()}")
但是,這種回溯測試只是一個簡化版本,並非一個完整的回測系統。對於完整的回測系統,你還需要考慮生存偏差,預測偏差,市場體制變化和交易成本等因素。本篇只是一個簡單的教學項目,上面的回測就足夠了。
下圖展示了LSTM模型在2月份Apple股票價格的預測表現。
可以看到,對於還沒有優化的簡單LSTM模型,這是非常好的預測。這真實說明了神經網絡和機器學習模型在建模參數之間複雜關係方面是非常穩健的。
▍超參數調整
對於優化神經網絡模型,在樣本外測試提高模型的性能通常很重要。我沒有在開源版本的項目中使用調優,因爲我希望能夠給更多的人機會來調節參數,並嘗試優化模型以使其表現更好。對於那些不瞭解優化的人來說,就需要找到最大化模型性能的超參數。有幾種方法可以幫助搜索這些最優的超參數,比如網格搜索,隨機方法。我認爲,優化模型的學習可以將你的機器學習水平,因此,我提出了一個優於上圖中性能的優化模型。
▍結論
機器學習真的是每天都在不斷變化發展,每天都會有新的方法。但重要的是,我們不斷更新知識,最好的方法是找到有趣的項目,去建模,如股票價格預測。雖然上面完成的LSTM模型還不足以用於實時交易,但通過練習實踐這樣的模型可以幫助我們構建更好的模型,這些模型很有可能在未來某一天爲我們使用。