简介这份资源面向具备一定MATLAB基础、希望入门时间序列与机器学习组合建模的学习者与量化研究者核心是用支持向量机改进ARIMA股票价格预测。项目以MATLAB为工具先由ARIMA对历史开盘价建模并自动选取p、d、q参数生成基础预测再引入SVM通过核函数处理非线性关系对预测结果做二次修正最后用R方、均方差和相对误差等指标对比改进前后的精度。压缩包共3个文件包含2个m脚本与1个xlsx数据表整体约13KB脚本分别承担主流程调度与SVM训练预测数据表提供上证指数历史收盘价作为训练与验证来源。目前已有1039人学习下载。读者可据此掌握ARIMA与SVM的衔接思路、评价指标计算方式并替换为其他股票或更长时间范围的数据开展预测研究。1. ARIMA 单打独斗为什么在股票预测上总差一口气用 ARIMA 做股票价格预测的人十有八九经历过同一个场景模型在训练集上拟合得漂漂亮亮残差看着也像白噪声一放到测试集上就原形毕露要么滞后一两拍要么干脆把昨天的价格平移过来当预测。这不是你调参不够努力而是 ARIMA 这类线性模型的天花板就在那里——它擅长捕捉自相关和趋势但对非线性突变、成交量异动、情绪面冲击几乎无能为力。于是就有了「ARIMASVM」这条路线先用 ARIMA 把序列里的线性成分榨干再让支持向量机去啃 ARIMA 啃不动的非线性残差最后把两段预测叠回去。这个思路在股票价格预测里被反复验证过也是很多人搜「svm支持向量机python代码」时真正想找的东西。这篇笔记就按我实际落地的顺序把数据怎么切、ARIMA 阶数怎么定、SVM 核函数和参数怎么选、残差怎么对齐、坑在哪一条条讲清楚。适合已经会写 Python、跑过 sklearn但被单模型精度卡住的从业者。2. 拆解 ARIMASVM 混合结构线性与非线性怎么分工2.1 混合模型的数学直觉残差里藏着 SVM 的活ARIMA(p,d,q) 的本质是把序列 (y_t) 拆成线性可解释的部分和误差项[ y_t L_t e_t ]其中 (L_t) 是 ARIMA 拟合出的线性预测(e_t) 是残差。如果残差是纯白噪声说明线性模型已经吃干净了没必要上 SVM。但股票残差往往还有结构——波动聚集、非线性依赖、对前期大幅涨跌的滞后反应这些就是 SVM 的输入。常见做法是把残差序列 (e_t) 用滑动窗口重构成监督学习样本用前 (k) 个残差预测第 (k1) 个残差。SVM 回归SVR学的是 (e_t f(e_{t-1}, e_{t-2}, ..., e_{t-k})) 这个映射再把预测出的残差加回 ARIMA 的线性预测[ \hat{y}_t \hat{L}_t \hat{e}_t ]这里有个容易翻车的点ARIMA 做差分后残差长度和原始序列对不上必须先把差分还原再对齐残差否则叠加时整体错位精度反而比单 ARIMA 还差。2.2 数据准备与平稳性处理的最小可跑流程先上代码把数据读取、差分、平稳性检验跑通。这里用 pandas 和 statsmodels数据假设是一列收盘价。import numpy as np import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 读取数据假设 csv 有 date 和 close 两列 df pd.read_csv(stock.csv, parse_dates[date], index_coldate) series df[close].astype(float) # 一阶差分 diff1 series.diff().dropna() # ADF 检验p 值小于 0.05 认为平稳 adf_result adfuller(diff1) print(ADF Statistic:, adf_result[0]) print(p-value:, adf_result[1]) # 画 ACF/PACF 辅助定阶 fig, axes plt.subplots(2, 1, figsize(10, 6)) plot_acf(diff1, axaxes[0], lags30) plot_pacf(diff1, axaxes[1], lags30) plt.show()逻辑说明diff()做一阶差分消除趋势adfuller返回的 p 值决定要不要继续差分。ACF 拖尾、PACF 截尾通常对应 AR 模型反过来对应 MA。参数上lags30是经验值日频股票数据看 20 到 40 个滞后足够太长反而让图糊成一团。提示差分次数 d 不要盲目加到平稳为止股票价格一阶差分通常就够二阶差分会把有效信号也差没残差全是噪声SVM 学不到东西。2.3 ARIMA 阶数选择AIC 网格搜索与残差白噪声检验定阶不要靠眼睛看 ACF/PACF 拍脑袋用 AIC 网格搜索更稳。下面这段在 p、q 各 0 到 5 的范围里搜。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(6): for q in range(6): try: model ARIMA(series, order(p, 1, q)) result model.fit() if result.aic best_aic: best_aic result.aic best_order (p, 1, q) best_model result except Exception: continue print(Best order:, best_order, AIC:, best_aic) # 残差白噪声检验Ljung-Box from statsmodels.stats.diagnostic import acorr_ljungbox lb acorr_ljungbox(best_model.resid, lags[10], return_dfTrue) print(lb)逻辑说明order(p,1,q)里的 1 是前面定下的差分次数。AIC 越小越好但别只看 AIC还要看 Ljung-Box 的 p 值大于 0.05 才说明残差没有明显自相关剩下的才值得交给 SVM。如果 Ljung-Box 拒绝白噪声假设说明 ARIMA 还没榨干线性成分先回去调阶数别急着上 SVM。参数说明lags[10]是检验前 10 阶自相关日频数据用 10 到 20 都合理。best_model.resid是残差序列注意它和原始序列长度差 d 个点后面重构样本时要处理。3. 用 SVM 啃残差样本重构、核函数与参数搜索3.1 残差滑动窗口重构把时间序列变成监督学习样本SVM 不认时间序列只认特征矩阵 X 和标签 y。所以要把残差 (e_t) 用滑动窗口切成「前 k 个预测下一个」的样本对。def create_dataset(resid, look_back5): X, y [], [] for i in range(len(resid) - look_back): X.append(resid[i:i look_back]) y.append(resid[i look_back]) return np.array(X), np.array(y) look_back 5 X_svm, y_svm create_dataset(best_model.resid.values, look_back) print(X shape:, X_svm.shape, y shape:, y_svm.shape)逻辑说明look_back5表示用前 5 个残差预测第 6 个。这个值不是越大越好股票残差的有效记忆通常很短5 到 10 之间试。窗口太大SVM 输入维度高容易过拟合训练也慢。参数说明best_model.resid.values是 numpy 数组直接切片。注意残差里可能含 NaN切片前先dropna()否则 SVM 训练会报错。3.2 核函数选择RBF 为什么是残差预测的默认答案SVR 的核函数决定它怎么衡量样本相似度。线性核适合线性关系多项式核参数多容易过拟合RBF 核高斯核能把样本映射到无穷维对非线性残差最稳。我一般先用 RBF 打底再对比线性核看有没有提升。from sklearn.svm import SVR from sklearn.preprocessing import StandardScaler from sklearn.model_selection import TimeSeriesSplit, GridSearchCV from sklearn.pipeline import Pipeline # 时间序列不能随机切分用 TimeSeriesSplit tscv TimeSeriesSplit(n_splits5) pipe Pipeline([ (scaler, StandardScaler()), (svr, SVR(kernelrbf)) ]) param_grid { svr__C: [0.1, 1, 10, 100], svr__gamma: [scale, 0.01, 0.1, 1], svr__epsilon: [0.001, 0.01, 0.1] } grid GridSearchCV(pipe, param_grid, cvtscv, scoringneg_mean_squared_error, n_jobs-1) grid.fit(X_svm, y_svm) print(Best params:, grid.best_params_) print(Best MSE:, -grid.best_score_)逻辑说明StandardScaler必须加SVR 对特征尺度敏感残差量纲不统一会让 RBF 核直接失效。TimeSeriesSplit保证训练集永远在测试集之前避免用未来数据预测过去这种低级但致命的错误。neg_mean_squared_error是因为 sklearn 的 scoring 统一越大越好所以取负。参数说明C控制惩罚力度越大越容易过拟合gamma控制 RBF 核的影响半径scale是 1/(特征数×方差)通常够用epsilon是不敏感损失带宽股票残差噪声大epsilon设 0.01 到 0.1 之间能过滤小波动。3.3 叠加预测与还原别在差分还原上翻车SVM 预测的是残差最终要加回 ARIMA 的线性预测再还原差分。这一步顺序错了前面全白做。# ARIMA 线性预测原始尺度 linear_pred best_model.predict(start..., end...) # SVM 残差预测 resid_pred grid.predict(X_svm) # 对齐长度SVM 预测比残差少 look_back 个点 aligned_resid_pred np.concatenate([np.zeros(look_back), resid_pred]) # 叠加 final_pred linear_pred aligned_resid_pred逻辑说明best_model.predict返回的是原始尺度上的线性预测已经包含差分还原。SVM 残差预测前面补look_back个零是为了长度对齐因为前 look_back 个点没有足够的历史残差做输入。补零会让开头几个点预测偏线性如果在意开头精度可以截掉这部分再评估。参数说明start和end按测试集索引填。对齐时务必确认linear_pred和aligned_resid_pred长度一致不一致就检查差分次数和 look_back。4. 避坑与排查ARIMASVM 落地时最容易翻车的 5 个点4.1 现象混合模型精度反而低于单 ARIMA原因残差对齐错位或者 SVM 在噪声上过拟合把随机波动当信号学。解决先单独评估 ARIMA 的残差是否还有自相关Ljung-Box p 值大于 0.05 就别硬上 SVM对齐时打印两段预测的长度和前 10 个值肉眼确认没有整体平移。4.2 现象SVM 训练集 MSE 极低测试集爆炸原因用了随机切分而不是时间序列切分或者C和gamma调得过大。解决强制TimeSeriesSplitC从 0.1 开始往上试gamma优先用scale不要一上来就 1 或 10。4.3 现象预测曲线整体滞后一天原因股票价格自相关极强任何模型都容易学成「昨天价格等于今天价格」。解决不要只看价格预测改看收益率或差分后的序列或者在特征里加入成交量、换手率等外生变量打破单一滞后依赖。4.4 现象Ljung-Box 检验通过但 SVM 没提升原因残差虽然还有结构但结构太弱SVM 学到的增益被自身误差抵消。解决对比叠加前后的 RMSE、MAE、方向准确率如果提升不到 1%说明这个数据集上混合模型性价比不高不如把精力放在特征工程上。4.5 现象每次跑出来结果不一样原因SVM 参数搜索的交叉验证切分有随机性或者数据里有缺失值导致样本数变化。解决固定random_state缺失值统一用前向填充或插值不要一次删一次填。5. 进阶技巧用方向准确率和滚动重训验证混合模型值不值得做价格预测的 RMSE 好看不代表能赚钱股票里方向比幅度重要。我习惯在叠加预测后加一个方向准确率评估def direction_accuracy(y_true, y_pred): true_dir np.sign(np.diff(y_true)) pred_dir np.sign(np.diff(y_pred)) return np.mean(true_dir pred_dir) print(Direction Accuracy:, direction_accuracy(y_test, final_pred))逻辑说明np.diff算一阶差分np.sign取方向比较真实方向和预测方向一致的比例。这个指标比 RMSE 更贴近交易场景低于 50% 说明模型连涨跌都分不清RMSE 再低也没用。参数说明y_test是测试集真实价格final_pred是叠加后的预测价格两者长度必须一致。另一个技巧是滚动重训不要用一次训练好的模型预测整个测试集而是每预测 N 个点就把新数据加进训练集重新拟合 ARIMA 和 SVM。股票市场结构会变三年前的参数今天未必适用。window_size 250 # 约一年交易日 retrain_step 20 for i in range(window_size, len(series) - retrain_step, retrain_step): train series[i - window_size:i] test series[i:i retrain_step] # 重新定阶、拟合 ARIMA、训练 SVM、预测 test # 累积预测结果逻辑说明window_size250是经验值对应一年交易日能覆盖一个完整市场周期。retrain_step20表示每 20 个交易日重训一次太频繁计算量大太稀疏模型跟不上市场变化。参数说明窗口大小和重训步长要根据数据频率调日频用 250 和 20周频用 52 和 4分钟频另说。我自己的习惯是任何混合模型上线前先跑一遍单 ARIMA 基线再跑混合模型对比 RMSE 和方向准确率提升不明显就不硬推。股票预测没有银弹ARIMASVM 只是把线性残差里的非线性部分多榨出一点别指望它翻天覆地。希望帮到你。本文还有配套的精品资源点击获取
