ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

股价预测怎么做?基于Python和LSTM的完整实战流程

股价预测怎么做?基于Python和LSTM的完整实战流程 简介基于Python的神经网络股票价格预测项目源码面向高校期末大作业或课程设计场景可用于学习LSTM模型在金融时序数据上的完整预测流程。代码以上证指数CSV数据为输入包含数据加载与预处理标准化、训练/测试集划分、LSTM模型定义、训练与预测等模块可直接运行train.py完成模型训练运行evaluate.py输出预测结果。资源共13个文件以Python脚本为核心配有模型保存文件、指数数据文件、结果图片及说明文档压缩包约360KB轻量完整便于快速上手。已有463人学习浏览适合正在完成股票预测类课题、希望参考工程化代码结构的学生参考。除可直接运行外还可借其理解数据标准化、滑动窗口划分与循环神经网络调参思路是期末大作业或入门量化预测的实用资料。1. 从期末大作业到能跑的股价预测这个zip里到底装了什么股票价格预测是Python神经网络最经典的练手场景一个期末大作业级别的源码包通常就是一条「数据获取 → 特征处理 → 模型训练 → 回测画图」的完整流水线。这类项目不追求复杂交易策略核心是让你看到历史行情数据进模型未来价格走势出结果期间经历数据清洗、归一化、滑窗切分、前馈网络或LSTM训练、反归一化画图的完整闭环。对刚入门Python和神经网络的人来说读完这套项目等于把深度学习落地的流程走了一遍对已经上手的人来说重点则在于从源码里拆出「哪些模块能复用、哪些参数是玄学、哪些地方容易翻车」。适合谁这件事也很具体期末要交作业的学生、想用Python做量化入门分析的自学者、以及需要给团队做技术预研的工程师。整篇博文不依赖你手头那份zip的具体实现我按这类项目最常见的组织方式来拆——数据层、模型层、评估层然后把踩过的坑和参数边界讲透。看懂了自己重新写一个也不是难事。2. 股票数据从哪来先解决“喂给神经网络吃什么”的问题2.1 数据源选型免费接口、爬虫和本地文件的取舍股票价格预测的第一步是把历史行情拿到手。期末大作业场景下数据来源通常有三种免费财经接口、爬虫抓取、本地CSV。免费接口是最省事的选择akshare、yfinance、tushare这些库都能直接拿到日线数据返回DataFrame字段包括日期、开盘价、最高价、最低价、收盘价、成交量。爬虫适合拿不到接口数据的特殊市场或更高频数据但要处理反爬、更新失效、数据合法性这些问题对期末作业来说性价比不高。本地CSV则适合老师直接给定数据集的情况读取稳定也方便复现。实际推荐优先级是本地CSV 免费接口 爬虫。本地数据没有网络波动结果可复现免费接口适合想展示“完整数据获取流程”的作业但注意接口字段差异——有的返回复权价有的返回原始价混用的话模型会被价格突变带偏。yfinance返回的Adj Close是复权价直接用它训练预测的是复权后的价格走势和实盘看到的原始K线有出入这一点下面讲数据清洗时会再强调。2.2 数据清洗的四个必做动作拿到原始数据后第一件事不是建模而是清洗。常见动作有四个去重与补空日线数据偶尔有重复行或停牌日缺口对日期列去重对缺失值用前向填充ffill或插值不要留NaN进模型。选列与改名把需要的列整理成date、open、high、low、close、volume六个标准字段避免后面写代码时被中文列名或多余列干扰。复权处理如果接口给的是前复权价直接用如果是原始价遇到分红除权会出现价格跳空需要自己算复权因子。期末作业可以不做复权但要意识到这个边界。排序与索引按日期升序排列用datetime索引这是后续滑窗切分的前提。下面这段代码是用akshare拉取数据并完成基础清洗的最小实现import akshare as ak import pandas as pd # 拉取平安银行日线数据adjustqfq表示前复权 df ak.stock_zh_a_hist( symbol000001, perioddaily, start_date20150101, end_date20231231, adjustqfq ) # 统一列名 df.rename(columns{ 日期: date, 开盘: open, 最高: high, 最低: low, 收盘: close, 成交量: volume }, inplaceTrue) df[date] pd.to_datetime(df[date]) df.sort_values(date, inplaceTrue) df.reset_index(dropTrue, inplaceTrue) # 去重 前向填充缺失值 df.drop_duplicates(subset[date], keeplast, inplaceTrue) df[[open, high, low, close, volume]] df[ [open, high, low, close, volume] ].fillna(methodffill) print(df.head()) print(f共 {len(df)} 条记录)这段代码里有几个参数比较关键。adjustqfq前端复权会让历史价格扣除分红影响训练数据的价格连续性好fillna(methodffill)是前向填充也就是说用前一个交易日的数据补缺口这是金融时序数据最常用的补法比均值填充更合理——股票不会因为某天停牌就把价格变成历史均值。去重的keeplast保留重复记录中最后一条避免同一交易日出现两套价格时选到早盘数据。2.3 特征工程不要只喂收盘价很多人第一次做股价预测只把收盘价序列丢进神经网络这叫单变量预测。效果不是不行但模型能用的信息太少了。更常见的做法是加入成交量、涨跌幅、最高最低价差等技术特征。期末大作业如果要演示特征工程可以构造几个经典因子当日涨跌幅pct_change、5日均线与收盘价的偏离度、成交量滚动均值比。这些特征能从不同侧面反映价格运动的动力和情绪温度。但注意一个反直觉的点特征不是越多越好。对小样本的日线数据特征多了会增加过拟合风险训练集上表现好得离谱验证集上马上现原形。一个稳定可用的基线是收盘价加成交量两个维度如果作业对创新有要求再往上涨跌幅和技术因子。特征构造完成后必须做归一化因为神经网络的激活函数对输入尺度很敏感价格是百元级别、成交量是百万级别直接喂进去会让训练过程极其不稳定。归一化一般用MinMaxScaler把数据压缩到[-1, 1]或[0, 1]这个操作在训练前做下面一节细说。3. 模型搭建与训练前馈网络与LSTM的选型和调参3.1 为什么默认用LSTM而不是纯全连接层股票价格预测这个标题下最常见的网络结构是前馈神经网络也叫全连接网络和LSTM。前者结构简单适合演示神经网络的基本原理——输入层、隐藏层、输出层每一层做线性变换加激活函数后者引入了时间步的概念适合处理序列关系因为股价天然是时间序列今天的价格和昨天、上周的价格都有关系。期末大作业的常见选择是LSTM原因有三一是课程里讲了循环神经网络正好应用二是LSTM对时间步的建模能力肉眼可见地强于全连接效果好展示三是即使效果不好也可以用「金融市场噪声大」这种话术兜底。但如果数据集只有几百条LSTM未必比全连接好反而是全连接网络在小样本上更稳。我的建议是两种都实现做一个对比实验——这是期末作业拿高分的经典套路一个基准模型加一个改进模型用同一份数据跑画对比图。3.2 LSTM预测的最小可运行代码下面是一套用TensorFlow/Keras实现LSTM单步预测的最小代码整体结构是滑窗生成样本、切分训练集测试集、归一化、建模型、训练、反归一化回测。import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout # 假设df已经按2.2节清洗好用收盘价和成交量两列 data df[[close, volume]].values scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(data) # 滑窗函数用过去window天预测下一天 def create_sequences(data, window20): X, y [], [] for i in range(window, len(data)): # 输入是最近window天的数据输出是第i天 X.append(data[i - window:i]) y.append(data[i]) return np.array(X), np.array(y) window 20 X, y create_sequences(scaled, window) # 按时间顺序切分前80%训练后20%测试 split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] # 构建LSTM模型 model Sequential() model.add(LSTM(units64, return_sequencesTrue, input_shape(window, 2))) model.add(Dropout(0.2)) model.add(LSTM(units32)) model.add(Dropout(0.2)) model.add(Dense(units2)) # 输出为预测的close和volume model.compile(optimizeradam, lossmse) history model.fit( X_train, y_train, epochs30, batch_size32, validation_data(X_test, y_test), verbose1 )这里需要解释几个重点。create_sequences里循环从window开始生成样本的方式是滑动窗口——第0到19天的数据预测第20天第1到20天预测第21天依此类推。这样构造出的样本彼此有重叠这是时间序列预测的常规做法不必担心数据泄漏因为预测目标永远是未来一天。模型结构用了两层LSTM第一层return_sequencesTrue是为了把完整的时间步序列传给第二层如果单层LSTM就不需要这个参数。Dropout放在LSTM层之后作用是随机丢弃一部分神经元输出降低过拟合比例0.2是常见起步值。Dense(units2)对应两个输出收盘价和成交量。这里有个容易踩的坑如果只预测收盘价训练时输出目标只有y[:, 0]模型结构也要改成Dense(units1)。混合预测的好处是让模型学两个序列之间的联动关系坏处是如果成交量数据噪声很大反而会拖累收盘价预测。期末作业建议先用收盘价单变量跑通再尝试加入成交量做对比这样每一步都有话可写。3.3 训练过程中的三个必调参数epochs、batch_size、window这三个参数决定了模型能不能收敛也是期末答辩最容易被老师追问的点。epochs控制整个训练集被遍历多少次。太小欠拟合预测曲线是一条直线太大过拟合训练误差趋近于零测试集完全走样。判断方法是看训练和验证两条loss曲线的走势——训练loss持续下降而验证loss先降后升就是过拟合了。30是起步值如果发现验证loss还在下降就加大到50或100如果已经过拟合就调小并配合早停。batch_size是每次梯度更新用的样本数。32是通用默认样本量小几百条时可以设为16梯度更新更频繁收敛更平滑代价是训练时间变长。样本量大几千条时设64或128训练更快。window是滑窗长度相当于模型能回顾多少天的历史。20个交易日约等于一个自然月是日线预测的常见起点。window太小模型没有足够上下文预测波动特别大window太大模型学到的是过时的价格模式对最近的市场变化反应迟钝。这个参数值得做一组对比实验5、10、20、40各跑一遍把验证集loss画成曲线选最低的那个。# 一个快速验证不同window效果的代码片段 from sklearn.metrics import mean_squared_error for w in [5, 10, 20, 40]: Xw, yw create_sequences(scaled, w) split_w int(len(Xw) * 0.8) model_w Sequential() model_w.add(LSTM(units64, return_sequencesTrue, input_shape(w, 2))) model_w.add(Dropout(0.2)) model_w.add(LSTM(units32)) model_w.add(Dropout(0.2)) model_w.add(Dense(units2)) model_w.compile(optimizeradam, lossmse) model_w.fit(Xw[:split_w], yw[:split_w], epochs30, batch_size32, validation_split0.1, verbose0) mse_w mean_squared_error(yw[split_w:], model_w.predict(Xw[split_w:])) print(fwindow{w}, test_mse{mse_w:.6f})这个代码片段演示了调参的基本姿势固定其他参数只改变window记录测试集上的MSE最后选择MSE最小的window。注意validation_split0.1是从训练集尾部分出10%做验证而不是用测试集这是为了避免用手头的测试集反复调参造成信息泄漏。真正选好window后再用全部80%数据重新训练最终模型对最后的20%测试集做一次评估这个流程才是干净的。4. 预测结果评估与可视化别被漂亮的拟合曲线骗了4.1 反归一化的正确姿势模型输出是归一化后的数值直接画图会是0到1区间的曲线没法跟真实价格对比。反归一化需要用训练时同一个scaler对象把预测值映射回原始价格尺度。有一个细节必须注意因为是二维数据close和volume一起归一化的反归一化后得到的也是一组二维向量只取第一列才是收盘价。# 反归一化并对比真实价格 pred_scaled model.predict(X_test) pred_inv scaler.inverse_transform(pred_scaled) # 二维反归一化 y_test_inv scaler.inverse_transform(y_test) true_close y_test_inv[:, 0] # 真实收盘价 pred_close pred_inv[:, 0] # 预测收盘价 # 计算均方根误差和平均绝对误差 rmse np.sqrt(mean_squared_error(true_close, pred_close)) mae np.mean(np.abs(true_close - pred_close)) print(fRMSE: {rmse:.2f}, MAE: {mae:.2f})这里的核心逻辑是对训练数据拟合的scaler必须复用在对测试数据的反归一化上。如果对测试数据单独fit一个新的scaler两者的取值范围和分布都会发生变化反归一化出来的价格完全对不上。这也是期末作业里最隐蔽的逻辑错误之一——预测曲线走势看着没问题但价格差了好几倍多半是反归一化这一步用了两个不同的scaler对象。误差指标上RMSE和MAE的价格单位与真实价格一致可以直接读。以价格在10到20区间的股票为例MAE在0.5以内说明平均每天预测价差0.5元这在日线预测里就算不错的水平MAE超过2则基本没什么实用价值但作为期末演示依然能说明整体流程。更专业的指标是MAPE平均绝对百分比误差因为不同股票价格尺度差异大用百分比衡量更客观。4.2 画图时一定要避免的“未来函数”这是个股市预测项目里最经典的翻车场景把测试集的真实数据喂进模型然后画出一条以为是自己预测出来的曲线。具体表现是——预测曲线在测试集起始阶段和真实曲线几乎重合偏差从某个时间点才逐渐变大。原因是滑窗生成的测试集样本本身包含了一部分测试集开头几天的真实值模型预测测试集的头几个点时输入里已经混入了当天的真实价格信息这在量化术语里叫未来函数现实中是不可用的。正确画法是把测试集预测结果与真实结果放到同一张图上并清楚标注两者的时间范围和输入窗口的错位。更严格的做法是滚动预测只用训练集末尾的window天做起点预测下一天然后把预测值作为下一轮输入的一部分逐步向前滚动。这种自回归式的预测更接近真实场景但误差会随步数累积画出来的曲线越往后偏离越大这是正常的不要慌。下面这段代码实现了一个滚动预测的可视化对比import matplotlib.pyplot as plt # 用X_test的第一个样本作为初始输入滚动预测20步 current_input X_test[0].reshape(1, window, 2) # 初始输入 future_preds [] for _ in range(20): next_pred model.predict(current_input, verbose0) future_preds.append(next_pred[0, 0]) # 只取预测收盘价 # 构造下一步输入去掉最老一天拼上预测值 next_input np.vstack([current_input[0, 1:], next_pred]) current_input next_input.reshape(1, window, 2) future_preds np.array(future_preds) # 反归一化后画图 future_preds_inv scaler.inverse_transform( np.column_stack([future_preds, np.zeros_like(future_preds)]) )[:, 0]这个滚动预测展示的含义很清晰每一步模型只看到历史数据包括自己上一步的预测值不会看到真实未来。反归一化时np.zeros_like(future_preds)是为了凑齐两个维度因为反归一化需要二维结构成交量维度用0占位反正只取第一列。随着滚动步数增加误差累积导致预测曲线逐渐偏离真实曲线这是模型能力的真实体现比那种一开始就贴合得很好的一步预测图更有说服力。4.3 期末答辩时怎么解释一张“不好的图”预测曲线和真实曲线偏差大几乎是必然结果——股票价格受消息面、政策、市场情绪影响历史模式只能解释一部分。答辩时的正确策略是先展示训练集的拟合效果说明模型有能力学习历史模式再展示测试集上的滚动预测说明泛化能力有边界最后补充误差指标和特征重要性分析说明哪些输入变量对预测贡献大。这套逻辑把「模型不好」转化为「对市场复杂性的合理讨论」比硬说预测很准要体面得多。评估这个环节不只是算指标更是让读者信服这套源码有真实边界、有改进方向的核心章节。5. 避坑指南期末大作业最容易翻车的5个细节5.1 归一化前忘记先切分数据现象训练集和测试集上表现都不错但在反归一化后价格整体偏移甚至出现负数。原因在切分数据之前就对整个数据集做了MinMaxScaler归一化测试集的信息混进了训练过程。虽然模型训练时没有直接见到测试集但归一化用了全量的最小值和最大值本质上就泄漏了测试集的尺度信息。在严格的时间序列评估里这一步已经污染了评估的公正性。解决先切分原始数据再做归一化。只对训练集fit scaler然后用训练集的scaler去transform测试集。训练集和测试集保留各自的DataFrame切片清洗和归一化都在切片上完成这是时间序列和普通机器学习评估的最大区别。5.2 LSTM输入维度少了一维现象ValueError: Input 0 of layer lstm is incompatible with the layer。原因LSTM期望输入形状是(samples, time_steps, features)的三维张量。很多新手在构造X时只得到二维——比如直接用data[i-window:i]得到(window, 2)但没有通过np.array(X)把所有样本堆叠成(batch, window, 2)的三维结构。报错信息通常会提示shape mismatch但初学者容易忽略时还记得LSTM与全连接层对输入形状要求不同。解决确认X在送入模型前的shape是(样本数, window, 特征数)。用X.shape打印检查特征数为2对应close和volume两列如果是单变量则为1。reshape操作可以显式调整但更推荐在滑窗构造时就用np.array(X).reshape(-1, window, features)确保维度正确。5.3 用整个数据集的随机切分代替时间顺序切分现象测试集上预测精度极高但实际模拟交易时完全不赚钱。原因随机切分shuffleTrue把未来数据混进了训练集模型已经提前“见”过测试集区间的数据模式了。比如训练集里包含了2022年底的数据测试集中在2022年中间模型学到的是“未来”的信息。时间序列数据的顺序性决定了必须按时间先后切分。解决严格按时间切分例如前80%时间段的样本做训练后20%做测试。这也是用split int(len(X) * 0.8)而不是train_test_split(shuffleTrue)的原因。金融时序预测里任何随机切分的结果都是虚高答辩时被追问就容易翻车。5.4 复权数据与原始K线混用现象训练时使用前复权价格画图时却拿原始K线对比发现历史价格对不上。原因前复权价会调整历史价格去除分红送股的跳空影响。如果训练数据和验证数据分别来自复权和未复权接口模型的输入分布不一致预测结果自然偏差很大。解决明确数据源的复权状态并保持一致。yfinance的Adj Close字段是复权价直接用akshare拉历史日线时显式指定adjustqfq或adjusthfq。如果非要使用原始价需要自行计算复权因子做统一处理期末作业不建议在这上面花时间。5.5 输出了NaN但程序不报错现象训练正常loss值正常下降但预测结果全是NaN或无数值。原因常见于数据中包含NaN或Inf模型参数更新时梯度爆炸产生了NaN。虽然有fillna(methodffill)兜底但可能仍有极个别缺失值以原始形式存在或者在计算涨跌幅、滚动均值时早期位置会出现NaN这些NaN被带入滑窗生成样本。解决训练前加一步检查打印数据的isnull().sum()和np.isinf()统计信息。在滑窗函数里加一行data np.nan_to_num(data, nan0.0, posinf0.0, neginf0.0)做兜底。这会略微改变数据分布但只要NaN数量极少对结果影响可以忽略远比模型输出NaN要可控。6. 从期末作业到量化策略这套源码还能怎么改6.1 把预测值改成买卖信号预测价格本身难以直接用于交易但预测结果转化为涨跌方向就有实用价值了。一个简单的信号规则是如果预测下一日收盘价高于当日收盘价则发出买入信号低于则发出卖出信号。用回测框架计算这个策略的收益率曲线对比“买入持有”基准就能知道预测模型是否真的带来了超额收益。注意策略对比要扣除手续费和滑点这两项在回测中占比很大。6.2 用分位数置信区间替代点预测LSTM输出的是一个点预测值但市场本质是概率分布。更进阶的玩法是在预测值上叠加误差分布假设比如假设预测误差服从正态分布取预测值的正负两个标准差作为区间估计。这个区间可以直接画在预测图上展示模型的不确定性。另一种做法是用多个随机种子的模型做集成取多次预测的均值作为最终预测方差作为不确定性估计这比单次预测更稳。6.3 数据更新的自动化脚本期末作业交付后如果想让它持续跑写一个数据更新的自动化脚本是实用的下一步。用schedule库或操作系统的定时任务每天收盘后自动拉取最新行情跑一遍增量训练生成最新预测把结果写入数据库或本地csv。这样模型处于持续维护状态而不是一次性演示品。注意增量训练和全量重新训练的区别增量训练速度快但有遗忘旧知识的风险全量重训效果好但费时。从做过的项目经验来看最值得养成的习惯是改任何一个参数前先把旧结果的可视化图存好再跑新实验对比两张图。这个习惯能省下大量「我改了哪几个参数导致结果变差」的排查时间。也希望这篇笔记能帮你在期末大作业和量化入门之间搭一座桥——先跑通预测再理解边界最后把它变成能讲清原理、敢于展示不足的完整作品。希望帮到你。本文还有配套的精品资源点击获取
返回列表