簡介
預測股市將如何變化歷來是最困難的事情之一。這個預測行為中包含著如此之多的因素—包括物理或心理因素、理性或者不理性行為因素等等。所有這些因素結合在一起,使得股價波動劇烈,很難準確預測。
使用機器學習可能改變遊戲規則嗎?機器學習技術使用最新的組織公告、季度收益等作為特徵,有潛力挖掘出我們以前沒有見過的模式和見解,並可用於準確無誤的預測。
在本文中,我們將研究上市公司股價的歷史資料。我們將結合機器學習演算法來預測這家公司的未來股價,從平均和線性迴歸這樣的簡單演算法開始,然後轉向像Auto ARIMA和LSTM這樣的高階模型。
本文背後的核心思想是展示這些演算法是如何實現的,因此我只會簡單描述該技術並提供相關參考連結,以便在必要時對這些概念進行復習。如果您是時間序列領域的新手,我建議您先閱讀以下文章:
建立時間序列預測的初學者綜合指南
時間序列建模的完整教程
目錄
1、 問題理解
2、 移動平均
3、 線性迴歸
4、 K-近鄰
5、 自動ARIMA
6、 先知(Prophet)
7、 長短時記憶網路(LSTM)
1、問題理解
我們將很快深入本文的實現部分,但首先重要的是確定我們要解決的問題。一般來說,股票市場分析分為兩個部分——基本面分析和技術分析。
基本面分析是根據公司目前的經營環境和財務狀況,對公司未來的盈利能力進行分析。
技術分析包括閱讀圖表和使用統計數字來確定股票市場的趨勢。
您可能已經猜到,我們的重點將放在技術分析部分。我們將使用來自Quandl的資料集(您可以在這裡找到各種股票的歷史資料),這個專案中,我使用了“塔塔全球飲料”的資料。是時候開始了!
首先讓我們載入資料集,定義問題的目標變數:
#import packages
import pandas as pd
import numpy as np
#to plot within notebook
import matplotlib.pyplot as plt
%matplotlib inline
#setting figure size
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 20,10
#for normalizing data
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
#read the file
df = pd.read_csv('NSE-TATAGLOBAL(1).csv')
#print the head
df.head()
資料集中有多個變數——日期(date)、開盤價(open)、最高價(high)、最低價(low)、最後交易價(last)、收盤價(close)、總交易額(total_trade_quantity)和營業額(turnover)。
開盤價和收盤價代表股票在某一天交易的起始價和最終價。
最高價、最低價和最後交易價表示當天股票的最高價、最低價和最後交易價格。
交易總量是指當天買賣的股票數量,而營業額(Lacs)是指某一特定公司在某一特定日期的營業額。
要注意的另一點是,市場在週末和公共假期休市。注意上表缺失了一些日期值——2/10/2018、6/10/2018、7/10/2018。其中2號是國慶節,6號和7號是週末。
損益的計算通常由股票當日的收盤價決定,因此我們將收盤價作為目標變數。讓我們畫出目標變數來理解它在我們的資料集中的分佈:
#setting index as date
df['Date'] = pd.to_datetime(df.Date,format='%Y-%m-%d')
df.index = df['Date']
#plot
plt.figure(figsize=(16,8))
plt.plot(df['Close'], label='Close Price history')
在接下來的部分中,我們將探索這些變數,並使用不同的技術來預測股票的每日收盤價。
2、移動平均
簡介
“平均數”是我們日常生活中最常用的統計量之一。例如,計算平均分數來確定整體表現,或者根據過去幾天的平均溫度來了解今天的溫度——這些都是我們經常做的日常工作。因此,使用這個方法開始用資料集進行預測是個不錯的選擇。
我們利用一組先前觀測值的平均值作為每天的預期收盤價。我們將使用移動平均法,而不是使用簡單的平均值,移動平均法使用最近的一組資料計算預測值。換句話說,對於後續的每個新的時間,在考慮預測值時,將從集合中刪除最早的觀測值,並加入上一個觀測值。下面是一個簡單的圖,它將幫助您更清楚地理解這一點。
我們將在資料集上使用這種技術。第一步是建立一個只包含日期和收盤價列的資料框,然後將其拆分為訓練集和驗證集來驗證我們的預測。
實現
#creating dataframe with date and the target variable
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'][i] = data['Date'][i]
new_data['Close'][i] = data['Close'][i]
在將資料分割為訓練和驗證時,我們不能使用隨機分割,因為這會破壞時間順序。所以這裡我將去年的資料作為驗證集,將之前4年的資料作為訓練集。
#splitting into train and validation
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誤差。
#make predictions
preds = []
for i in range(0,248):
a = train['Close'][len(train)-248+i:].sum() + sum(preds)
b = a/248
preds.append(b)
結果
#calculate rmse
rms=np.sqrt(np.mean(np.power((np.array(valid['Close'])-preds),2)))
rms
104.51415465984348
僅僅檢查RMSE並不能幫助我們評估模型預測效果的。讓我們通過做圖得到更直觀的理解。下面是預測值和實際值的曲線圖。
#plot
valid['Predictions'] = 0
valid['Predictions'] = preds
plt.plot(train['Close'])
plt.plot(valid[['Close', 'Predictions']])
推論
RMSE值接近105,但是結果不是很理想(從圖中可以看出)。預測值與訓練集的觀測值的範圍相同(開始有上升趨勢,然後緩慢下降)。
在下一節中,我們將使用兩種常用的機器學習技術——線性迴歸和kNN,看看它們在我們的股票市場資料上表現如何。
3、線性迴歸
簡介
在這些資料上可以實現的最基本的機器學習演算法是線性迴歸。線性迴歸模型生成一個確定自變數和因變數之間關係的方程。
線性迴歸方程可以寫成:
在這裡x1, x2, ....xn代表自變數,係數θ1,θ2,....θn代表權重。你可以參考下面的文章來更詳細地學習線性迴歸:
對於線性迴歸和Lasso迴歸的一個綜合的初學者指南.
我們的問題中,沒有太多的自變數,只有日期。讓我們使用時間(date)列提取特徵,如- day, month, year, mon/fri等,然後擬合線性迴歸模型。
實現
我們將首先按升序對資料集進行排序,然後建立一個單獨的資料集,這樣建立的任何新特性都不會影響原始資料。
#setting index as date values
df['Date'] = pd.to_datetime(df.Date,format='%Y-%m-%d')
df.index = df['Date']
#sorting
data = df.sort_index(ascending=True, axis=0)
#creating a separate dataset
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
for i in range(0,len(data)):
new_data['Date'][i] = data['Date'][i]
new_data['Close'][i] = data['Close'][i]
#create features
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'][i] == 0 or new_data['Dayofweek'][i] == 4):
new_data['mon_fri'][i] = 1
else:
new_data['mon_fri'][i] = 0
如果是星期日或星期五,列值將為1,否則為0。類似地,您可以建立多個特性。如果你對有助於預測股價的特徵有一些想法,請在留言裡分享。
現在我們將把資料分成訓練集和驗證集來檢查模型的效能。
#split into train and validation
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']
#implement linear regression
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(x_train,y_train)
結果
#make predictions and find the 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值高於之前的方法,這清楚地表明線性迴歸的表現很差。讓我們看看這個圖,並理解為什麼線性迴歸預測效果不是很好:
#plot
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月也將如此。線性迴歸技術可以很好地解決像大賣場銷售這樣的問題,在這些問題中,獨立的特徵對於確定目標值是有用的。
4、k-近鄰
簡介
另一個有趣的ML演算法是kNN (k近鄰)。基於自變數,kNN可以發現新資料點與舊資料點之間的相似性。讓我用一個簡單的例子來解釋這個問題。
考慮11個人的身高和年齡。根據給定的特徵(“年齡”和“身高”),下表可以用圖形形式表示,如下圖所示:
為了確定ID #11的權重,kNN考慮該ID的最近鄰居的權重。ID #11的權重被預測為其鄰居的平均值。如果我們現在考慮三個鄰居(k=3), ID#11的重量將是= (77+72+60)/3 = 69.66 kg。
對於kNN的詳細瞭解,可以參考以下文章:
k近鄰介紹:簡介
對k近鄰迴歸演算法的實際介紹
實現
#importing libraries
from sklearn import neighbors
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
使用上一節中相同的訓練和驗證集:
#scaling data
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)
#using gridsearch to find the best parameter
params = {'n_neighbors':[2,3,4,5,6,7,8,9]}
knn = neighbors.KNeighborsRegressor()
model = GridSearchCV(knn, params, cv=5)
#fit the model and make predictions
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值並沒有太大的差異,但是一個預測值和實際值的曲線圖應該提供一個更清晰的理解。
#plot
valid['Predictions'] = 0
valid['Predictions'] = preds
plt.plot(valid[['Close', 'Predictions']])
plt.plot(train['Close'])
推論
RMSE值與線性迴歸模型近似,圖中呈現出相同的模式。與線性迴歸一樣,kNN也發現了2018年1月的下降,因為這是過去幾年的模式。我們可以有把握地說,迴歸演算法在這個資料集上表現得並不好。
讓我們來看一些時間序列預測技術,看看它們在面對股價預測挑戰時的表現。
5、自動ARIMA
簡介
ARIMA是一種非常流行的時間序列預測統計方法。ARIMA模型使用過去的值來預測未來的值。ARIMA中有三個重要引數:
p(用來預測下一個值的過去值)
q(用來預測未來值的過去預測誤差)
d(差分的順序)
ARIMA的引數優化需要大量時間。因此我們將使用自動 ARIMA,自動選擇誤差最小的(p,q,d)最佳組合。要了解更多關於自動ARIMA的工作原理,請參閱本文:
利用自動ARIMA建立高效能時間序列模型
實現
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
#plot
plt.plot(train['Close'])
plt.plot(valid['Close'])
plt.plot(forecast['Prediction'])
推論
正如我們前面看到的,自動ARIMA模型使用過去的資料來理解時間序列中的模式。利用這些值,該模型捕捉到該系列中的增長趨勢。雖然使用這種技術的預測比以前實現的機器學習模型的預測要好得多,但是這些預測仍然與實際值相距甚遠。
從圖中可以明顯看出,該模型在序列中捕捉到了一種趨勢,但忽略了季節的影響。在下一節中,我們將使用一個同時考慮了序列的趨勢和季節性的時間序列模型。該模型。
6、先知(Prophet)
簡介
有許多時間序列技術可以用在股票預測資料集上,但是大多數技術在擬合模型之前需要大量的資料預處理。Prophet(先知)由Facebook設計和開發,是一個時間序列預測庫,不需要資料預處理,並且非常容易實現。先知的輸入是一個帶有兩列的資料框:日期和目標(ds和y)。
先知試圖在過去的資料中捕捉季節性,並且在資料集很大的時候依然表現良好。這裡有一篇有趣的文章,用一個簡單和直觀的方式解釋了先知演算法:
使用Facebook的Prophet生成快速準確的時間序列預測
實現
#importing prophet
from fbprophet import Prophet
#creating dataframe
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
for i in range(0,len(data)):
new_data['Date'][i] = data['Date'][i]
new_data['Close'][i] = data['Close'][i]
new_data['Date'] = pd.to_datetime(new_data.Date,format='%Y-%m-%d')
new_data.index = new_data['Date']
#preparing data
new_data.rename(columns={'Close': 'y', 'Date': 'ds'}, inplace=True)
#train and validation
train = new_data[:987]
valid = new_data[987:]
#fit the model
model = Prophet()
model.fit(train)
#predictions
close_prices = model.make_future_dataframe(periods=len(valid))
forecast = model.predict(close_prices)
結果
#rmse
forecast_valid = forecast['yhat'][987:]
rms=np.sqrt(np.mean(np.power((np.array(valid['y'])-np.array(forecast_valid)),2)))
rms
57.494461930575149
#plot
valid['Predictions'] = 0
valid['Predictions'] = forecast_valid.values
plt.plot(train['y'])
plt.plot(valid[['y', 'Predictions']])
推論
先知(像大多數時間序列預測技術一樣)試圖從過去的資料中捕捉趨勢和季節性。該模型通常在時間序列資料集上表現良好,但在本例中沒有達到預期效果。
事實證明,股票價格沒有特定的趨勢或季節性。價格的漲跌很大程度上取決於目前市場上的情況。因此,像ARIMA、SARIMA和Prophet這樣的預測技術並不能很好地解決這個特殊的問題。
讓我們繼續嘗試另一種高階技術——長短時記憶網路(LSTM)。
7、長短期記憶網路(LSTM)
簡介
LSTM 演算法廣泛應用於序列預測問題中,並被證明是一種非常有效的方法。它們之所表現如此出色,是因為LSTM能夠儲存重要的既往資訊,並忽略不重要的資訊。
LSTM有三個門:
輸入門:輸入門將資訊新增到細胞狀態
遺忘門:它移除模型不再需要的資訊
輸出門:LSTM的輸出門選擇作為輸出的資訊
要更詳細地瞭解LSTM及其體系結構,可以閱讀下面的文章:
長短期記憶網路簡介
現在,讓我們將LSTM實現為一個黑盒,並檢查它在特定資料上的效能。
實現
#importing required libraries
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM
#creating 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'][i] = data['Date'][i]
new_data['Close'][i] = data['Close'][i]
#setting index
new_data.index = new_data.Date
new_data.drop('Date', axis=1, inplace=True)
#creating train and test sets
dataset = new_data.values
train = dataset[0:987,:]
valid = dataset[987:,:]
#converting dataset into x_train and y_train
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))
# create and fit the LSTM network
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)
#predicting 246 values, using past 60 from the train data
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
#for plotting
train = new_data[:987]
valid = new_data[987:]
valid['Predictions'] = closing_price
plt.plot(train['Close'])
plt.plot(valid[['Close','Predictions']])
推論
哇!LSTM輕鬆地超越了我們目前看到的任何演算法。LSTM模型可以對各種引數進行調優,如改變LSTM層數、增加dropout值或增加訓練迭代輪數(epoch)數。但LSTM的預測是否足以確定股票價格將上漲還是下跌?當然不行!
正如我在文章開頭提到的,股價受到公司新聞和其他因素的影響,如公司的非貨幣化或合併/分拆。還有一些無形的因素往往是無法事先預測的。
寫在最後
我在寫這些文章時意識到時間序列預測是一個非常有趣的領域。在社群中有觀點認為這是一個複雜的領域,雖然有一定道理,我還是想說一旦你掌握了基本的技巧,就不難理解它了。
我對LSTM如何處理其他時間序列的問題很感興趣,並鼓勵您自己也嘗試一下。如果你有任何問題,請在下面的留言部分與我聯絡。
原文連結:https://www.analyticsvidhya.com/blog/2018/10/predicting-stock-price-machine-learningnd-deep-learning-techniques-python/