使用機器學習和深度學習技術預測股票價格
介紹
預測股市的走勢是最困難的事情之一。影響預測的因素很多 - 包括物理因素與心理因素,理性行為和非理性行為等。所有這些因素結合在一起共同導致股價波動,很難以高精度預測。
我們是否可以將機器學習作為該領域的遊戲規則改變者嗎?利用一些特性,比如關於一個組織的最新公告,他們的季度收入結果等功能,機器學習技術有可能發掘出我們以前沒有看到的模式和見解,並且可以用來做出準確無誤的預測。
在本文中,我們將使用有關上市公司股票價格的歷史資料。我們將使用多種機器學習演算法來預測該公司的未來股票價格,從平均演算法和線性迴歸等簡單的演算法開始,然後轉向Auto ARIMA和LSTM等高階技術。
本文背後的核心思想是展示如何實現這些演算法。我將簡要介紹該技術並提供相關連結,以便在必要時瞭解這些概念。
問題描述
我們很快就會深入到本文的實現部分,但首先要確定我們要解決的問題。從廣義上講,股票市場分析分為兩部分 - 基礎分析和技術分析。
- 基礎分析是根據公司當前的商業環境和財務業績分析公司未來的盈利能力。
- 另一方面,技術分析包括閱讀圖表和使用統計資料來確定股票市場的趨勢。
你可能已經猜到了,我們的重點將放在技術分析部分。我們將使用Quandl的資料集(你可以在這裡查詢各種股票的歷史資料),對於這個特定的專案,我使用了“ 塔塔全球飲料 ” 的資料。是時候讓我們動起來了!
注意:有關文章的資料集我將在文章最後放出
首先我們先載入資料集,定義問題的目標變數:
匯入包
import pandas as pd
import numpy as np
在筆記本上繪圖
import matplotlib.pyplot as plt
%matplotlib inline
設定圖的大小
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 20,10
規範資料
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
讀取檔案
df = pd.read_csv('NSE-TATAGLOBAL(1).csv')
列印頭部資料
df.head()
資料集中有多個變數 - date,open,high,low,last,close,total trade quantity和turnover。
- “ 開盤價(open)“和“收盤價(close)”列表示股票在特定日期交易的起始和最終價格。
- 高(hight)、低(low)和最後(last)代表當天股票的最高,最低和最後價格。
- 總交易數量(Total Trade Quantity)是當天買入或賣出的股票數量,而成交量(Lacs)是特定公司在特定日期的成交量。
另一個需要注意的重要事項是,市場在週末和公共假期關閉。再次注意上表,一些日期值缺失 - 2/10/201,6/10/201,7/10/201。在這些日期中,2號是法定假日,6號和7號是週末。
損益的計算通常由當天股票的收盤價確定,因此我們將收盤價視為目標變數。讓我們繪製目標變數,以瞭解它在我們的資料中是如何形成的:
將索引設定為日期
df['Date'] = pd.to_datetime(df.Date,format='%Y-%m-%d')
df.index = df['Date']
畫圖
plt.figure(figsize=(16,8))
plt.plot(df['Close'], label='Close Price history')
!]( http://upload-images.jianshu....
在接下來的部分中,我們將探索這些變數,並使用不同的技術來預測股票的每日收盤價。
1.移動平均法
介紹
“平均”很容易成為我們日常生活中最常用的詞彙之一。例如,計算平均分來確定整體效能,或者找出過去幾天的平均溫度以瞭解今天的溫度 - 這些都是我們經常做的例行工作。因此,這是一個很好的起點,可以用於我們的資料集進行預測。
每天的預計收盤價將是一組先前觀測值的平均值。我們將使用移動平均技術而不是使用簡單平均值,該技術為每個預測使用最新的一組值。換句話說,對於每個後續步驟,在從集合中移除最老的觀測值的同時考慮預測值。下面是一個簡單的圖形,可以幫助你更清晰地理解這一點。
我們將在我們的資料集上實現此技術。第一步是建立一個僅包含Date和Close price列的DataFrame,然後將其拆分為訓練集和驗證集以驗證我們的預測。
Python程式碼
使用日期和目標變數建立dataframe
data = df.sort_index(ascending=True, axis=0)
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
for i in range(0,len(data)):
new_data'Date' = data'Date'
new_data'Close' = data'Close'
在將資料拆分為訓練集和驗證集時,我們不能使用隨機拆分,因為這會破壞時間元件。所以這裡我把去年的資料和之前四年的資料進行了驗證。
分隔為訓練集和驗證集
train = new_data[:987]
valid = new_data[987:]
new_data.shape, train.shape, valid.shape
((1235, 2), (987, 2), (248, 2))
train['Date'].min(), train['Date'].max(), valid['Date'].min(), valid['Date'].max()
(Timestamp('2013-10-08 00:00:00'),
Timestamp('2017-10-06 00:00:00'),
Timestamp('2017-10-09 00:00:00'),
Timestamp('2018-10-08 00:00:00'))
下一步是為驗證集建立預測,並使用實際值檢查RMSE。
做出預測
preds = []
for i in range(0,248):
a = train'Close'.sum() + sum(preds)
b = a/248
preds.append(b)
結果
計算 rmse
rms=np.sqrt(np.mean(np.power((np.array(valid['Close'])-preds),2)))
rms
104.51415465984348
僅檢查RMSE並不能幫助我們理解模型的執行方式。讓我們把它形象化來獲得更直觀的理解。因此,這是預測值與實際值的關係圖。
畫圖
valid['Predictions'] = 0
valid['Predictions'] = preds
plt.plot(train['Close'])
plt.plot(valid[['Close', 'Predictions']])
推論
RMSE值接近105,但結果不是很有希望(可以從圖中看出)。預測值與驗證集中的觀測值具有相同的範圍(最初存在增加趨勢,然後緩慢減小)。
在下一節中,我們將介紹兩種常用的機器學習技術 - 線性迴歸和kNN,並瞭解它們在我們股票市場資料上的表現。
2.線性迴歸
介紹
可以在此資料上實現的最基本的機器學習演算法是線性迴歸。線性迴歸模型返回一個確定自變數和因變數之間關係的方程。
線性迴歸的方程可以寫成:
這裡,x1,x2,... .Xň代表獨立變數,而係數θ1,θ2,...θÑ表示的權重。
對於我們的問題描述,我們沒有一組自變數。我們只有日期而已。讓我們使用日期列來提取諸如 - 日,月,年,星期一/星期五等特徵,然後擬合線性迴歸模型。
Python程式碼
我們將首先按升序對資料集進行排序,然後建立一個單獨的資料集,以便建立的任何新要素都不會影響原始資料。
將索引設定為日期值
df['Date'] = pd.to_datetime(df.Date,format='%Y-%m-%d')
df.index = df['Date']
排序
data = df.sort_index(ascending=True, axis=0)
建立單獨的資料集
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
for i in range(0,len(data)):
new_data'Date' = data'Date'
new_data'Close' = data'Close'
建立功能
from fastai.structured import add_datepart
add_datepart(new_data, 'Date')
new_data.drop('Elapsed', axis=1, inplace=True) #elapsed will be the time stamp
這會建立以下特徵:
‘Year’, ‘Month’, ‘Week’, ‘Day’, ‘Dayofweek’, ‘Dayofyear’, ‘Is month end’, ‘Is month start’, ‘Is quarter end’, ‘Is quarter start’, ‘Is year end’, and ‘Is year start’.
注意: 我使用了fastai庫中的add_datepart。如果你沒有安裝它,只需使用命令 pip install fastai。或者,你可以在python中使用簡單的for迴圈建立這個功能。我在下面展示了一個例子。
除此之外,我們可以新增我們自己認為與預測相關的特徵。例如,我的假設是,本週的第一天和最後一天可能會影響股票的收盤價,並且遠遠超過其他日子。所以我建立了一個特徵,可以確定某一天是週一/週五還是週二/週三/週四。這可以使用以下的程式碼行完成:
new_data['mon_fri'] = 0
for i in range(0,len(new_data)):
if (new_data'Dayofweek' == 0 or new_data'Dayofweek' == 4):
new_data'mon_fri' = 1
else:
new_data'mon_fri' = 0
如果星期幾等於0或4,則列值將為1,否則為0。同樣的,你可以建立多個特徵。如果你對可以幫助預測股票價格的功能有一些想法,請在評論區分享。
我們現在將資料拆分為訓練集和驗證集,以檢查模型的效能。
拆分為訓練集與驗證集
train = new_data[:987]
valid = new_data[987:]
x_train = train.drop('Close', axis=1)
y_train = train['Close']
x_valid = valid.drop('Close', axis=1)
y_valid = valid['Close']
實現線性迴歸
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(x_train,y_train)
結果
做出預測並找rmse
preds = model.predict(x_valid)
rms=np.sqrt(np.mean(np.power((np.array(y_valid)-np.array(preds)),2)))
rms
121.16291596523156
RMSE值高於之前的技術,這清楚地表明線性迴歸表現不佳。讓我們看一下圖表,並理解為什麼線性迴歸做的不好:
畫圖
valid['Predictions'] = 0
valid['Predictions'] = preds
valid.index = new_data[987:].index
train.index = new_data[:987].index
plt.plot(train['Close'])
plt.plot(valid[['Close', 'Predictions']])
推論
線性迴歸是一種簡單的技術,並且很容易解釋,但有一些明顯的缺點。使用迴歸演算法的一個問題是模型與日期列和月份列過度匹配。模型將考慮一個月前的同一日期或一年前的同一日期/月的值,而不是從預測的角度考慮以前的值。
從上圖可以看出,2016年1月和2017年1月,股價出現下跌。該模型已預測2018年1月的情況相同。線性迴歸技術可以很好地解決諸如大型超市的銷售問題,在這些問題中獨立特徵對於確定目標值是有用的。
3.K-近鄰
介紹
這裡可以使用的另一個有趣的ML演算法是KNN(K近鄰)。KNN基於自變數找到新資料點和舊資料點之間的相似性。讓我用一個簡單的例子解釋一下。
考慮11個人的身高和年齡。根據給定的特徵('年齡Age'和'身高Height'),表格可以用圖形格式表示,如下所示:
為了確定ID#11的權重,K-NN考慮該ID的最近鄰的權重。ID#11的權重預計是其鄰居的平均值。如果我們現在考慮三個鄰居(k = 3),ID#11的權重將是=(77 + 72 + 60)/ 3 = 69.66千克。
PYthon程式碼
匯入庫
from sklearn import neighbors
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
使用上一節中相同的訓練集和驗證集:
尺度資料
x_train_scaled = scaler.fit_transform(x_train)
x_train = pd.DataFrame(x_train_scaled)
x_valid_scaled = scaler.fit_transform(x_valid)
x_valid = pd.DataFrame(x_valid_scaled)
使用gridsearch查詢最佳引數
params = {'n_neighbors':[2,3,4,5,6,7,8,9]}
knn = neighbors.KNeighborsRegressor()
model = GridSearchCV(knn, params, cv=5)
擬合模型並做出預測
model.fit(x_train,y_train)
preds = model.predict(x_valid)
結果
檢視RMSe值
rms=np.sqrt(np.mean(np.power((np.array(y_valid)-np.array(preds)),2)))
rms
115.17086550026721
RMSE值沒有太大差異,但預測值和實際值的圖應提供一個更清晰的理解。
畫圖
valid['Predictions'] = 0
valid['Predictions'] = preds
plt.plot(valid[['Close', 'Predictions']])
plt.plot(train['Close'])
推論
RMSE值幾乎與線性迴歸模型類似,並且圖表也顯示了相同的模式。與線性迴歸一樣,KNN也確定了2018年1月的下降,因為這是過去幾年的形式。我們可以有把握地說,迴歸演算法在這個資料集上表現不佳。
讓我們繼續看看一些時間序列預測技術,以瞭解它們在面對股票價格預測挑戰時的表現。
4.Auto ARIMA
介紹
ARIMA是一種非常流行的時間序列預測統計方法。ARIMA模型考慮了過去的值來預測未來的價值。ARIMA有三個重要引數:
- p(用於預測下一個值的過去值)
- q(用於預測未來值的過去預測誤差)
- d(差分的順序)
ARIMA的引數調整會消耗大量時間。因此,我們將使用auto ARIMA,它自動選擇(p,q,d)提供最小錯誤的的最佳組合。
Python程式碼
from pyramid.arima import auto_arima
data = df.sort_index(ascending=True, axis=0)
train = data[:987]
valid = data[987:]
training = train['Close']
validation = valid['Close']
model = auto_arima(training, start_p=1, start_q=1,max_p=3, max_q=3, m=12,start_P=0, seasonal=True,d=1, D=1, trace=True,error_action='ignore',suppress_warnings=True)
model.fit(training)
forecast = model.predict(n_periods=248)
forecast = pd.DataFrame(forecast,index = valid.index,columns=['Prediction'])
結果
rms=np.sqrt(np.mean(np.power((np.array(valid['Close'])-np.array(forecast['Prediction'])),2)))
rms
44.954584993246954
畫圖
plt.plot(火車[ '關閉'])
plt.plot(有效[ '關閉'])
plt.plot(預測[ '預測'])
推論
如前所述,auto ARIMA模型使用過去的資料來理解時間序列中的模式。使用這些值,模型獲得了該系列中的增長趨勢。雖然使用這種技術的預測遠比先前實現的機器學習模型的預測好,但這些預測仍然沒有接近實際值。
從圖中可以看出,該模型已經捕捉到了該系列中的一個趨勢,但並沒有關注季節性部分。在下一節中,我們將實現一個時間序列模型,而這個模型考慮了系列的趨勢和季節性。
5.Prophet
介紹
有許多時間序列技術可以在股票預測資料集上實現,但是大多數這些技術在擬合模型之前需要大量的資料預處理。由Facebook設計和開創的Prophet是一個時間序列預測庫,它不需要資料預處理,實現起來也非常簡單。Prophet的輸入是一個包含兩列的資料框:date和target(ds和y)。
Prophet試圖獲取過去資料中的季節性,並在資料集很大時可以進行很好的工作。
Python程式碼
匯入 prophet
from fbprophet import Prophet
建立 dataframe
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
for i in range(0,len(data)):
new_data'Date' = data'Date'
new_data'Close' = data'Close'
new_data['Date'] = pd.to_datetime(new_data.Date,format='%Y-%m-%d')
new_data.index = new_data['Date']
準備資料
new_data.rename(columns={'Close': 'y', 'Date': 'ds'}, inplace=True)
訓練和驗證
train = new_data[:987]
valid = new_data[987:]
使用合適的模型
model = Prophet()
model.fit(train)
預測
close_prices = model.make_future_dataframe(periods=len(valid))
forecast = model.predict(close_prices)
結果
檢視rmse
forecast_valid = forecast'yhat'
rms=np.sqrt(np.mean(np.power((np.array(valid['y'])-np.array(forecast_valid)),2)))
rms
57.494461930575149
畫圖
valid['Predictions'] = 0
valid['Predictions'] = forecast_valid.values
plt.plot(train['y'])
plt.plot(valid[['y', 'Predictions']])
推論
Prophet(與大多數時間序列預測技術一樣)試圖從過去的資料中獲取趨勢性和季節性。此模型通常在時間序列資料集上表現良好,但在這種情況下無法達到它的名譽。
事實證明,股票價格沒有特定的趨勢性或季節性。它在很大程度上取決於市場目前的情況,從而價格會上漲和下跌。因此,像ARIMA,SARIMA和Prophet這樣的預測技術對於這個特定問題不會顯示出良好的結果。
讓我們繼續嘗試另一種先進技術 - 長短時記憶(LSTM)。
6.長短時記憶(LSTM)
介紹
LSTM廣泛用於序列預測問題,並且已被證明是非常有效的。他們非常有效的原因是因為LSTM能夠儲存過去重要的資訊,並忘記不重要的資訊。LSTM有三個門:
- 輸入門: 輸入門將資訊新增到單元格狀態
- 遺忘門: 它刪除模型不再需要的資訊
- 輸出門: LSTM的輸出門選擇要顯示為輸出的資訊
現在,讓我們將LSTM實現為一個黑盒子,並檢查它在我們的特定資料上的效能。
Python程式碼
匯入所需要的庫
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM
建立dataframe
data = df.sort_index(ascending=True, axis=0)
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
for i in range(0,len(data)):
new_data'Date' = data'Date'
new_data'Close' = data'Close'
設定索引
new_data.index = new_data.Date
new_data.drop('Date', axis=1, inplace=True)
建立訓練集和測試集
dataset = new_data.values
train = dataset[0:987,:]
valid = dataset[987:,:]
將資料集轉換為X列和Y列
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(dataset)
x_train, y_train = [], []
for i in range(60,len(train)):
x_train.append(scaled_data[i-60:i,0])
y_train.append(scaled_data[i,0])
x_train, y_train = np.array(x_train), np.array(y_train)
x_train = np.reshape(x_train, (x_train.shape[0],x_train.shape[1],1))
建立並使用LSTM網路
model = Sequential()
model.add(LSTM(units=50, return_sequences=True, input_shape=(x_train.shape[1],1)))
model.add(LSTM(units=50))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(x_train, y_train, epochs=1, batch_size=1, verbose=2)
使用訓練集中的過去的60個值預測246個值
inputs = new_data[len(new_data) - len(valid) - 60:].values
inputs = inputs.reshape(-1,1)
inputs = scaler.transform(inputs)
X_test = []
for i in range(60,inputs.shape[0]):
X_test.append(inputs[i-60:i,0])
X_test = np.array(X_test)
X_test = np.reshape(X_test, (X_test.shape[0],X_test.shape[1],1))
closing_price = model.predict(X_test)
closing_price = scaler.inverse_transform(closing_price)
結果
rms=np.sqrt(np.mean(np.power((valid-closing_price),2)))
rms
11.772259608962642
畫圖
train = new_data[:987]
valid = new_data[987:]
valid['Predictions'] = closing_price
plt.plot(train['Close'])
plt.plot(valid[['Close','Predictions']])
推論
LSTM模型可以根據不同的引數進行調整,例如改變LSTM層的數量,新增dropout值或增加epoch的數量。但LSTM的預測是否足以確定股價是漲還是降?當然不是!
正如我在文章開頭提到的那樣,股票價格受到有關公司的新聞以及其他因素的影響,如公司的非貨幣化或合併/分拆。還有一些無形因素,往往是事先無法預測的。
結束筆記
時間序列預測是一個非常有趣的領域,正如我在撰寫這些文章時所認識到的那樣。在社群中有一種看法,認為它是一個非常複雜的領域,雖然有些的確比較複雜,但是一旦掌握了基本技術,也就不那麼困難了。
本文作者使用了六種方法來進行了對股票漲跌的預測,並從結果中分析了每個演算法用於時間序列模型的優劣,並且從圖中可以看出LSTM方法是擬合最好的一種方法,但是股票市場需要考慮的因素有很多,並不是只需要幾個關鍵的特徵就可以預測的,我們可以根據以前的資料,對演算法進行驗證,但使用演算法去預測未來的股票的漲跌,還是有一些風險的,所以還是要謹慎的去使用這些演算法。至少現在沒有一種演算法可以百分之百的去預測未來股票的時間序列模型演算法,還是先暫時的用演算法去不斷的訓練,直到未來技術成熟的一天。
Stock Prices Prediction Using Machine Learning and Deep Learning Techniques (with Python codes)