1. 思维导图
2. 获取网易股票数据
2.1 模块导入
import pandas_datareader.data as web
import datetime as dt
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from matplotlib.pylab import style
style.use('ggplot') # 图例风格
plt.rcParams['font.sans-serif']=['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus']='False' # 显示负号
from statsmodels.graphics.tsaplots import plot_acf,plot_pacf
import itertools
import pandas as pd
import numpy as np
import statsmodels.api as sm
from statsmodels.tsa.arima_model import ARIMA
2.2 获取数据
# 获取数据起始时间
start=dt.date(2000,1,1)
# 获取数据结束时间
end=dt.date.today()
# 获取网易2017年1月1日至今的股票数据
stock=web.DataReader('NTES','yahoo',start,end)
- 查看Pandas的操作文档可以发现,第一个参数为股票代码,苹果公司的代码为"AAPL",国内股市采用的输入方式“股票代码”+“对应股市”,上证股票在股票代码后面加上“.SS”,深圳股票在股票代码后面加上“.SZ”。DataReader可从多个金融网站获取到股票数据,如“Yahoo! Finance” 、“Google Finance”等,这里以Yahoo为例。第三、四个参数为股票数据的起始时间断。返回的数据格式为DataFrame。
- 这里我获取的是2000年6月30日至今网易的股票数据。
2.3 观察数据
stock
3. 数据重采样(降采样)及观察数据平稳性
stock_week=stock['Close'].resample('W-MON').mean()
stock_train=stock_week['2000':'2018'] # 这里索引的2018年是2018年整年,包含12月
- 对close特征进行预测,且对date特征进行降采样,以周为单位,且从周一开始算起
- 划分数据集为stock_train
stock_train.plot(figsize=(12,8))
plt.legend() # 显示标签
plt.title('Stock_train Close')
sns.despine() # 隐藏右边和上边的边框线
- 从上述图例的分布趋势来看,误差较大,数据不平稳,所以考虑差分。
4. 一阶差分
stock_diff1=stock_train.diff() # 减去前一行数据,相当于原数据向下移动1行
stock_diff1=stock_diff1.dropna() # 删除nan值
# 画图
plt.figure()
plt.plot(stock_diff1)
plt.title('一阶差分')
- 从一阶差分来看,差分数据平稳性已经很好了,所以这里不用再做二阶差分。d=1
5. 为ARIMA模型寻找合适的自相关函数ACF和偏相关函数PACF
5.1 ACF
acf=plot_acf(stock_diff1,lags=20) # lags:延迟值的整型或数组,用于水平轴上。个人理解是横坐标点的个数
plt.title('ACF')
5.2 PACF
pacf=plot_pacf(stock_diff1,lags=20)
plt.title('PACF')
- 对于P值,ACF衰减趋近于0,PACF p阶后截尾。
- 对于q值,PACF衰减趋近于0,ACF q阶后截尾。
- 所以综合来看,p=3,q=1。
6. 也可以一起展现在一个画布上
也可以将这些图例封装成函数,展现在一个画布上:
def stockplot(y,lags=None,title='',figsize=(14,8)):
fig=plt.figure(figsize=figsize)
layout=(2,2)
stock_ax=plt.subplot2grid(layout,(0,0))
hist_ax=plt.subplot2grid(layout,(0,1))
acf_ax=plt.subplot2grid(layout,(1,0))
pacf_ax=plt.subplot2grid(layout,(1,1))
# 画图1
y.plot(ax=stock_ax)
stock_ax.set_title(title)
# 画图2
y.plot(ax=hist_ax,kind='hist',bins=25)
hist_ax.set_title('Histogram')
# 画图3,4
plot_acf(y,lags=lags,ax=acf_ax)
plot_pacf(y,lags=lags,ax=pacf_ax)
[ax.set_xlim(0) for ax in [acf_ax,pacf_ax]]
sns.despine()
fig.tight_layout() # 自动调整子图参数,使之自动填充整个图像区域
return stock_ax,acf_ax,pacf_ax
stockplot(stock_diff1,title='A Given Training Series',lags=20)
7. 其他方法求p、d、q值
7.1 热力图
p_min=0
d_min=1
q_min=0
p_max=4
d_max=1
q_max=4
results_bic=pd.DataFrame(index=['AR{}'.format(i) for i in range(p_min,p_max+1)],
columns=['MA{}'.format(i) for i in range(q_min,q_max+1)])
for p,d,q in itertools.product(range(p_min,p_max+1),
range(d_min,d_max+1),
range(q_min,q_max+1)): # 各自排列组合
if p==0 and d==0 and q==0:
results_bic.loc['AR{}'.format(p),'MA{}'.format(q)]=np.nan
continue
try:
model=sm.tsa.SARIMAX(stock_train,order=(p,d,q))
results=model.fit()
results_bic.loc['AR{}'.format(p),'MA{}'.format(q)]=results.bic
except:
continue
results_bic=results_bic.astype(float)
plt.figure(figsize=(10,8))
ax=sns.heatmap(results_bic,
mask=results_bic.isnull(),
annot=True,
fmt='.2f'
)
ax.set_title('BIC')
上图是自回归AR和移动平均MR的热力图,热力图的值越小越好,所以:
- AR(p)=0
- MA(q)=1
7.2 通过AIC、BIC
train_results=sm.tsa.arma_order_select_ic(stock_diff1,ic=['aic','bic'],trend='nc',max_ar=4,max_ma=4)
print('AIC',train_results.aic_min_order)
print('BIC',train_results.bic_min_order)
目前p和q值的取值有(d=1):
0 1
3 1
4 2
8. 寻找最合适的p、d、q值
分别对这几种值观察QQ图线性、正态分布和残差分析。
8.1 p=0,q=1
# 残差分析 正态分布 QQ图线性
arima011=sm.tsa.SARIMAX(stock_train,order=(0,1,1))
model_results011=arima011.fit()
model_results011.plot_diagnostics(figsize=(16, 12));
8.2 p=3,q=1
arima311=sm.tsa.SARIMAX(stock_train,order=(3,1,1))
model_results311=arima311.fit()
model_results311.plot_diagnostics(figsize=(16, 12));
8.3 p=4,q=2
arima412=sm.tsa.SARIMAX(stock_train,order=(4,1,2))
model_results412=arima412.fit()
model_results412.plot_diagnostics(figsize=(16, 12));
- 综合来看,p=3,d=1,q=1或者p=4,d=1,q=2最好。
9. 模型预测
model_stock=ARIMA(stock_train,order=(4,1,2),freq='W-MON')
result=model_stock.fit()
pred=result.predict('2018','2020-06-29',dynamic=True,typ='levels')
print(pred)
plt.figure(figsize=(12,8))
plt.xticks(rotation=45)
plt.plot(pred)
plt.plot(stock_train)
- 蓝色曲线即训练集的时间为2000年6月至2018年12月,红色曲线即预测的时间为2018年1月至今,从上图可以看到,红色曲线经过3次小波动下降后呈上升趋势。
同时我们可以根据实际数据去验证预测结果:
plt.figure(figsize=(12,8))
plt.xticks(rotation=45)
plt.plot(pred)
plt.plot(stock_week)
- 这里蓝色的曲线代表2000年6月至今的股票close股价值,可以看到蓝色曲线基本也是在3-4次小幅度下降后升上升趋势。
10. 总结
- 从上图预测的结果以及实际数据的结果对比来看,趋势的预测效果还是比较好的,close值的差异较大。
- 尽管股票预测值和真实值是有差异的,但是预测值的趋势和真实值的趋势是一样的。
- 趋势的差异其实受数据集是否有很强的规律性有关,这里虽然曲线预测的趋势和实际数据的趋势惊人的相似,但是我也试验过修改训练集的时间范围和预测集的时间范围来做,更多地是一条直线而不是像上图所示的曲线,所以也证实了数据集规律性相对并不是很强,而且我也在其他文章作者提到过预测的时间范围不适宜过大。
- 目前股票预测很难做到一个比较精准的,其实大部分都是做这样一个趋势预测。