简介这份资源面向金融量化初学者与机器学习实践者聚焦如何用LSTM长短期记忆网络对股票、基金的时间序列数据进行价格走势预测。内容围绕数据预处理、LSTM网络构建、训练验证与预测评估展开涉及归一化、移动平均、技术指标、MSE损失与Adam优化器等具体环节并讨论了市场政策与突发事件带来的预测挑战。压缩包共10个文件约154KB包含5个xlsx数据表用于训练与测试样本、3个py脚本负责模型搭建与数据获取、1个pyc缓存及1张png结果图结构紧凑便于直接运行与二次修改。目前已有363人学习下载。读者可借此掌握从历史行情到预测输出的完整流程理解门控机制在长期依赖建模中的作用并获得可复用的代码骨架与评估思路为后续优化模型精度、拓展特征工程提供参考。1. 从一份 LSTM 股票基金预测模型压缩包说起它到底能预测什么很多人第一次拿到「基于机器学习 LSTM 的股票基金预测模型」这类压缩包第一反应是打开看有没有能直接跑出收益的脚本。我当年也一样解压、装依赖、python main.py然后盯着屏幕上那条几乎贴着真实收盘价走的曲线兴奋了半小时直到把它挪到最近三个月的数据上——曲线立刻变成一条迟钝的直线。这不是模型坏了而是我们一开始就误解了 LSTM 在时序预测里能干什么。LSTM长短期记忆网络本质是一个带门控的循环神经网络它擅长从一段历史序列里学出「短期波动 长期趋势」的混合模式然后外推下一格。放到股票基金场景它能做的是基于历史价格、成交量、换手率这类数值序列给出下一个交易日或下一周的数值估计而不是告诉你「买还是卖」。这个区别决定了整个项目的定位它是一个时序回归工程不是一个交易策略。适合谁适合想用 Python 把 LSTM 时间序列预测完整跑一遍的人——从数据清洗、窗口切分、模型搭建到回测评估这套流程走通了迁移到销量预测、负荷预测、流量预测都是同一套骨架。热搜里「lstm时间序列预测python」「lstm模型代码」「时序预测模型最新」这些词说的其实就是这件事。2. 数据准备与特征工程LSTM 预测模型的地基怎么打2.1 为什么股票基金数据不能直接喂给 LSTM原始行情数据有三个绕不开的问题缺失、量纲不统一、非平稳。停牌日没有成交基金净值周末不更新直接dropna会切断时间连续性收盘价几千块、成交量几亿手不归一化会让梯度爆炸价格序列带趋势均值方差随时间漂移模型学到的「高位」和「低位」在不同时间段含义完全不同。常见做法是先做交易日对齐用前值填充停牌缺口再对每个特征单独做归一化。归一化我一般用训练集的最小最大值把scaler存下来预测阶段复用同一组参数——这一点后面避坑章节会重点讲因为它是新手最容易翻车的地方。import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler # 读取行情数据index 为交易日 df pd.read_csv(fund_nav.csv, parse_dates[date], index_coldate) df df[[close, volume, turnover]].sort_index() # 交易日对齐按自然日重采样后前向填充再删掉开头可能产生的空值 df df.resample(D).ffill().dropna() # 只对训练段拟合 scaler避免未来信息泄漏 split int(len(df) * 0.8) train_df df.iloc[:split] scaler MinMaxScaler(feature_range(0, 1)) scaler.fit(train_df.values) scaled scaler.transform(df.values) print(训练段长度:, len(train_df), 总长度:, len(df))这段代码的关键在scaler.fit(train_df.values)这一行。很多人图省事对全量数据fit_transform结果测试集的极值参与了归一化回测指标虚高实盘一上就露馅。参数上feature_range(0,1)是 LSTM 的常规选择如果你用 tanh 激活这个区间正好匹配输出范围用 ReLU 系列可以放宽到(-1,1)但没必要。2.2 滑动窗口切分lookback 和 horizon 怎么定LSTM 吃的是三维张量(样本数, 时间步, 特征数)。时间步就是 lookback也就是「用过去多少天预测未来」。horizon 是预测未来第几天。这两个参数没有标准答案但有经验区间日频数据 lookback 取 20 到 60 比较稳对应一个月到一个季度的记忆horizon 取 1 到 5超过 5 天误差会明显放大因为股票基金的中短期噪声占比太高。def make_windows(data, lookback30, horizon1): X, y [], [] for i in range(len(data) - lookback - horizon 1): X.append(data[i:i lookback]) # 过去 lookback 天 y.append(data[i lookback horizon - 1, 0]) # 未来第 horizon 天的收盘价 return np.array(X), np.array(y) LOOKBACK, HORIZON 30, 1 X, y make_windows(scaled, LOOKBACK, HORIZON) # 按时间顺序切分绝不能 shuffle 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, X_test.shape) # (样本, 30, 3)y只取第 0 列是因为我们预测目标是收盘价其他特征作为辅助输入。切分必须按时间顺序train_test_split默认 shuffle 会把未来样本混进训练集这是时序预测里最隐蔽的数据泄漏。如果你看到验证 loss 低得离谱先查这里。2.3 特征选择除了价格还能加什么纯价格序列的 LSTM 很容易退化成「预测昨天」因为相邻交易日相关性太高。想提升信息量可以加几类特征成交量与换手率反映资金活跃度滚动波动率比如 20 日标准差刻画风险状态均线偏离度收盘价除以 MA20把趋势位置显式编码进去。基金数据还可以加折溢价率、份额变化。特征不是越多越好超过 10 个而样本只有几千条时过拟合风险陡增我一般控制在 5 到 8 个。3. LSTM 模型搭建与训练从 Keras 到 PyTorch 的最小可跑版本3.1 网络结构几层、多少隐藏单元股票基金预测不需要深层网络。两层 LSTM 加一层全连接输出就够了隐藏单元 32 到 128 之间。层数多了梯度回传路径变长反而难训单元太少欠拟合曲线学不动。Dropout 放在 LSTM 层之间取 0.2 左右能明显缓解「训练 loss 降、验证 loss 升」的过拟合。import tensorflow as tf from tensorflow.keras import layers, models def build_lstm(lookback, n_features): model models.Sequential([ layers.Input(shape(lookback, n_features)), layers.LSTM(64, return_sequencesTrue), # 第一层保留序列传给下一层 layers.Dropout(0.2), layers.LSTM(32), # 第二层只输出最后时间步 layers.Dropout(0.2), layers.Dense(16, activationrelu), layers.Dense(1) # 回归输出无激活 ]) model.compile(optimizertf.keras.optimizers.Adam(1e-3), lossmse, metrics[mae]) return model model build_lstm(LOOKBACK, X_train.shape[2]) model.summary()return_sequencesTrue是堆叠 LSTM 的必要条件第二层才能接到完整序列。最后一层Dense(1)不加激活函数因为回归输出可以是任意实数。优化器用 Adam学习率 1e-3 是稳妥起点如果 loss 震荡降到 5e-4。损失函数 MSE 对大误差敏感想让模型别被极端值带偏可以换 Huber。3.2 训练配置epoch、batch 与早停训练轮数不是越多越好。股票数据噪声大通常 30 到 80 个 epoch 就收敛再训就是记忆训练集。早停EarlyStopping是必备后悔药监控验证 losspatience 设 8 到 10连续多轮不降就回滚最优权重。from tensorflow.keras import callbacks es callbacks.EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) rlr callbacks.ReduceLROnPlateau(monitorval_loss, factor0.5, patience5, min_lr1e-5) history model.fit( X_train, y_train, validation_split0.15, # 从训练段尾部切验证仍保持时间顺序 epochs80, batch_size32, callbacks[es, rlr], verbose1 )validation_split0.15是从训练集尾部切不是随机切这一点 Keras 默认按顺序取符合时序要求。batch_size 32 适合几千条样本样本上万可以提到 64 或 128训练更快但泛化可能略降。ReduceLROnPlateau在 loss 停滞时减半学习率配合早停能省不少调参时间。3.3 预测与反归一化把数字还原成价格模型输出的是归一化空间的数值必须用之前存的 scaler 反变换回真实价格。这里有个细节scaler 是对多列拟合的反变换时要构造同样列数的数组只把预测列填进去其余列补零再取第 0 列。pred_scaled model.predict(X_test) # 构造与 scaler 维度一致的占位数组 pad np.zeros((len(pred_scaled), scaler.n_features_in_)) pad[:, 0] pred_scaled.flatten() pred_price scaler.inverse_transform(pad)[:, 0] true_pad np.zeros((len(y_test), scaler.n_features_in_)) true_pad[:, 0] y_test true_price scaler.inverse_transform(true_pad)[:, 0] from sklearn.metrics import mean_absolute_error, mean_squared_error mae mean_absolute_error(true_price, pred_price) rmse np.sqrt(mean_squared_error(true_price, pred_price)) print(fMAE{mae:.4f} RMSE{rmse:.4f})评估指标别只看 MSE它受价格量纲影响。MAE 更直观RMSE 对大误差惩罚重。更实用的是看方向准确率预测涨跌方向对的比例这个指标和实际决策更相关。如果 MAE 很小但方向准确率接近 50%说明模型只是学会了「跟着昨天走」没有真正的预测力。4. 避坑与排查LSTM 股票基金预测最常见的 5 个翻车点4.1 现象回测曲线完美实盘一塌糊涂原因几乎都是数据泄漏。要么归一化用了全量数据要么切分时 shuffle 了要么特征里混入了未来信息比如用了当日收盘后才公布的指标。解决方法是把整个流程按时间切成互不重叠的训练、验证、测试三段所有拟合操作只在训练段做验证和测试段只做 transform。写完代码后养成一个习惯打印每段的起止日期肉眼确认没有重叠。4.2 现象训练 loss 一直不降模型像没学先查学习率1e-3 对 LSTM 通常没问题但如果你的输入没归一化梯度会爆炸或消失loss 卡住不动。其次查窗口切分如果 lookback 设成 1LSTM 退化成全连接学不到时序模式。还有一种情况是标签构造错了比如 horizon 偏移量算错导致 X 和 y 错位。排查时先把模型换成线性回归跑一遍如果线性回归能拟合而 LSTM 不能问题在训练配置如果线性回归也不行问题在数据。4.3 现象验证 loss 远高于训练 loss典型过拟合。股票基金数据信噪比低几千条样本配几十万参数过拟合几乎是必然。对策按优先级先加 Dropout0.2 到 0.4再减隐藏单元64 降到 32再减特征数最后才考虑加数据。L2 正则也能用但 LSTM 里效果不如 Dropout 直接。如果这些做完验证 loss 还是高说明这个预测目标本身太难考虑换 horizon 或换标的。4.4 现象预测值是一条平滑直线模型输出了均值。原因是损失函数 MSE 在噪声大的数据上最优解就是预测条件均值而股票收益的条件均值接近零。解决办法有两个方向一是改预测目标不预测价格而预测收益率或涨跌方向分类任务对噪声更鲁棒二是换损失函数用方向性损失或者分位数损失。单纯调网络结构解决不了这个问题这是目标定义的锅。4.5 现象每次训练结果差异很大随机种子没固定。LSTM 权重初始化、Dropout、数据打乱都带随机性。做对比实验时固定tf.random.set_seed、np.random.seed和 Python 的random.seed并且同一组配置跑 3 到 5 次取平均。如果不同种子下指标波动超过 20%说明模型不稳定这时候比较两个模型的优劣没有意义先把方差降下来。5. 把预测模型用起来滚动回测与方向准确率的实操技巧训练完一个 LSTM 只是起点真正决定这套东西值不值得投入的是它在你关心的场景下能不能稳定输出。我一般会做两件事滚动回测和方向准确率拆解。滚动回测walk-forward模拟真实使用节奏用前 N 天训练预测第 N1 天然后把真实值并入训练集窗口前移再预测下一天。这样得到的指标比一次性切分更接近实盘。实现上不用每天重训可以每 20 个交易日重训一次中间用已训练模型滚动预测兼顾成本和真实性。def walk_forward(data, lookback30, retrain_every20): preds, trues [], [] for start in range(lookback, len(data) - 1): if (start - lookback) % retrain_every 0: # 用截至 start 的数据重训此处省略训练细节复用前面函数 X_tr, y_tr make_windows(data[:start], lookback, 1) model build_lstm(lookback, data.shape[1]) model.fit(X_tr, y_tr, epochs30, batch_size32, verbose0) window data[start - lookback:start][np.newaxis, ...] pred model.predict(window, verbose0)[0, 0] preds.append(pred) trues.append(data[start, 0]) return np.array(preds), np.array(trues)方向准确率的算法很简单比较预测值和当前值的差、真实值和当前值的差符号一致就算对。这个指标比 MAE 更能说明模型有没有实用价值。我自己的经验是日频股票基金预测方向准确率能稳定在 53% 到 56% 就已经不错超过 60% 要警惕数据泄漏。别小看这几个百分点扣掉交易成本后能不能剩下才是真正要算的账。还有一个容易被忽略的技巧把 LSTM 的输出和简单基线做对比。基线就是「预测值等于今天收盘价」如果 LSTM 连这个都跑不赢说明模型没学到任何超出惯性的东西。我现在的习惯是任何时序预测项目先跑基线再上 LSTM指标提升幅度才是模型真正的贡献。这套流程走下来你会对「LSTM 能预测什么、不能预测什么」有比看十篇科普更清楚的认识。希望帮到你。本文还有配套的精品资源点击获取