资讯详情

Python LSTM股市预测实战:从数据到模型完整方案

📅 2026/9/28 9:38:42 | 华诺云谱 👁 阅读
Python LSTM股市预测实战:从数据到模型完整方案
简介这份资源面向计算机、金融数据方向的学生与开发者提供一套基于Python与LSTM模型实现股市预测的完整项目可作为高分课程设计、期末大作业的参考方案也适合想入门深度学习时序预测的初学者练手。压缩包共89个文件约8.87MB以24个py源码文件为核心辅以pyc编译文件、txt说明、jpg与gif图示、css/js/html前端页面、xml配置、xls与xlsx数据表、sqlite3数据库及md文档覆盖数据、模型、界面与说明各环节。目前已有792人学习下载。项目代码结构完整下载后即可运行注释较为详尽便于理解LSTM建模、训练与预测流程读者可据此掌握数据预处理、模型搭建、结果可视化等关键步骤并在此基础上进行二次开发或迁移到其他时序预测场景。1. 用 LSTM 预测股市一套能跑通的 Python 方案长什么样很多人第一次搜「Python 基于 LSTM 模型实现预测股市源代码模型数据集」心里想的其实是同一件事有没有一套现成的、能直接跑起来的代码把股票历史数据喂进去第二天开盘价就吐出来。这个诉求非常具体也非常容易翻车。我见过太多人从网上抄了一份 LSTM 预测代码把close列一换跑出来一条平滑得不像话的曲线然后兴冲冲拿去实盘结果被市场教育得很惨。这套方案真正要解决的不是「预测准不准」而是「怎么把一条时间序列正确地喂进 LSTM并且知道它到底学到了什么」。它适合有 Python 基础、懂一点 pandas、想入门量化或时间序列预测的工程师。你需要准备的是一份股票日线数据CSV 即可、一个能跑 PyTorch 或 TensorFlow 的环境、以及一颗接受「模型大概率跑不赢买入持有」的平常心。下面我按自己实际搭过的流程从数据、模型、训练到排错一步步拆开讲。2. 数据准备与特征工程把 OHLCV 变成 LSTM 能吃的张量2.1 为什么原始收盘价不能直接喂给 LSTMLSTM 对输入尺度非常敏感。股票价格动辄几十上百不同股票量级差异巨大直接输入会导致梯度爆炸或收敛极慢。更关键的是价格序列是非平稳的均值方差随时间漂移模型很容易学到「最近价格大概在这个区间」这种无意义的记忆而不是真正的时序模式。常见做法是先做差分或对数收益率转换再做归一化。我一般用对数收益率log(close_t / close_{t-1})它近似平稳且能消除量纲。如果你坚持用价格至少要做 MinMax 归一化并且归一化参数必须只用训练集拟合否则就是典型的数据泄露。import pandas as pd import numpy as np # 读取股票日线数据至少包含 date, open, high, low, close, volume df pd.read_csv(stock_daily.csv, parse_dates[date]) df df.sort_values(date).reset_index(dropTrue) # 对数收益率消除量纲和非平稳性 df[log_ret] np.log(df[close] / df[close].shift(1)) df[vol_chg] df[volume].pct_change() df df.dropna().reset_index(dropTrue) # 只用训练集拟合归一化参数避免数据泄露 train_end int(len(df) * 0.8) train_df df.iloc[:train_end] feat_cols [log_ret, vol_chg, high, low] mu train_df[feat_cols].mean() sigma train_df[feat_cols].std() df[feat_cols] (df[feat_cols] - mu) / sigma这段代码的关键点有三个shift(1)保证收益率用的是前一天收盘价不会引入未来信息train_end切分后才计算mu和sigmafeat_cols里我加了高低价和成交量变化单靠收盘价一条序列LSTM 能学到的信息太单薄。2.2 滑动窗口构造样本lookback 到底设多少LSTM 需要三维输入(样本数, 时间步, 特征数)。时间步就是 lookback表示用过去多少天预测下一天。这个参数没有标准答案但有几个经验边界太短比如 5 天学不到中期趋势太长比如 120 天参数量暴涨且容易过拟合。A 股常用 20 到 60 个交易日我一般从 30 开始试。def make_sequences(data, feat_cols, target_col, lookback30): X, y [], [] arr data[feat_cols].values target data[target_col].values for i in range(lookback, len(data)): X.append(arr[i - lookback:i]) y.append(target[i]) return np.array(X), np.array(y) lookback 30 X, y make_sequences(df, feat_cols, log_ret, lookback) # 按时间顺序切分不能随机打乱 split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] print(X_train.shape) # (样本数, 30, 4)这里最容易犯的错是切分前用了train_test_split(shuffleTrue)。时间序列一旦打乱测试集里就混入了未来信息评估结果会虚高得离谱。另外注意target_col我用的还是log_ret也就是预测下一天收益率而不是价格。预测价格看起来直观但评估时会被价格水平掩盖误差收益率才是更干净的监督信号。3. LSTM 模型搭建层数、隐藏单元和 Dropout 怎么定3.1 从单层到双层什么时候该加深PyTorch 的nn.LSTM用起来很简单但参数选择直接决定模型是学到东西还是纯背数据。单层 LSTM 适合特征少、样本量小的场景双层能提取更高阶的时序抽象但参数量翻倍过拟合风险也翻倍。我的判断标准是训练集 loss 能稳定下降但验证集不降才考虑加层如果训练集本身就降不下去加层没用先检查学习率和特征。import torch import torch.nn as nn class StockLSTM(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, dropout0.3): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0 ) self.fc nn.Linear(hidden_size, 1) def forward(self, x): # x: (batch, seq_len, input_size) out, _ self.lstm(x) # 只取最后一个时间步的输出做预测 last out[:, -1, :] return self.fc(last).squeeze(-1) model StockLSTM(input_sizelen(feat_cols), hidden_size64, num_layers2, dropout0.3)hidden_size64是我在日线数据上比较稳的起点128 以上在小数据集上几乎必过拟合。dropout只在num_layers 1时生效单层加 dropout 意义不大。batch_firstTrue让输入维度是(batch, seq, feature)和前面构造的样本对齐忘了设这个参数是新手最常见的维度报错来源。3.2 训练循环里的三个必调参数学习率、batch size、早停轮数这三个参数决定训练是顺利收敛还是原地打转。学习率我一般从 1e-3 开始配合ReduceLROnPlateau在验证 loss 不降时减半batch size 用 32 或 64太小梯度噪声大太大泛化差早停 patience 设 10 到 15防止在验证集上反复横跳。from torch.utils.data import TensorDataset, DataLoader train_ds TensorDataset(torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.float32)) train_loader DataLoader(train_ds, batch_size64, shuffleFalse) # 时间序列不 shuffle optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, patience5, factor0.5) criterion nn.MSELoss() best_val float(inf) patience, wait 12, 0 for epoch in range(200): model.train() for xb, yb in train_loader: optimizer.zero_grad() loss criterion(model(xb), yb) loss.backward() # 梯度裁剪防止 LSTM 梯度爆炸 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() model.eval() with torch.no_grad(): val_pred model(torch.tensor(X_test, dtypetorch.float32)) val_loss criterion(val_pred, torch.tensor(y_test, dtypetorch.float32)).item() scheduler.step(val_loss) if val_loss best_val: best_val, wait val_loss, 0 torch.save(model.state_dict(), best_lstm.pt) else: wait 1 if wait patience: print(fearly stop at epoch {epoch}) breakshuffleFalse在训练集上也要保持因为 LSTM 虽然按 batch 训练但样本内部时序不能乱。梯度裁剪clip_grad_norm_对 LSTM 几乎是必备的尤其是层数多或序列长的时候不加很容易看到 loss 突然变成 nan。早停保存的是验证集最优权重不是最后一轮这个细节决定了你最终拿到的是不是过拟合模型。4. 避坑与排查LSTM 预测股市最常见的五个翻车现场4.1 预测曲线完美贴合实盘一塌糊涂现象画图时预测线和真实线几乎重合MSE 低到 0.001但把模型用于下一天预测方向完全反了。原因最常见的是数据泄露。比如归一化用了全量数据、切分时打乱了时间顺序、或者特征里混入了未来才公布的数据。另一个隐蔽原因是模型学到了「昨天收益率接近今天收益率」这种自相关而不是真正的预测能力。解决严格按时间切分归一化参数只用训练集把评估指标从 MSE 换成方向准确率涨跌方向对不对如果方向准确率在 50% 附近说明模型没有预测能力MSE 再低也没用。4.2 Loss 降到一半突然变 nan现象训练几个 epoch 后 loss 打印出 nan模型参数全废。原因LSTM 梯度爆炸或者学习率太大。序列较长、层数较多时尤其常见。解决加clip_grad_norm_(model.parameters(), max_norm1.0)学习率降到 1e-4 再试检查输入里有没有 inf 或极端异常值收益率出现 ±50% 这种数据要先处理。4.3 验证集 loss 比训练集还低现象训练 loss 0.02验证 loss 0.01看起来模型泛化很好。原因验证集样本太少或者验证集恰好落在一段低波动行情里。时间序列的验证集如果只有几十个样本loss 波动极大不能作为可靠依据。解决用滚动窗口做多段验证而不是只切一刀至少保证验证集覆盖一段完整涨跌周期对比买入持有基准模型跑不赢基准就没有落地价值。4.4 换一只股票模型完全失效现象在茅台数据上训练好的模型直接套用到另一只股票预测全错。原因不同股票的价格量级、波动率、成交量分布差异巨大归一化参数和模型权重都不通用。解决要么每只股票单独训练要么做跨股票的标准化比如按波动率归一化并在特征里加入行业或市值因子。指望一个模型通吃所有股票在当前数据量下不现实。4.5 用未来函数做特征回测漂亮实盘崩现象回测年化 80%实盘一个月亏 15%。原因特征里用了当天收盘后才能知道的数据比如用当日最高价预测当日收盘价或者用了未来几天的均线。解决所有特征在t时刻只能使用t-1及之前的数据用shift(1)统一滞后回测时严格按信号次日开盘成交不要用当日收盘价成交。5. 进阶技巧用方向准确率和滚动预测验证模型真实价值训练完模型别急着看 MSE。我一般会做两件事一是算方向准确率二是做滚动单步预测。方向准确率直接回答「模型能不能判断涨跌」滚动预测则模拟真实使用场景——每天用最新数据预测下一天然后滚一天。model.load_state_dict(torch.load(best_lstm.pt)) model.eval() with torch.no_grad(): pred model(torch.tensor(X_test, dtypetorch.float32)).numpy() # 方向准确率预测收益率和真实收益率同号的比例 direction_acc np.mean((pred 0) (y_test 0)) print(f方向准确率: {direction_acc:.3f}) # 滚动单步预测每次只用历史 lookback 天预测下一天 rolling_preds [] for i in range(lookback, len(df) - 1): seq df[feat_cols].values[i - lookback:i] seq torch.tensor(seq[None, :, :], dtypetorch.float32) with torch.no_grad(): p model(seq).item() rolling_preds.append(p) rolling_preds np.array(rolling_preds) real df[log_ret].values[lookback:-1] print(f滚动方向准确率: {np.mean((rolling_preds 0) (real 0)):.3f})方向准确率能到 55% 以上才勉强有讨论价值50% 附近就是抛硬币。滚动预测比一次性预测更接近实盘因为它模拟了每天重新输入最新数据的过程。如果滚动准确率明显低于一次性预测说明模型对某段特定历史过拟合了。还有一个我踩过的坑不要用预测出来的价格反推买卖信号就直接上实盘。LSTM 输出的是收益率预测不是价格预测中间还隔着滑点、手续费、涨跌停无法成交这些现实约束。我现在的习惯是任何模型先跑三个月模拟盘方向准确率和夏普比率都稳定了再考虑小仓位试水。这套代码和流程能帮你把 LSTM 预测股市的完整链路跑通但能不能赚钱取决于你对市场的理解和风控不是模型本身。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑