ARIMA+LSTM组合模型预测上证指数:从原理到实战避坑指南

发布时间:2026/10/12 17:58:48
ARIMA+LSTM组合模型预测上证指数:从原理到实战避坑指南 简介这份资源面向金融时间序列预测的学习者与研究者提供基于MATLAB实现的ARIMA与LSTM组合预测方案用于上证指数及沪深300等指数的走势建模。针对新版MATLAB中ARIMA函数易报错的问题代码做了兼容处理并利用LSTM对时序数据的建模优势提升预测效果适合本科及以上层次用于课程设计、论文复现或扩展创新。压缩包共19个文件约3.23MB包含10个m脚本、5个csv数据、2个mat文件、1个xls及1个asv备份脚本覆盖ARIMA与LSTM的建模、训练与预测流程数据文件提供指数及加密货币等序列mat文件保存中间结果xls用于原始行情导入。资源已有198人学习下载代码完整且带注释便于读者直接运行、理解组合模型思路并在此基础上替换数据或调整网络结构开展自己的实验。1. 从单模型翻车说起ARIMALSTM 做上证指数预测到底靠不靠谱很多人第一次做上证指数预测都是先拿 ARIMA 单独跑一遍发现短期还行、拐点全错再换 LSTM 单独跑发现拟合曲线漂亮得像作弊一到实盘就滞后半拍。这不是模型不行而是你把线性部分和非线性部分硬塞给了一个模型。上证指数这种日频数据既有明显的趋势和均值回归线性可建模又有政策消息、情绪脉冲带来的非线性跳变LSTM 的强项单一模型必然顾此失彼。ARIMALSTM 的组合思路就是先用 ARIMA 把线性结构吃掉再把残差交给 LSTM 去啃非线性尾巴最后把两段预测相加。这套方案适合有 Python 基础、做过时间序列入门、想把手上的沪深指数数据真正跑出一套可复现流程的人。代码完整、数据齐全不是重点重点是每一步为什么这么设、参数怎么调、哪里最容易翻车。2. 数据准备与平稳性检验上证指数日线怎么清洗才不会污染模型2.1 数据字段选择与缺失值处理做上证指数预测最常用的数据源是日频 OHLCV开盘、最高、最低、收盘、成交量。但真正喂给模型的通常只用收盘价序列因为 ARIMA 和 LSTM 都是单变量时间序列模型多变量虽然能上但会引入额外噪声和维度灾难。我一般会保留日期、收盘价、成交量三列成交量作为后续验证的辅助变量不直接进主模型。缺失值处理是第一个坑。沪深指数在交易日不会缺数据但如果你用的是自己拼接的数据节假日、停牌、数据源切换都可能造成缺口。常见做法是先按交易日历 reindex再用前向填充ffill补价格成交量缺口用 0 或均值填。不要用插值插值会人为制造平滑让模型误以为那段没有波动。import pandas as pd import numpy as np # 读取上证指数日线数据假设字段为 date, open, high, low, close, volume df pd.read_csv(sh_index.csv, parse_dates[date]) df df.sort_values(date).set_index(date) # 只保留收盘价和成交量 df df[[close, volume]].copy() # 按交易日历重采样缺失交易日先标记 df df.asfreq(B) # B 表示工作日实际应按交易所日历 df[close] df[close].ffill() df[volume] df[volume].fillna(0) # 删除仍然为空的行通常是数据起始段 df df.dropna() print(df.head()) print(样本区间, df.index.min(), 至, df.index.max(), 共, len(df), 条)这段代码的逻辑是先按日期排序并设为索引保证时间顺序然后用asfreq(B)把缺失的工作日补出来价格前向填充、成交量补零最后 dropna 清掉开头没有价格的空行。参数上asfreq的频率要根据实际交易所日历调整A 股不是简单的工作日春节、国庆调休都会影响严谨做法是引入pandas_market_calendars或交易所日历文件。成交量补零而不是补均值是因为停牌日确实没有成交补均值会制造虚假信息。2.2 平稳性检验与差分阶数确定ARIMA 的“I”就是差分差几次由平稳性检验决定。上证指数收盘价几乎肯定是非平稳的直接做 ADF 检验 p 值大概率大于 0.05。一阶差分后通常就平稳了极少数阶段可能需要二阶但二阶差分容易过度差分把有用信号也差掉。from statsmodels.tsa.stattools import adfuller def adf_test(series, name): result adfuller(series.dropna(), autolagAIC) print(f{name} ADF 统计量: {result[0]:.4f}, p 值: {result[1]:.4f}) for key, value in result[4].items(): print(f 临界值 {key}: {value:.4f}) return result[1] # 原始序列 p_raw adf_test(df[close], 原始收盘价) # 一阶差分 p_diff1 adf_test(df[close].diff().dropna(), 一阶差分) # 二阶差分备用 p_diff2 adf_test(df[close].diff().diff().dropna(), 二阶差分)逻辑说明ADF 检验的原假设是“存在单位根非平稳”。p 值小于 0.05 才能拒绝原假设认为平稳。参数autolagAIC让 statsmodels 自动选滞后阶数避免手动试。实际跑下来原始收盘价 p 值通常接近 1一阶差分后 p 值会掉到 0.01 以下所以 d1 是绝大多数情况下的选择。如果一阶差分还不平稳先别急着上二阶检查是不是数据里混入了异常值或结构断点比如股权分置改革、熔断机制那几段可以考虑分段建模或加虚拟变量。2.3 训练集/测试集划分的时间顺序原则时间序列不能随机打乱划分这是血泪经验。必须按时间切前 80% 做训练后 20% 做测试或者用滚动窗口。更严谨的做法是 walk-forward validation每次用前 N 天训练预测后 M 天然后窗口向前滑动。这样评估出来的误差才接近实盘。train_size int(len(df) * 0.8) train df[close][:train_size] test df[close][train_size:] print(训练集, train.index.min(), 至, train.index.max(), 共, len(train)) print(测试集, test.index.min(), 至, test.index.max(), 共, len(test))参数上80/20 是常用起点但如果你的数据只有几百条测试集太短会导致评估不稳定可以改成 70/30 或直接用滚动预测。注意测试集绝对不能参与任何拟合、差分阶数选择、归一化参数计算否则就是数据泄露回测结果会好得离谱实盘一塌糊涂。3. ARIMA 建模p、d、q 三个参数怎么定才不会靠猜3.1 用 ACF/PACF 初定阶数再用 AIC 网格搜索兜底ARIMA(p,d,q) 里 d 已经由差分确定剩下 p 和 q。传统做法是看 ACF 和 PACF 图ACF 拖尾、PACF 截尾选 AR(p)ACF 截尾、PACF 拖尾选 MA(q)都拖尾就上 ARMA。但看图这件事玄学成分大不同人看出不同阶数。我一般会用 AIC 网格搜索兜底把 p 和 q 在 0 到 5 之间遍历选 AIC 最小的组合。import warnings from statsmodels.tsa.arima.model import ARIMA warnings.filterwarnings(ignore) best_aic np.inf best_order None best_model None for p in range(0, 6): for q in range(0, 6): try: model ARIMA(train, order(p, 1, q)) fitted model.fit() if fitted.aic best_aic: best_aic fitted.aic best_order (p, 1, q) best_model fitted except Exception as e: continue print(最优阶数, best_order, AIC, best_aic) print(best_model.summary())逻辑说明order(p,1,q)里的 1 就是前面确定的差分阶数 d。遍历范围 0 到 5 是经验值上证指数日线一般不会超过这个范围再大容易过拟合。AIC 越小越好但要注意AIC 只衡量拟合优度和复杂度不保证残差白噪声。跑完还要看残差检验。参数上ARIMA类来自 statsmodelsfit()默认用 MLE。如果数据量大可以加enforce_stationarityFalse和enforce_invertibilityFalse加速但会牺牲一些稳定性。实际项目中我一般先跑网格再对最优的几个模型做残差 Ljung-Box 检验选残差最接近白噪声的那个。3.2 残差提取LSTM 要学的到底是什么ARIMA 拟合完之后残差 真实值 - ARIMA 预测值。这个残差里剩下的就是线性模型没抓到的非线性结构。LSTM 的任务就是学这个残差的模式。# 用最优 ARIMA 模型对训练集做拟合提取残差 train_pred best_model.predict(starttrain.index[0], endtrain.index[-1]) residuals train - train_pred residuals residuals.dropna() print(残差统计) print(residuals.describe()) print(残差 ADF p 值, adfuller(residuals, autolagAIC)[1])逻辑说明predict返回的是 ARIMA 对训练集的拟合值残差就是真实减拟合。残差应该围绕 0 波动如果均值明显不为 0说明 ARIMA 有系统偏差。ADF 检验残差是为了确认它平稳平稳序列 LSTM 才好学。如果残差还有明显趋势说明 d 选少了或者数据有结构断点。参数上残差序列的长度和训练集一致但开头几个点因为差分和滞后会缺失dropna 之后会短一点。LSTM 训练时要用这个残差序列而不是原始价格序列这是整个组合模型的核心。3.3 用 ARIMA 做基准预测并保存模型训练好 ARIMA 后要对测试集做预测作为最终组合的线性部分。同时把模型保存下来方便复现。import joblib # 对测试集做预测 test_pred_arima best_model.predict(starttest.index[0], endtest.index[-1]) # 保存模型 joblib.dump(best_model, arima_model.pkl) print(ARIMA 测试集预测前 5 条) print(test_pred_arima.head())逻辑说明predict的 start 和 end 用测试集索引statsmodels 会自动做样本外预测。注意ARIMA 的样本外预测是递归的每一步都用前一步的预测值误差会累积所以预测步长越长越不准。保存模型用 joblib比 pickle 对 numpy 数组更友好。参数上如果测试集很长可以只预测前 30 到 60 个点再滚动更新模型这样更接近实盘。保存的模型文件后续可以直接 load 回来不用重新拟合。4. LSTM 残差建模把非线性尾巴交给神经网络4.1 残差序列的归一化与滑窗构造LSTM 对输入尺度敏感残差虽然数值不大但仍要归一化。我一般用 MinMaxScaler 把残差缩到 [-1, 1] 或 [0, 1]。然后构造滑窗用前 look_back 天的残差预测下一天。from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout scaler MinMaxScaler(feature_range(-1, 1)) residuals_scaled scaler.fit_transform(residuals.values.reshape(-1, 1)) def create_dataset(data, look_back20): X, y [], [] for i in range(len(data) - look_back): X.append(data[i:ilook_back, 0]) y.append(data[ilook_back, 0]) return np.array(X), np.array(y) look_back 20 X_train, y_train create_dataset(residuals_scaled, look_back) X_train X_train.reshape((X_train.shape[0], X_train.shape[1], 1)) print(LSTM 训练集形状, X_train.shape, y_train.shape)逻辑说明MinMaxScaler的 fit 只能在训练集残差上做测试集残差要用同一个 scaler 变换否则泄露。create_dataset把序列切成滑窗look_back20 表示用过去 20 天预测第 21 天。这个参数很关键太小抓不到模式太大训练慢且容易过拟合。上证指数日线我一般试 10、20、30用验证集选。参数上reshape成 (样本数, 时间步, 特征数)LSTM 要求三维输入。特征数为 1 是因为只用了残差单变量。如果想加成交量残差可以扩到 2但会复杂化。4.2 LSTM 网络结构与训练参数设置网络结构不用太深两层 LSTM 加 Dropout 足够。上证指数残差模式不会特别复杂层数多了反而过拟合。model Sequential() model.add(LSTM(64, return_sequencesTrue, input_shape(look_back, 1))) model.add(Dropout(0.2)) model.add(LSTM(32, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(1)) model.compile(optimizeradam, lossmse) model.summary() history model.fit( X_train, y_train, epochs50, batch_size32, validation_split0.1, verbose1 )逻辑说明第一层 LSTM 64 个单元return_sequencesTrue把序列传给下一层第二层 32 个单元输出最后一步Dropout 0.2 防过拟合Dense(1) 输出预测值。损失用 MSE优化器 Adam 是默认首选。参数上epochs 50 是起点看 validation loss 是否还在降早停可以加EarlyStopping。batch_size 32 适合几千条样本。validation_split0.1 从训练集里再切 10% 做验证不碰测试集。如果训练 loss 降但验证 loss 升就是过拟合减层数或加 Dropout。4.3 残差预测还原与组合预测LSTM 输出的是归一化后的残差预测要反归一化再加回 ARIMA 的预测才是最终价格预测。# 对测试集残差做预测 # 先用 ARIMA 对测试集的预测值算出测试集残差真实 - ARIMA test_residuals test - test_pred_arima test_residuals_scaled scaler.transform(test_residuals.values.reshape(-1, 1)) X_test, y_test create_dataset(test_residuals_scaled, look_back) X_test X_test.reshape((X_test.shape[0], X_test.shape[1], 1)) lstm_pred_scaled model.predict(X_test) lstm_pred scaler.inverse_transform(lstm_pred_scaled) # 组合预测ARIMA 预测 LSTM 残差预测 # 注意对齐长度LSTM 预测从第 look_back 天开始 final_pred test_pred_arima.values[look_back:] lstm_pred.flatten() # 真实值对齐 true_values test.values[look_back:] # 评估 from sklearn.metrics import mean_squared_error, mean_absolute_error rmse np.sqrt(mean_squared_error(true_values, final_pred)) mae mean_absolute_error(true_values, final_pred) print(f组合模型 RMSE: {rmse:.2f}, MAE: {mae:.2f})逻辑说明测试集残差 测试集真实值 - ARIMA 测试集预测值。用训练好的 scaler 变换构造滑窗LSTM 预测。反归一化后从第 look_back 天开始与 ARIMA 预测相加。长度对齐是关键LSTM 因为滑窗会少掉前 look_back 个点。参数上RMSE 和 MAE 是常用指标但要注意价格预测的 RMSE 受价格量级影响上证指数 3000 点附近RMSE 几十点算正常。更直观的是看 MAPE 或方向准确率。如果组合模型还不如单独 ARIMA检查残差是否真的还有非线性或者 LSTM 过拟合了。5. 避坑与排查这套组合模型最容易翻车的 5 个地方5.1 现象回测 RMSE 极低实盘完全对不上原因数据泄露。最常见的是归一化 scaler 用了全量数据 fit或者差分阶数、ARIMA 阶数用测试集选出来的。另一个隐蔽原因是滑窗构造时训练集和测试集边界处混入了未来信息。解决严格按时间切分所有 fit 操作只在训练集上做。测试集变换用训练集的 scaler。ARIMA 阶数选择只用训练集。滑窗构造时测试集的第一个窗口应该来自测试集内部不能跨到训练集末尾。5.2 现象LSTM 训练 loss 一直不降或者降了但预测是一条直线原因残差序列本身接近白噪声没有可学的模式。或者学习率、batch_size 设置不当梯度消失。解决先画残差 ACF/PACF如果残差已经像白噪声说明 ARIMA 已经把线性部分吃干净了LSTM 学不到东西这时候组合模型不会比 ARIMA 好。可以尝试换更复杂的 ARIMA 或者直接放弃 LSTM。如果残差还有结构检查 LSTM 输入是否归一化、学习率是否太大试 0.001 或 0.0001、batch_size 是否太小。5.3 现象组合预测在拐点处滞后严重原因LSTM 本质是自回归对突变反应慢。ARIMA 也是线性外推拐点处两者都滞后。解决这是模型固有局限不要指望纯时序模型抓拐点。可以加入外部变量比如成交量突变、波动率指标或者用分类模型先判断涨跌方向再让回归模型预测幅度。另一个做法是缩短 look_back让模型更关注近期变化但会牺牲稳定性。5.4 现象ARIMA 拟合报错 “Non-stationary starting autoregressive parameters”原因数据差分后仍不平稳或者 p 阶数太高导致参数估计不稳定。解决先确认 d 是否选够ADF 检验一阶差分 p 值是否小于 0.05。如果还不行检查数据有没有异常值比如单日暴涨暴跌可以 winsorize 处理。或者降低 p 和 q 的上限从 0 到 3 重新搜。statsmodels 的 ARIMA 可以加enforce_stationarityFalse绕过但预测会不可靠不推荐。5.5 现象LSTM 预测结果每次跑都不一样原因神经网络随机初始化且没有设随机种子。解决在 import tensorflow 之后、建模型之前设tf.random.set_seed(42)和np.random.seed(42)。但即使这样GPU 上的某些操作仍可能有微小非确定性。如果要求完全可复现用 CPU 训练或者固定所有随机源。实际项目中我一般跑 5 次取平均减少随机性影响。6. 进阶技巧用滚动预测和方向准确率验证组合模型是否值得上6.1 滚动窗口预测更接近实盘的评估方式一次性预测整个测试集误差会累积评估偏乐观。滚动预测是每次只预测下一天然后把真实值加入历史重新拟合或更新模型。这样更接近实盘但计算量大。def walk_forward_arima_lstm(series, train_size, look_back20): predictions [] for i in range(train_size, len(series)): train_data series[:i] # ARIMA 拟合 model_arima ARIMA(train_data, orderbest_order).fit() arima_pred model_arima.forecast(steps1)[0] # 残差 residuals train_data - model_arima.predict(starttrain_data.index[0], endtrain_data.index[-1]) residuals residuals.dropna() # 这里简化实际应重新训练 LSTM 或用已训练模型 # 为演示直接用已训练 LSTM 对最新残差窗口预测 # 真实项目中建议每 N 步重新训练一次 LSTM predictions.append(arima_pred) # 占位实际应加 LSTM 残差预测 return predictions逻辑说明这个函数演示了滚动框架但完整实现需要每步重新提取残差、构造滑窗、用 LSTM 预测。实际中为了效率可以每 20 或 60 步重新训练一次 LSTM中间用已训练模型推理。参数上train_size 是初始训练长度look_back 和之前一致。6.2 方向准确率比 RMSE 更实用的指标价格预测的 RMSE 受量级影响方向准确率涨跌判断正确比例更直观。如果方向准确率能稳定在 55% 以上这套模型就有实用价值。def direction_accuracy(true_values, pred_values): true_diff np.diff(true_values) pred_diff np.diff(pred_values) correct np.sum((true_diff 0) (pred_diff 0)) np.sum((true_diff 0) (pred_diff 0)) return correct / len(true_diff) acc direction_accuracy(true_values, final_pred) print(f方向准确率{acc:.2%})逻辑说明比较真实涨跌和预测涨跌的符号一致就算对。注意这个指标对随机波动敏感短期日频数据方向准确率很难超过 60%50% 左右就是随机水平。如果只有 50%说明模型没有方向判断能力只能做幅度参考。6.3 组合权重ARIMA 和 LSTM 一定要 1:1 相加吗不一定。可以给 ARIMA 和 LSTM 的预测各加一个权重用验证集优化。常见做法是线性回归融合或者简单网格搜索权重。best_w 0 best_rmse np.inf for w in np.arange(0, 1.1, 0.1): combined w * test_pred_arima.values[look_back:] (1 - w) * lstm_pred.flatten() rmse np.sqrt(mean_squared_error(true_values, combined)) if rmse best_rmse: best_rmse rmse best_w w print(f最优 ARIMA 权重{best_w:.1f}, RMSE{best_rmse:.2f})逻辑说明w 是 ARIMA 权重1-w 是 LSTM 权重。在验证集上搜最优 w再应用到测试集。参数上步长 0.1 够用更细可以 0.05。注意权重也要在验证集上选不能用测试集否则又是泄露。6.4 我踩过的坑与固定习惯这套 ARIMALSTM 组合我前后跑了不下十遍最大的教训是不要迷信复杂模型。很多时候 ARIMA 单独跑就能达到组合模型 90% 的效果LSTM 带来的提升有限但工程复杂度翻倍。如果方向准确率没有显著提升不如把精力花在数据质量和特征工程上。我现在的固定习惯是先跑 ARIMA 基准记录 RMSE 和方向准确率再上 LSTM 残差只有验证集上方向准确率提升超过 3 个百分点才考虑保留。另外随机种子、数据版本、参数配置全部写进配置文件不然两周后自己都复现不出来。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询