Python预测基金净值:LSTM模型有点香

  • 搭建LSTM神经网络预测基金净值
    • 一、LSTM简述
    • 二、继续爬基金,生成训练集、验证集、测试集文件
    • 三、建模,读入数据,训练,验证
    • 四、看图
  • 总结
  • 后记

搭建LSTM神经网络预测基金净值

上一篇《Python预测基金净值:keras神经网络》搭建了一个简单神经网络用来预测基金净值。

最后得出什么结论?
有点臭。

看看训练情况:
Epoch 1/100
156/156 [= = = = =] - 0s 3ms/step - loss: 0.0450 - val_loss: 0.1114

Epoch 100/100
156/156 [= = = = =] - 0s 2ms/step - loss: 0.0299 - val_loss: 0.1056

经过100次训练,训练集的loss已经从0.0450降到0.0299,但是验证集的val_loss几乎么有改善(说明训练集的“改善”只是过拟合)。这对于一个神经网络而言,相当于死刑判决:模型或参数有大问题。

事实上,对于股票/指数/基金净值这些“有序”数据而言,应该采用循环神经网络来搭建,本篇就此试一试。

初步结论是:有点香。

一、LSTM简述

循环神经网络(Recurrent Neural Network, RNN)是以序列数据为输入,在序列的演进方向进行循环递归的递归神经网络。RNN适用于处理时间顺序、逻辑顺序或其他序列特性顺序的数据。

RNN的单元结构如下图(左)所示,上一时刻输出ht-1与当前时刻输入Xt进行拼接,然后由神经网络(以tanh为激活函数)进行处理,得到输出ht。


后续研究发现RNN存在“长期依赖问题”(long-term dependencies problem),亦即在学习过程中,会出现梯度消失或梯度爆炸现象。

LSTM比较好地解决了这个问题,其单元结构如上图(中)所示,增加了一个t时刻的细胞状态Ct,并且增加了三个门(忘记门、输入门、输出门),即图中标注为σ(sigma)的3个以sigmoid作为激活函数的神经网络。如果去掉Ct和三个门,如上图(右)所示,则与上图(左)的RNN等效。

关于LSTM,可参见:理解 LSTM 网络 by 朱小虎

关于keras,可参见:keras中文文档

二、继续爬基金,生成训练集、验证集、测试集文件

import requests
import time
import execjsfileTrain = './data/accTrain.csv'
jjTrain = ['004609', '004853', '005524', '005824', '007749']
fileTest = './data/accTest.csv'
jjTest = '007669'def getUrl(fscode):head = 'http://fund.eastmoney.com/pingzhongdata/'tail = '.js?v='+ time.strftime("%Y%m%d%H%M%S",time.localtime())return head+fscode+tail# 根据基金代码获取净值
def getWorth(fscode):content = requests.get(getUrl(fscode))jsContent = execjs.compile(content.text)#累计净值走势ACWorthTrend = jsContent.eval('Data_ACWorthTrend')ACWorth = []for dayACWorth in ACWorthTrend:ACWorth.append(dayACWorth[1])return ACWorthACWorthFile = open(fileTrain, 'w')
for code in jjTrain:try:ACWorth = getWorth(code)except:continue    if len(ACWorth) > 0:ACWorthFile.write(",".join(list(map(str, ACWorth))))ACWorthFile.write("\n")print('{} data downloaded'.format(code))
ACWorthFile.close()ACWorthTestFile = open(fileTest, 'w')
ACWorth = getWorth(jjTest)
if len(ACWorth) > 0:ACWorthTestFile.write(",".join(list(map(str, ACWorth))))ACWorthTestFile.write("\n")print('{} data downloaded'.format(jjTest))
ACWorthTestFile.close()

如上一篇所述,‘004609’, ‘004853’, ‘005524’, ‘005824’, '007749’是5只目前收益较稳定的偏债型混合基金。爬取每日净值数据,作为训练集和验证集(通过设置validation_split=0.25)的数据文件。'007669’也是一只同类型的基金,上一篇没选它,是因为它目前在支付宝基金里面暂停代购。这次用作测试集的数据文件。

注意,和上一篇相比,代码改动了一点:基金数据按日期正序保存。

三、建模,读入数据,训练,验证

import numpy as np
import pandas as pd
import csv
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from matplotlib import pyplot as pltplt.rcParams['font.sans-serif']='SimHei'
plt.rcParams['axes.unicode_minus']=Falsebatch_size = 4
epochs = 50
time_step = 6 #用多少组天数进行预测
input_size = 6 #每组天数,亦即预测天数
look_back = time_step * input_size
showdays = 120 #最后画图观察的天数(测试天数)X_train = []
y_train = []
X_validation = []
y_validation = []
testset = [] #用来保存测试基金的近期净值#忽略掉最近的forget_days天数据(回退天数,用于预测的复盘)
forget_days = 0def create_dataset(dataset):dataX, dataY = [], []print('len of dataset: {}'.format(len(dataset)))for i in range(0, len(dataset) - look_back, input_size):x = dataset[i: i + look_back]dataX.append(x)y = dataset[i + look_back: i + look_back + input_size]dataY.append(y)return np.array(dataX), np.array(dataY)def build_model():model = Sequential()model.add(LSTM(units=128, input_shape=(time_step, input_size)))model.add(Dense(units=input_size))model.compile(loss='mean_squared_error', optimizer='adam')return model# 设定随机数种子
seed = 7
np.random.seed(seed)# 导入数据(训练集)
with open(fileTrain) as f:row = csv.reader(f, delimiter=',')for r in row:dataset = []r = [x for x in r if x != 'None']#涨跌幅是2天之间比较,数据会减少1个days = len(r) - 1#有效天数太少,忽略if days <= look_back + input_size:continuefor i in range(days):f1 = float(r[i])f2 = float(r[i+1])if f1 == 0 or f2 == 0:dataset = []break#把数据放大100倍,相当于以百分比为单位f2 = (f2 - f1) / f1 * 100#如果涨跌幅绝对值超过15%,基金数据恐有问题,忽略该组数据if f2 > 15 or f2 < -15:dataset = []breakdataset.append(f2)n = len(dataset)#进行预测的复盘,忽略掉最近forget_days的训练数据n -= forget_daysif n >= look_back + input_size:#如果数据不是input_size的整数倍,忽略掉最前面多出来的m = n % input_sizeX_1, y_1 = create_dataset(dataset[m:n])X_train = np.append(X_train, X_1)y_train = np.append(y_train, y_1)# 导入数据(测试集)
with open(fileTest) as f:row = csv.reader(f, delimiter=',')#写成了循环,但实际只有1条测试数据for r in row:dataset = []#去掉记录为None的数据(当天数据缺失)r = [x for x in r if x != 'None']#涨跌幅是2天之间比较,数据会减少1个days = len(r) - 1#有效天数太少,忽略,注意:测试集最后会虚构一个input_sizeif days <= look_back:print('only {} days data. exit.'.format(days))continue#只需要最后画图观察天数的数据if days > showdays:r = r[days-showdays:]days = len(r) - 1for i in range(days):f1 = float(r[i])f2 = float(r[i+1])if f1 == 0 or f2 == 0:print('zero value found. exit.')dataset = []break#把数据放大100倍,相当于以百分比为单位f2 = (f2 - f1) / f1 * 100#如果涨跌幅绝对值超过15%,基金数据恐有问题,忽略该组数据if f2 > 15 or f2 < -15:print('{} greater then 15 percent. exit.'.format(f2))dataset = []breaktestset.append(f1)dataset.append(f2)#保存最近一天基金净值f1=float(r[days])testset.append(f1)#测试集虚构一个input_size的数据(若有forget_days的数据,则保留)if forget_days < input_size:for i in range(forget_days,input_size):dataset.append(0)testset.append(np.nan)else:dataset = dataset[:len(dataset) - forget_days + input_size]testset = testset[:len(testset) - forget_days + input_size]if len(dataset) >= look_back + input_size:#将testset修正为input_size整数倍加1m = (len(testset) - 1) % input_sizetestset = testset[m:]m = len(dataset) % input_size#将dataset修正为input_size整数倍X_validation, y_validation = create_dataset(dataset[m:])#将输入转化成[样本数,时间步长,特征数]
X_train = X_train.reshape(-1, time_step, input_size)
X_validation = X_validation.reshape(-1, time_step, input_size)#将输出转化成[样本数,特征数]
y_train = y_train.reshape(-1, input_size)
y_validation = y_validation.reshape(-1, input_size)print('num of X_train: {}\tnum of y_train: {}'.format(len(X_train), len(y_train)))
print('num of X_validation: {}\tnum of y_validation: {}'.format(len(X_validation), len(y_validation)))# 训练模型
model = build_model()
model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size, verbose=1, validation_split=0.25, shuffle=True)# 评估模型
train_score = model.evaluate(X_train, y_train, verbose=0)
validation_score = model.evaluate(X_validation, y_validation, verbose=0)# 预测
predict_validation = model.predict(X_validation)#将之前虚构的最后一组input_size里面的0涨跌改为NAN(不显示虚假的0)
if forget_days < input_size:for i in range(forget_days,input_size):y_validation[-1, i] = np.nanprint('Train Set Score: {:.3f}'.format(train_score))
print('Test Set Score: {:.3f}'.format(validation_score))
print('未来{}天实际百分比涨幅为:{}'.format(input_size, y_validation[-1]))
print('未来{}天预测百分比涨幅为:{}'.format(input_size, predict_validation[-1]))#进行reshape(-1, 1)是为了plt显示
y_validation = y_validation.reshape(-1, 1)
predict_validation = predict_validation.reshape(-1, 1)
testset = np.array(testset).reshape(-1, 1)# 图表显示
fig=plt.figure(figsize=(15,6))
plt.plot(y_validation, color='blue', label='基金每日涨幅')
plt.plot(predict_validation, color='red', label='预测每日涨幅')
plt.legend(loc='upper left')
plt.title('关联组数:{}组,预测天数:{}天,回退天数:{}天'.format(time_step, input_size, forget_days))
plt.show()

代码稍稍比上一篇复杂一点,建议先看懂上一篇(让上一篇“臭文”发挥余热)。

几个参数介绍一下:
input_size:即作为时间窗口大小,同时也是LSTM神经网络的输入参数之一,以及输出参数(多对多的模式)。

time_step:时间窗口组数,即用多少组维度为input_size的数据,来预测下一个input_size的数据。

look_back:等于time_step * input_size,相当于使用多少个现有数据来预测,LSTM网络的input_shape是也。

showdays:其实就是测试集大小,按说可以大一些,但太大了画图看不清,建议用120吧。

forget_days:回退天数,用于复盘前forget_days天的预测。

以上代码采用:time_step=6,input_size=6(因此LSTM的units设为128,通常建议为input_shape的2-3倍),运行之:

训练速度很快,打印信息参考如下:
num of X_train: 434 num of y_train: 434
num of X_validation: 15 num of y_validation: 15
Epoch 1/50
56/56 [= = = = =] - 0s 20ms/step - loss: 0.0482 - val_loss: 0.0956

Epoch 50/50
56/56 [= = = = =] - 0s 4ms/step - loss: 0.0100 - val_loss: 0.0431
Train Set Score: 0.018
Test Set Score: 0.045

未来6天预测百分比涨幅为:[-0.05394792 -0.4340353 0.20469473 0.15502352 0.0738658 0.1280248 ]

和上一篇“臭文”相比,至少val_loss训练有效果了。而且最后得分也不错。

当然,结果到底如何,还得看图说话。

四、看图







凭直觉,look_back为30多天(相当于1.5个月的交易日)较为合理。采用不同的参数组合(time_step/input_size),跑几次,可见预测图形和实际图形的拟合度,和上一篇相比,有了极大改善。

感觉“有点香”了。

最后再加一段看净值图形的代码:(上一篇这一段写差了点,这次改了)

# 实际净值、预测净值
y_validation_plot = np.empty_like(testset)
predict_validation_plot = np.empty_like(testset)
y_validation_plot[:, :] = np.nan
predict_validation_plot[:, :] = np.nany = testset[look_back, 0]
p = testset[look_back, 0]
for i in range(look_back, len(testset)-1):y *= (1 + y_validation[i-look_back, 0] / 100)p *= (1 + predict_validation[i-look_back, 0] / 100)#print('{:.4f} {:.4f} {:.4f}'.format(testset[i+1,0], y, p))y_validation_plot[i, :] = ypredict_validation_plot[i, :] = p# 图表显示
fig=plt.figure(figsize=(15,6))
plt.plot(y_validation_plot, color='blue', label='基金每日净值')
plt.plot(predict_validation_plot, color='red', label='预测每日净值')
plt.legend(loc='upper left')
plt.title('关联组数:{}组,预测天数:{}天,回退天数:{}天'.format(time_step, input_size, forget_days))
plt.show()

由于基金净值是连续乘出来的,10天里面就算9天预测相同,但另一天有些差异(比如实际爆涨1.3%,而预测只是大涨0.5%),那么净值就会一直有较大的差异了------差不多要一直差1分多钱了,所以只贴一张图作为示意(这一段其实是为了验证代码是否正确计算了净值------把print语句打开)。

总结

LSTM模型效果有了明显改进。

当然,这里面有一些“作弊”成分:作为测试集的007669基金,由于跟训练集的5个基金相似(都是稳健的偏债混合基金),因此大的起伏基本一致,能拟合也不足为奇(既然验证集的val_loss经训练有了明显改进,测试集007669当然也可以表现不错------事实上,测试集分数,与验证集的val_loss分数相当)。

但是,如果认为LSTM模型只是靠“作弊”得分,那么这篇文章我就白写,您也白看了。LSTM本身是有“预测”能力的:不妨设置forget_days进行回退,复盘之前的预测。

后记

1、该模型可以用来预测股票指数吗?
当然可以,只需改动两行代码------先把测试基金007669改为股票指数基金比如005918(天弘沪深300ETF联接C),再把训练集数据放大100倍的地方,改为放大400倍(训练集是偏债基金,股票占比大约四分之一,涨跌幅度大约是测试集股指基金的四分之一),就可以啦。效果如下图所示:

2、time_step和input_size建议都用6天。
原因:一是基金持有7天(亦即5个交易日)则卖出手续费较低,预测后续6天走势作为参考是合理的(再长则没意义);二是感觉这种时间序列,time_step和input_size相接近是合理的。

3、进一步改进,采用层叠LSTM(第一层的ht作为第二层的Xt,需设参数return_sequences=True),例如改这几行代码:

#epochs = 50
epochs = 200#model.add(LSTM(units=128, input_shape=(time_step, input_size)))#model.add(Dense(units=input_size))model.add(LSTM(units=128, return_sequences=True, input_shape=(time_step, input_size)))model.add(LSTM(units=32))model.add(Dense(units=input_size))

又或者这样:

#epochs = 50
epochs = 200#model.add(LSTM(units=128, input_shape=(time_step, input_size)))#model.add(Dense(units=input_size))model.add(LSTM(units=128, return_sequences=True, input_shape=(time_step, input_size)))model.add(LSTM(units=32, return_sequences=True))model.add(Dense(units=1))

注意:后者这个模型仅当time_step = input_size才可以,因为它最后全连接层的输出维数是(time_step, 1),不是(input_size)。感觉该模型不够合理。

4、将LSTM模型改为GRU(门控循环单元),也是可以的。改两处代码:

#from keras.layers import LSTM
from keras.layers.recurrent import GRU#model.add(LSTM(units=128, input_shape=(time_step, input_size)))model.add(GRU(units=128, input_shape=(time_step, input_size)))

测试结果,层叠LSTM似有提高,而GRU没什么改善。

5、由于基金净值通常在工作日19:00—21:00进行更新,因此需避免在傍晚下载基金净值进行训练。

6、当然,不可以用于预测具体股票,切记切记!勿谓言之不预也。

这三篇就告一段落了,有缘人请耐心研究收获(请勿剽窃,更勿招摇),实现小目标,呵呵。

Python预测基金净值:LSTM模型有点香相关推荐

  1. Python中利用LSTM模型进行时间序列预测分析

    时间序列模型 时间序列预测分析就是利用过去一段时间内某事件时间的特征来预测未来一段时间内该事件的特征.这是一类相对比较复杂的预测建模问题,和回归分析模型的预测不同,时间序列模型是依赖于事件发生的先后顺 ...

  2. Python预测基金净值:keras神经网络

    Python预测基金净值:keras神经网络 如何搭建神经网络预测基金净值 一.时间窗口 二.爬基金数据,准备作为训练集.验证集.测试集 三.建模,读入数据,训练,验证 四.看图 总结 如何搭建神经网 ...

  3. 【深度学习 项目实战】Keras深度学习多变量时间序列预测的LSTM模型

    无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家.教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家.点这里可以跳转到教程.人工智能教程 本篇文章将介绍基于 ...

  4. 阿里天池竞赛 A股上市公司营收预测 使用LSTM模型做时序预测

    参赛结束了,最后结果一百多名,先把清洗好的数据和预测算法文件记录下来. 使用的完全代码和数据 https://download.csdn.net/download/infent/10693927 代码 ...

  5. 时间序列预测(四)—— LSTM模型

    时间序列预测(四)-- LSTM模型 欢迎大家来我的个人博客网站观看原文:https://xkw168.github.io/2019/05/20/时间序列预测-四-LSTM模型.html 文章链接 ( ...

  6. 深度学习LSTM模型对股票分析预测

    项目描述: 本程序使用了循环神经网络中的长期短期记忆模型(LSTM)以Apple公司过去60天的股价来预测收盘价 LSTM模型相当于对RNN模型的改良版本 RNN:想把所有的信息都记住,不管是有用的信 ...

  7. LSTM模型对家庭用电进行多步时间序列预测

    随着智能电表的兴起和太阳能电池板等发电技术的广泛应用,有大量可用的用电数据.这些数据代表了一系列与电力相关的多元时间序列,进而可以用来建模甚至预测未来的用电量.与其他机器学习算法不同,长短时记忆递归神 ...

  8. 使用python预测基金_使用python先知3 1创建预测

    使用python预测基金 This tutorial was created to democratize data science for business users (i.e., minimiz ...

  9. Pytorch LSTM模型 参数详解

    本文主要依据 Pytorch 中LSTM官方文档,对其中的模型参数.输入.输出进行详细解释. 目录 基本原理 模型参数 Parameters 输入Inputs: input, (h_0, c_0) 输 ...

最新文章

  1. Java 日志缓存机制的实现--转载
  2. equal、hashcode、==
  3. 处理android11以上无法删除相册图片的问题
  4. shell指令可以直接在终端输入吗_简化shell终端命令输入的脚本式快捷键工具
  5. Nginx学习笔记(三)
  6. Visual C++ 时尚编程百例009(响应键盘)
  7. linux系统界面光标不见了---木有了
  8. linux 写一个包含test的脚本程序,linux的test命令及相关shell脚本详解
  9. mysql 1130本地连接_mysql ERROR 1130 问题解决方案
  10. c语言双精度型输出小数位数_4.1 C语言数据的输出
  11. .netcore mvc docker环境jenkins一键部署(DevOps)
  12. java switch null,如何在switch中使用null
  13. Java JDK中文帮助文档免费下载,百度网盘下载。
  14. 交通流理论1——发展阶段
  15. Spring Boot 应用在 kubernetes 的 sidecar 设计与实战
  16. python图像手绘效果_如何使用Python实现手绘图效果
  17. 潘赟九宫格写作总结(精简背诵版)
  18. MitraStar DSL-2401HN-T1C-GV硬件与启动信息
  19. luogu P3899 [湖南集训]谈笑风生 线段树合并
  20. 关于SearchView的一些小细节

热门文章

  1. luoguP5108 仰望半月的夜空 [官方?]题解 后缀数组 / 后缀树 / 后缀自动机 + 线段树 / st表 + 二分...
  2. 百度董事长兼CEO李彦宏创立《百度互联网》全球最大的中文搜索引擎
  3. java力矩_机器人单关节力矩控制(前馈+反馈)
  4. Java代码混淆工具-ProGuard
  5. S3C6410 中的 cascaded irqdomain 之 gpio
  6. window.print()打印指定页面内容
  7. Excel VBA编程的常用代码
  8. linux命令下载电影,linux命令行---用wget下载电影
  9. leetcode-1100. 长度为 K 的无重复字符子串
  10. 爬取B站视频弹幕生成词云