Python替你预测股票!让你在股市叱咤风云

作为一种技术手段,预测在金融、证券领域的应用非常广泛,尤其是对股票价格的预测。我们介绍一下获得股票数据的方法,并基于此对数据进行预处理,接着使用数据分析方法,建立基础特征,进一步构建预测模型,且基于新数据验证模型效果。拟使用VAR及LSTM两种算法建立预测模型。

欢迎加群交流:1141249762(群主处可领取Python免费学习资料)

获取股票数据

股票数据通常可从新浪股票、雅虎股票等网页上获取,此外还有一些炒股软件,如同花顺、通达信等都提供了非常清楚的股票数据展示和图表呈现。如果要获得实时的股票数据,可以考虑使用新浪股票提供的接口获取数据。以大秦铁路(股票代码:601006)为例,如果要获取它的最新行情,只需访问新浪的股票数据接口(具体可以百度),该接口会返回一串文本,例如:

1varhq_str_sh601006="大秦铁路,6.980,6.960,7.010,7.070,6.950,7.010,7.020,121033256,847861533.000,18900, 7.010,214867,7.000,66500,6.990,386166,6.980,336728,6.970,273750,7.020,836066,7.030,630800,7.040,936306,7.050,579400,7.060,2016-03-18,15:00:00,00";

这个字符串由许多数据拼接在一起,不同含义的数据用逗号隔开了,按照程序员的思路,顺序号从0开始。

0:<大秦铁路>,股票名字1:<< span="">6.980>,今日开盘价2:<< span="">6.960>,昨日收盘价3:<< span="">7.010>,当前价格4:<< span="">7.070>,今日最高价5:<< span="">6.950>,今日最低价6:<< span="">7.010>,竞买价,即“买一”报价7:<< span="">7.020>,竞卖价,即“卖一”报价8:<< span="">121033256>,成交的股票数,由于股票交易以一百股为基本单位,所以在使用时,通常把该值除以一百9:<< span="">847861533.000>,成交金额,单位为“元”,为了一目了然,通常以“万元”为成交金额的单位,所以通常把该值除以一万10:<< span="">18900>,“买一”申请4695股,即47手11:<< span="">7.010>,“买一”报价12:<< span="">214867>,“买二”13:<< span="">7.000>,“买二”14:<< span="">66500>,“买三”15:<< span="">6.990>,“买三”16:<< span="">386166>,“买四”17:<< span="">6.980>,“买四”18:<< span="">336728>,“买五”19:<< span="">6.970>,“买五”20:<< span="">273750>,“卖一”申报3100股,即31手21:<< span="">7.020>,“卖一”报价(22,23),(24,25),(26,27),(28,29)分别为“卖二”至“卖四的情况”30:<< span="">2016-03-18>,日期31:<< span="">15:00:00>,时间

这个接口对于JavaScript程序非常方便,如果要查看该股票的日K线图,可访问新浪股票的K线图接口(具体可百度),便可得到日K线图。

日K线图

如果要查看该股票的分时线,可访问链接新浪股票的分时线图接口(具体可百度),便可得到分时线图。

分时线图

对于周K线和月K线的查询,可分别访问新浪股票的周K线图和月K线图的接口(具体可百度)。Python中我们可以使用pandas_datareader库来获取股票数据,默认是访问yahoofinance的数据,其中包括上证和深证的股票数据,还有港股数据,该库只能获取股票的历史交易记录信息:如最高价、最低价、开盘价、收盘价以及成交量,无法获取个股的分笔交易明细历史记录。上证代码是ss,深证代码是sz,港股代码是hk,比如茅台:6000519.ss,万科000002.sz,长江实业0001.hk。这里以贵州茅台股票为例,说明pandas_datareader库中股票数据的获取方法及简单的可视化,代码如下:

1importpandasaspd

2importpandas_datareader.dataasweb

3importdatetimeasdt

4data= web.DataReader('600519.ss','yahoo', dt.datetime(2019,8,1),dt.datetime(2019,8,31))

5data.head()

6High         Low        Open       Close    Volume   Adj Close

7# Date

8#2019-08-01977.000000953.020020976.51001959.2999883508952959.299988

9#2019-08-02957.979980943.000000944.00000954.4500123971940954.450012

10#2019-08-05954.000000940.000000945.00000942.4299933677431942.429993

11#2019-08-06948.000000923.799988931.00000946.2999884399116946.299988

12#2019-08-07955.530029945.000000949.50000945.0000002686998945.000000

13

14kldata=data.values[:,[2,3,1,0]] # 分别对应开盘价、收盘价、最低价和最高价

15from pyechartsimportoptionsasopts

16from pyecharts.chartsimportKline

17

18kobj = Kline().add_xaxis(data.index.strftime("%Y-%m-%d").tolist()).add_yaxis("贵州茅台-日K线图",kldata.tolist()).set_global_opts(

19yaxis_opts=opts.AxisOpts(is_scale=True),

20xaxis_opts=opts.AxisOpts(is_scale=True),

21title_opts=opts.TitleOpts(title=""))

22kobj.render()

贵州茅台股票日K线图如图:

为给定时间序列的财务图表,代码中对象data包含6个属性,依次为Open(开盘价)、High(最高价)、Low(最低价)、Close(收盘价)、Volume(成交量)、Adjusted(复权收盘价)。基于收盘价的重要性,可从收盘价的历史数据中分割训练集、验证集、测试集,使用适当的特征,建立预测模型,并实施预测。

基于VAR算法的预测

向量自回归(VAR)模型就是非结构化的多方程模型,它的核心思想不考虑经济理论,而直接考虑经济变量时间时序之间的关系,避开了结构建模方法中需要对系统中每个内生变量关于所有内生变量滞后值函数建模的问题,通常用来预测相关时间序列系统和研究随机扰动项对变量系统的动态影响。VAR模型类似联立方程,将多个变量包含在一个统一的模型中,共同利用多个变量信息,比起仅使用单一时间序列的ARIMA等模型,其涵盖的信息更加丰富,能更好地模拟现实经济体,因而用于预测时能够提供更加贴近现实的预测值。此处拟基于贵州茅台股票数据,建立VAR的预测模型。使用后30天的数据作为验证集,剩余的数据用于建立预测模型。本节从VAR模型的平稳性检验出发,依次完成VAR模型的定阶及建模预测,最终通过分析验证集上的准确率来评估预测效果。

1、平稳性检验

只有平稳的时间序列才能够直接建立VAR模型,因此在建立VAR模型之前,首先要对变量进行平稳性检验。通常可利用序列的自相关分析图来判断时间序列的平稳性,如果序列的自相关系数随着滞后阶数的增加很快趋于0,即落入随机区间,则序列是平稳的;反之,序列是不平稳的。另外,也可以对序列进行ADF检验来判断平稳性。对于不平稳的序列,需要进行差分运算,直到差分后的序列平稳后,才能建立VAR模型。此处首先提取用于建立预测模型的基础数据,并对其进行单位根检验,对应的Python代码如下:

1importstatsmodels.tsa.stattoolsasstat

2importpandas_datareader.dataasweb

3importdatetimeasdt

4importpandasaspd

5importnumpyasnp

6

7data= web.DataReader('600519.ss','yahoo', dt.datetime(2014,1,1),dt.datetime(2019,9,30))

8subdata =data.iloc[:-30,:4]

9for iinrange(4):

10pvalue = stat.adfuller(subdata.values[:,i],1)[1]

11print("指标 ",data.columns[i]," 单位根检验的p值为:",pvalue)

12# 指标  High  单位根检验的p值为:0.9955202280850401

13# 指标  Low  单位根检验的p值为:0.9942509439755689

14# 指标  Open  单位根检验的p值为:0.9938548193990323

15# 指标  Close  单位根检验的p值为:0.9950049124079876

可以看到,p值都大于0.01,因此都是不平稳序列。现对subdata进行1阶差分运算,并再次进行单位根检验,对应的Python代码如下:

1subdata_diff1= subdata.iloc[1:,:].values - subdata.iloc[:-1,:].values

2for i in range(4):

3pvalue = stat.adfuller(subdata_diff1[:,i],1)[1]

4print("指标 ",data.columns[i]," 单位根检验的p值为:",pvalue)

5# 指标  High  单位根检验的p值为:0.0

6# 指标  Low  单位根检验的p值为:0.0

7# 指标  Open  单位根检验的p值为:0.0

8# 指标  Close  单位根检验的p值为:0.0

如结果所示,对这4个指标的1阶差分单独进行单位根检验,其p值都不超过0.01,因此可以认为是平稳的。

2、VAR模型定阶

接下来就是为VAR模型定阶,可以让阶数从1逐渐增加,当AIC值尽量小时,可以确定最大滞后期。我们使用最小二乘法,求解每个方程的系数,并通过逐渐增加阶数,为模型定阶,Python代码如下:

1# 模型阶数从1开始逐一增加

2rows, cols = subdata_diff1.shape

3aicList = []

4lmList = []

5

6forpinrange(1,11):

7baseData = None

8foriinrange(p,rows):

9tmp_list = list(subdata_diff1[i,:]) + list(subdata_diff1[i-p:i].flatten())

10ifbaseData is None:

11baseData = [tmp_list]

12else:

13baseData = np.r_[baseData, [tmp_list]]

14X = np.c_[[1]*baseData.shape[0],baseData[:,cols:]]

15Y = baseData[:,0:cols]

16coefMatrix = np.matmul(np.matmul(np.linalg.inv(np.matmul(X.T,X)),X.T),Y)

17aic = np.log(np.linalg.det(np.cov(Y - np.matmul(X,coefMatrix),rowvar=False))) +2*(coefMatrix.shape[0]-1)**2*p/baseData.shape[0]

18aicList.append(aic)

19lmList.append(coefMatrix)

20

21#对比查看阶数和AIC

22pd.DataFrame({"P":range(1,11),"AIC":aicList})

23#   P   AIC

24#0113.580156

25#1213.312225

26#2313.543633

27#3414.266087

28#4515.512437

29#5617.539047

30#6720.457337

31#7824.385459

32#8929.438091

33#91035.785909

如上述代码所示,当p=2时,AIC值最小为13.312225。因此VAR模型定阶为2,并可从对象lmList[1]中获取各指标对应的线性模型。

3、预测及效果验证

基于lmList[1]中获取各指标对应的线性模型,对未来30期的数据进行预测,并与验证数据集进行比较分析,Python代码如下:

1p = np.argmin(aicList)+1

2n = rows

3preddf = None

4for iinrange(30):

5predData = list(subdata_diff1[n+i-p:n+i].flatten())

6predVals = np.matmul([1]+predData,lmList[p-1])

7# 使用逆差分运算,还原预测值

8predVals=data.iloc[n+i,:].values[:4]+predVals

9ifpreddfisNone:

10preddf = [predVals]

11else:

12preddf = np.r_[preddf, [predVals]]

13# 为subdata_diff1增加一条新记录

14subdata_diff1 = np.r_[subdata_diff1, [data.iloc[n+i+1,:].values[:4] -data.iloc[n+i,:].values[:4]]]

15

16#分析预测残差情况

17(np.abs(preddf -data.iloc[-30:data.shape[0],:4])/data.iloc[-30:data.shape[0],:4]).describe()

18#       High         Low         Open       Close

19# count30.00000030.00000030.00000030.000000

20# mean0.0100600.0093800.0056610.013739

21# std0.0085620.0099680.0065150.013674

22# min0.0014580.0001150.0001140.000130

23#25%0.0041460.0019500.0016530.002785

24#50%0.0071660.0071180.0029130.010414

25#75%0.0146520.0129990.0069330.022305

26# max0.0391910.0458020.0245760.052800

从上述代码第17行可以看出这4个指标的最大百分误差率分别为3.9191%、4.5802%、2.4576%、5.28%,最小百分误差率分别为0.1458%、0.0115%、0.0114%、0.013%,进一步,绘制二维图表观察预测数据与真实数据的逼近情况,Python代码如下:

1importmatplotlib.pyplotasplt

2plt.figure(figsize=(10,7))

3for iinrange(4):

4plt.subplot(2,2,i+1)

5plt.plot(range(30),data.iloc[-30:data.shape[0],i].values,'o-',c='black')

6plt.plot(range(30),preddf[:,i],'o--',c='gray')

7plt.ylim(1000,1200)

8plt.ylabel("$"+data.columns[i]+"$")

9plt.show()

10v =100*(1- np.sum(np.abs(preddf -data.iloc[-30:data.shape[0],:4]).values)/np.sum(data.iloc[-30:data.shape[0],:4].values))

11print("Evaluation on test data: accuracy = %0.2f%% \n"% v)

12# Evaluation on testdata: accuracy =99.03%

该预测效果如下图,其中黑色实线为真实数据,灰色虚线为预测数据,使用VAR模型进行预测的效果总体还是不错的,平均准确率为99.03%。针对多元时间序列的情况,VAR模型不仅考虑了其他指标的滞后影响,计算效率还比较高,从以上代码可以看到,对于模型的拟合,直接使用的最小二乘法,这增加了该模型的适应性。

预测效果

基于LSTM算法的预测

本节主要基于LSTM算法对贵州茅台股票数据进行预测,该算法非常擅长序列数据的建模,由于引入了遗忘门等更为复杂的内部处理单元来处理上下文信息的存储与更新,这样既可以消除梯度问题的困扰,也可以对存在短期或长期依赖的数据建模,该算法在文本、语音等序列数据模型中广泛使用。本节从LSTM建模的数据要求及网络结构设计讲起,通过设置合理的参数,通过训练得到模型,并基于该模型进行预测,最后将结果与真实数据进行比较,评估预测效果。

1、数据要求

本节使用LSTM算法对贵州茅台股票数据进行预测,可基于前N条样本对当前样本进行预测,因此该模型不需要像DNN那样,将历史数据进行复杂转换,将基础数据稍加处理就能用于训练模型。对基础数据的处理即为对该数据进行重新封装,将样本前N期的集合与当前样本对应上,分别得到训练数据的输入与输出。

所示数据对应关系(具体数据为示意)

2、数据预处理

首先,需要将基础数据重构为包含历史3周特征数据的基础数据,以预测日的High(最高价)、Low(最低价)、Open(开盘价)、Close(收盘价)4个指标作为输出数据。这里我们使用2014年1月1日至2019年8月31日的贵州茅台股票数据作为训练数据,使用2019年整个9月的数据作为测试数据,来验证模型效果。用Python将对全体数据进行标准化,并将基础数据的特征进行重构,代码如下:

1SEQLEN=21

2dim_in=4

3dim_out=4

4pred_len=30

5vmean=data.iloc[:,:4].apply(lambdax:np.mean(x))

6vstd=data.iloc[:,:4].apply(lambdax:np.std(x))

7t0=data.iloc[:,:4].apply(lambdax:(x-np.mean(x))/np.std(x)).values

8X_train=np.zeros((t0.shape[0]-SEQLEN-pred_len,SEQLEN,dim_in))

9Y_train=np.zeros((t0.shape[0]-SEQLEN-pred_len,dim_out),)

10X_test=np.zeros((pred_len,SEQLEN,dim_in))

11Y_test=np.zeros((pred_len,dim_out),)

12foriinrange(SEQLEN,t0.shape[0]-pred_len):

13Y_train[i-SEQLEN]=t0[i]

14X_train[i-SEQLEN]=t0[(i-SEQLEN):i]

15foriinrange(t0.shape[0]-pred_len,t0.shape[0]):

16Y_test[i-t0.shape[0]+pred_len]=t0[i]

17X_test[i-t0.shape[0]+pred_len]=t0[(i-SEQLEN):i]

如上述代码所示,SEQLEN表示使用前期数据的长度,dim_in表示输入数据的维度,dim_out表示输出数据的维度,pred_len表示预测数据的长度。第5~7行代码对数据进行zscore标准化,将数据映射到标准正态分布。第12~17行代码对基础数据进行重构,分别得到训练数据X_train、Y_train以及测试数据X_test、Y_test。

3、网络结构设计

经尝试,我们使用近3周的历史数据来训练LSTM模型,同时,设置隐含层神经元的数量为64。因此,我们可以将LSTM神经网络按下面的结构进行设计(图中N可取21,即3周对应的天数)。

LSTM神经网络结构

4、建立模型

现基于Keras搭建LSTM神经网络,并基于训练集对模型进行训练,Python代码如下:

1fromkeras.layersimportLSTM, Dense

2fromkeras.modelsimportSequential

3model = Sequential()

4model.add(LSTM(64, input_shape=(SEQLEN, dim_in),activation='relu',recurrent_dropout=0.01))

5model.add(Dense(dim_out,activation='linear'))

6model.compile(loss ='mean_squared_error', optimizer ='rmsprop')

7history = model.fit(X_train, Y_train, epochs=200, batch_size=10, validation_split=0)

8# Epoch 1/200

9# 1350/1350 [==============================] - 1s 1ms/step - loss: 0.0447

10# Epoch 2/200

11# 1350/1350 [==============================] - 1s 737us/step - loss: 0.0059

12# Epoch 3/200

13# 1350/1350 [==============================] - 1s 743us/step - loss: 0.0043

14# ......

15# Epoch 200/200

16# 1350/1350 [==============================] - 1s 821us/step - loss: 9.2794e-04

如上述代码所示,我们使用rmsprop算法来优化模型。由于当前的建模场景是数值预测,因此使用MSE(均方误差)来定义损失函数。算法经过200次迭代,loss从0.0447降到了9.2794e-04。我们可以基于得到的模型进行进一步预测。

5、预测实现

基于上文得到的模型,进一步编写Python代码,对X_test对应的输出数据进行预测。需要注意的是,直接得到的预测结果是处于标准化的数据空间中的,需要将其还原成原始数据空间的值,结果才有意义。对应的Python代码如下:

1preddf=model.predict(X_test)*vstd.values+vmean.values

如上述代码所示,将模型的预测结果pred_y乘以vstd再加上vmean,即可对数据进行还原。preddf即是最终得到的预测数据,可打印其值,代码如下:

1preddf

2#array([[1069.35781887, 1038.57915742, 1056.77147186, 1053.83827734],

3#[1070.65142282, 1039.58533719, 1057.34561875, 1054.85567074],

4#[1083.58529328, 1052.70457308, 1070.78824637, 1067.49741882],

5#

6#[1186.19297789, 1161.52758381, 1172.33666591, 1170.44623263],

7#[1181.42680223, 1155.14778501, 1166.5726204 , 1165.00336968],

8#[1186.75600881, 1160.84733425, 1172.37636963, 1170.09819923]])

9

10preddf.shape

11# (30, 4)

如上述代码所示,preddf是一个的二维数据,包含了2019年9月整月的预测结果。

6、效果评估

对贵州茅台股票数据预测的效果评估可以采用两种方法。一种方法是对预测的结果与真实结果进行绘图比较,通过直观观察可以知道预测效果,如果预测曲线与真实曲线完全重合或相当接近,则说明预测效果较好;反之,则说明预测模型还需要改进。另一种方法是基于贵州茅台股票数据预测的误差累计值来计算一个误差率,从而得到平均精度水平,该值越大说明整体预测效果也就越好,该值越小说明预测模型还存在优化空间。编写Python代码,同时实现预测结果与真实数据的对比图,以及计算累计误差,从而全面地评估预测效果,代码如下:

1importmatplotlib.pyplotasplt

2plt.figure(figsize=(10,7))

3for iinrange(4):

4plt.subplot(2,2,i+1)

5plt.plot(range(30),data.iloc[-30:data.shape[0],i].values,'o-',c='black')

6plt.plot(range(30),preddf[:,i],'o--',c='gray')

7plt.ylim(1000,1200)

8plt.ylabel("$"+data.columns[i]+"$")

9plt.show()

10v =100*(1- np.sum(np.abs(preddf -data.iloc[-30:data.shape[0],:4]).values)/np.sum (data.iloc[-30:data.shape[0],:4].values))

11print("Evaluation on test data: accuracy = %0.2f%% \n"% v)

12# Evaluation on testdata: accuracy =99.01%

预测评估对比图如下。

我们可以看到,黑色实线为真实数据,灰色虚线为预测数据,横坐标为日期下标,纵坐标为对应的股票价格。使用LSTM模型进行预测的效果总体还是不错的,平均准确率为99.01%。对于多元时间序列数据,可尝试使用LSTM模型,该模型能够记忆历史较长的重要信息,可有效识别历史数据中存在的规律和模式,如今广泛应用于包含大量序列数据的场景中。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350