预测股市的走势是最困难的事情之一。影响预测的因素不少 - 包括物理因素与心理因素,理性行为和非理性行为等。全部这些因素结合在一块儿共同致使股价波动,很难以高精度预测。python
咱们是否能够将机器学习做为该领域的游戏规则改变者吗?利用一些特性,好比关于一个组织的最新公告,他们的季度收入结果等功能,机器学习技术有可能发掘出咱们之前没有看到的模式和看法,而且能够用来作出准确无误的预测。算法
在本文中,咱们将使用有关上市公司股票价格的历史数据。咱们将使用多种机器学习算法来预测该公司的将来股票价格,从平均算法和线性回归等简单的算法开始,而后转向Auto ARIMA和LSTM等高级技术。网络
本文背后的核心思想是展现如何实现这些算法。我将简要介绍该技术并提供相关连接,以便在必要时了解这些概念。app
咱们很快就会深刻到本文的实现部分,但首先要肯定咱们要解决的问题。从广义上讲,股票市场分析分为两部分 - 基础分析和技术分析。机器学习
你可能已经猜到了,咱们的重点将放在技术分析部分。咱们将使用Quandl的数据集(你能够在这里查找各类股票的历史数据),对于这个特定的项目,我使用了“ 塔塔全球饮料 ” 的数据。是时候让咱们动起来了!性能
注意:有关文章的数据集我将在文章最后放出学习
首先咱们先加载数据集,定义问题的目标变量:测试
import pandas as pd
import numpy as npspa
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,totaltradequantity和turnover。
另外一个须要注意的重要事项是,市场在周末和公共假期关闭。再次注意上表,一些日期值缺失 - 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....
在接下来的部分中,咱们将探索这些变量,并使用不一样的技术来预测股票的每日收盘价。
“平均”很容易成为咱们平常生活中最经常使用的词汇之一。例如,计算平均分来肯定总体性能,或者找出过去几天的平均温度以了解今天的温度 - 这些都是咱们常常作的例行工做。所以,这是一个很好的起点,能够用于咱们的数据集进行预测。
天天的预计收盘价将是一组先前观测值的平均值。咱们将使用移动平均技术而不是使用简单平均值,该技术为每一个预测使用最新的一组值。换句话说,对于每一个后续步骤,在从集合中移除最老的观测值的同时考虑预测值。下面是一个简单的图形,能够帮助你更清晰地理解这一点。
咱们将在咱们的数据集上实现此技术。第一步是建立一个仅包含Date和Close price列的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)
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,并了解它们在咱们股票市场数据上的表现。
能够在此数据上实现的最基本的机器学习算法是线性回归。线性回归模型返回一个肯定自变量和因变量之间关系的方程。
线性回归的方程能够写成:
这里,x1,x2,... .Xň表明独立变量,而系数θ1,θ2,...θÑ表示的权重。
对于咱们的问题描述,咱们没有一组自变量。咱们只有日期而已。让咱们使用日期列来提取诸如 - 日,月,年,星期一/星期五等特征,而后拟合线性回归模型。
咱们将首先按升序对数据集进行排序,而后建立一个单独的数据集,以便建立的任何新要素都不会影响原始数据。
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’, ‘Ismonthend’, ‘Ismonthstart’, ‘Isquarterend’, ‘Isquarterstart’, ‘Isyearend’, and ‘Isyearstart’.
注意:我使用了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)
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月的状况相同。线性回归技术能够很好地解决诸如大型超市的销售问题,在这些问题中独立特征对于肯定目标值是有用的。
这里可使用的另外一个有趣的ML算法是KNN(K近邻)。KNN基于自变量找到新数据点和旧数据点之间的类似性。让我用一个简单的例子解释一下。
考虑11我的的身高和年龄。根据给定的特征('年龄Age'和'身高Height'),表格能够用图形格式表示,以下所示:
为了肯定ID#11的权重,K-NN考虑该ID的最近邻的权重。ID#11的权重预计是其邻居的平均值。若是咱们如今考虑三个邻居(k = 3),ID#11的权重将是=(77 + 72 + 60)/ 3 = 69.66千克。
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)
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)
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月的降低,由于这是过去几年的形式。咱们能够有把握地说,回归算法在这个数据集上表现不佳。
让咱们继续看看一些时间序列预测技术,以了解它们在面对股票价格预测挑战时的表现。
ARIMA是一种很是流行的时间序列预测统计方法。ARIMA模型考虑了过去的值来预测将来的价值。ARIMA有三个重要参数:
ARIMA的参数调整会消耗大量时间。所以,咱们将使用auto ARIMA,它自动选择(p,q,d)提供最小错误的的最佳组合。
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模型使用过去的数据来理解时间序列中的模式。使用这些值,模型得到了该系列中的增加趋势。虽然使用这种技术的预测远比先前实现的机器学习模型的预测好,但这些预测仍然没有接近实际值。
从图中能够看出,该模型已经捕捉到了该系列中的一个趋势,但并无关注季节性部分。在下一节中,咱们将实现一个时间序列模型,而这个模型考虑了系列的趋势和季节性。
有许多时间序列技术能够在股票预测数据集上实现,可是大多数这些技术在拟合模型以前须要大量的数据预处理。由Facebook设计和开创的Prophet是一个时间序列预测库,它不须要数据预处理,实现起来也很是简单。Prophet的输入是一个包含两列的数据框:date和target(ds和y)。
Prophet试图获取过去数据中的季节性,并在数据集很大时能够进行很好的工做。
from fbprophet import Prophet
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)
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)。
LSTM普遍用于序列预测问题,而且已被证实是很是有效的。他们很是有效的缘由是由于LSTM可以存储过去重要的信息,并忘记不重要的信息。LSTM有三个门:
如今,让咱们将LSTM实现为一个黑盒子,并检查它在咱们的特定数据上的性能。
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM
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:,:]
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))
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)
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)