基于LSTM算法的股票预测详解

一、需求

给定几个已知的股市因素(开盘、收盘、最高、最低、成交量、成交额)及各因素对应的大量数据,训练一个该股票的涨跌趋势的预测模型。并在给定的测试数据的条件下求出接下来的涨跌趋势。即得到下图中的label值。-1代表跌、1代表涨。
在这里插入图片描述

二、分析

1、LSTM简单介绍

LSTM这个算法是专门训练有时间序列信息的数据的,即这些数据不仅按照时间递增的顺序排布,并且前后的数据都有着很强的联系。个人认为与马尔可夫的思想差不多,即后面的值由前面的值来决定。本次需求是要根据已知的股市数据来分析某个时间段的涨跌趋势,并预测接下来该股票是涨还是跌,但是这里的股票预测存在一些问题:
(1)股票的涨跌不仅仅依赖上图中所给的几个因素,还有很多很多,所以预测的结果会有些偏差。
(2)LSTM算法本身就存在一个只考虑整体和前置信息的算法,没有考虑一些突发事件(比如马斯克宣布比特币不能购买特斯拉之后,比特币暴跌。。。)造成的偏差。
(3)LSTM算法默认所有的因素互相都是正相关的,比如A因素值在接下来一段时间内增加,B减少,如果将这两个因素结合起来训练的话得到的效果还不如只考虑一个A因素得到的效果好。(这里不是空说,我将每个因素都带入到网络中进行了训练和测试,得到的结果差异很大,就是说得到的模型预测趋势和测试数据完全不一样)

但是。。。这个算法还是更够以一个稍微大的概率预测股票趋势的。

2、代码

代码包分为几个部分:
1、run.py:主要运行代码
2、core.model.py:主要模型代码
3、core.dataprocessor.py:数据处理代码
4、config.json:配置文件

本代码需要的修改地方其实只是配置文件中的一些参数,参数的意思我在下面代码里作了标注。

{
	"data": {
		"filename": "StockData.csv", //这是保存数据集地址
		"columns": [

			"close_1500"//这里是数据集中决定股票涨跌的因素,可以写多个因素,因素之间用逗号相隔。
						//这里改完之后要把下面的imput_dim改成因素的个数

		],
		"sequence_length": 50, //步长,就是处理数据的时候按照步长50来取值,比如100条数据的话,就为成两个50条进行处理。
		"train_test_split": 0.5,//将数据集按照0.5的比例分为训练集和测试集
		"normalise": true //是否归一化
	},
	"training": {
		"epochs":1,
		"batch_size": 32   //训练的时候一次性加载的数据,可以大一点
	},
	"model": {
		"loss": "mse",
		"optimizer": "adam",
		"save_dir": "saved_models",
		"layers": [
			{
				"type": "lstm",
				"neurons": 100,
				"input_timesteps": 49,//这里就是上面的步长-1,输入到lstm中
				"input_dim": 1,
				"return_seq": true
			},
			{
				"type": "dropout",
				"rate": 0.2
			},
			{
				"type": "lstm",
				"neurons": 100,
				"return_seq": true
			},
			{
				"type": "lstm",
				"neurons": 100,
				"return_seq": false
			},
			{
				"type": "dropout",
				"rate": 0.2
			},
			{
				"type": "dense",
				"neurons": 1,
				"activation": "linear"
			}
		]
	}
}

配置好参数后,接下来就是运行run.py
这里面有三个函数需要着重解释一下:

predictions_multiseq  = model.predict_sequences_multiple(x_test, configs['data']['sequence_length'], configs['data']['sequence_length'])
    predictions_fullseq  = model.predict_sequence_full(x_test, configs['data']['sequence_length'])
    predictions_pointbypoint = model.predict_point_by_point(x_test)

(1)model.predict_point_by_point:单点预测
效果如下:
在这里插入图片描述
这个函数代码如下:

	def predict_point_by_point(self, data):
		#Predict each timestep given the last sequence of true data, in effect only predicting 1 step ahead each time
		#预测给定的最后一个真实数据序列的每个时间步,实际上每次只预测1步
		print('[Model] Predicting Point-by-Point...')
		predicted = self.model.predict(data)
		predicted = np.reshape(predicted, (predicted.size,))
		return predicted

data是测试数据,将测试数据直接代入模型中得到预测值,每次只预测一个值,也就是它只根据前一条数据来预测后一条数据,由于股票的数据本就是因为认为因素而瞬息万变的,所以这样的预测方式完全没有任何的参考价值。预测股票趋势的时候如果只按照一个值进行预测,得到的会是一个非常不确定的预测趋势。

(2)predictions_fullseq:全局预测

	def predict_sequence_full(self, data, window_size):
		#Shift the window by 1 new prediction each time, re-run predictions on new window
		#每次移动窗口1个新的预测,重新运行新窗口的预测
		print('[Model] Predicting Sequences Full...')
		curr_frame = data[0]
		predicted = []
		for i in range(len(data)):
			predicted.append(self.model.predict(curr_frame[newaxis,:,:])[0,0])
			curr_frame = curr_frame[1:]
			curr_frame = np.insert(curr_frame, [window_size-2], predicted[-1], axis=0)
		return predicted

这里的window_size就是步长,data是测试数据。
predicted.append(self.model.predict(curr_frame[newaxis,:,:])[0,0])
作用就是将测试整体数据都代入到模型中得到一个预测值,将这个预测值加到列表predicted中,然后下面这个函数
curr_frame = np.insert(curr_frame, [window_size-2], predicted[-1], axis=0)
作用就是把预测值列表中的最后一个值赋值给curr_frame的最后一条数据的所有维度(维度就是你一开在配置文件中设置的input_dim)。
例如:
predicted=[1,2,3,4]
curr_frame=[[5,6,7,8][9,10,11,12]]变成了[[9,10,11,12][4,4,4,4]]
这样的话原始的curr_frame就有了前面的预测信息4,依次类推
下一个predicted增加之后如下:
predicted=[1,2,3,4,5]
curr_frame=[[5,6,7,8][9,10,11,12]]变成了[[4,4,4,4][5,5,5,5]



这样进行下去之后,所有的curr_frame的各维度信息都一样了,预测曲线也就趋于平均值了。
在这里插入图片描述

综上
本函数的作用就是把所有的测试数据都丢到模型中预测出一个值,然后用这一个值来更新原来的测试数据,以此类推,循环进行len(data)次,即可得到全局的涨跌趋势,当然,结果表明,很差劲!!!

**(3)predict_sequences_multiple:**多序列预测

	def predict_sequences_multiple(self, data, window_size, prediction_len):
		#Predict sequence of 50 steps before shifting prediction run forward by 50 steps
		#预测顺序50步之前,移动预测运行前50步
		print('[Model] Predicting Sequences Multiple...')
		prediction_seqs = []
		for i in range(int(len(data)/prediction_len)):
			curr_frame = data[i*prediction_len]
			predicted = []
			for j in range(prediction_len):
				predicted.append(self.model.predict(curr_frame[newaxis,:,:])[0,0])
				curr_frame = curr_frame[1:]
				curr_frame = np.insert(curr_frame, [window_size-2], predicted[-1], axis=0)
			prediction_seqs.append(predicted)
		return prediction_seqs

这里相比较于(2)全局检测,多了一部分割,即先将所有的测试数据平均分成若干段,每一段的长度就是配置文件中设置的步长。每一段采用的方法与(2)中一样,最终这样的检测方式就可以捕获到局部和全局的趋势(单点检测只根据一条数据来预测,过于偏向局部;全局检测根据所有的数据来预测,过于偏向全局)

该方法效果如下:
在这里插入图片描述
上图中蓝色线是测试数据中第一个因素的数值图。彩色的每一段是预测的趋势,即当曲线向上表示这一段区间之后的股票可能会涨,曲线向下表示这一区间之后的股票可能会跌,当然,曲线的幅度也决定了涨跌的幅度。还有一个要强调的是,这个模型的分段预测曲线只能预测接下来一小段时间内的涨跌情况,不能够预测的很长远,也不能预测某一个值

备注:
(1)如果我给定了一些数据,怎么预测涨跌?
首先模型训练前要确定好步长,步长决定了你需要输入的数据数。比如你模型的步长是50,那么你在测试的时候输入的数据数必须要大于50(否则无法代入到lstm模型中,因为这个模型对输入的数据有要求,可在配置文件的layers中进行查看)。这样才能在图中显示出涨跌曲线,这些涨跌曲线不是任何的因素值(不是钱也不是交易额),而是一个抽象的描述各股票决定因素之间关系的量。
(2)表述可能不太清楚,有疑问请在评论区说明,我会补充到备注里。

评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值