从GDP预测到股价分析:Matlab时间序列建模第一步,用adftest搞定数据平稳性(附完整代码)
当你第一次拿到某只股票的历史价格数据,或是某地区过去20年的GDP记录时,脑海中可能已经浮现出各种预测模型——ARIMA、VAR、GARCH...但先别急,这些模型都有一个共同的前提假设:数据必须是平稳的。这就好比在建造摩天大楼前,必须先确保地基稳固一样。
时间序列分析中,平稳性检验就是那个"打地基"的关键步骤。而ADF检验(Augmented Dickey-Fuller test)则是这个领域最常用的工具之一。不同于简单的目测图表判断,ADF检验通过严格的统计假设检验,给出数据是否平稳的科学结论。本文将带你从实际应用场景出发,完整掌握如何用Matlab的adftest函数进行平稳性检验,并给出可直接复用的代码模板。
1. 为什么平稳性检验是建模的第一步?
想象你正在分析某科技公司过去5年的季度营收数据。原始序列显示出明显的增长趋势——这本身就是非平稳的典型特征。如果直接对这样的数据拟合ARIMA模型,结果很可能是完全错误的。因为大多数时间序列模型都基于一个核心假设:数据的统计特性(如均值、方差)不随时间变化。
平稳性的三个核心特征:
- 均值恒定(无趋势)
- 方差恒定(波动幅度稳定)
- 自协方差只与时间间隔有关,与具体时间点无关
在金融领域,直接对股价建模往往效果不佳,而对收益率(价格的变化率)建模则更合理,正是因为收益率序列通常更接近平稳状态。这就是为什么ADF检验会成为量化分析师工具箱中的标配。
提示:即使你最终使用的是深度学习等非线性方法,平稳化处理也能显著提升模型性能和训练稳定性。
2. ADF检验原理与Matlab实现详解
ADF检验的核心思想是检验时间序列是否存在单位根(unit root)——这是导致非平稳性的主要原因。Matlab的adftest函数封装了完整的检验流程,我们只需要关注如何正确使用和解读结果。
2.1 基础调用方式
最简单的调用只需要传入时间序列数据:
y = randn(100,1); % 生成随机序列(通常是平稳的) h = adftest(y);这里h=1表示拒绝原假设(即序列平稳),h=0则表示不能拒绝原假设(序列非平稳)。
2.2 完整参数调用与结果解读
更专业的用法是获取全部输出参数:
[h, pValue, stat, cValue] = adftest(y, 'alpha', 0.05);参数说明表:
| 参数 | 含义 | 判断准则 |
|---|---|---|
| h | 检验结果 | 1=平稳,0=非平稳 |
| pValue | P值 | p<α则拒绝原假设 |
| stat | 检验统计量 | stat<cValue则拒绝原假设 |
| cValue | 临界值 | 与stat比较使用 |
三种判断方法本质上是等价的,实践中通常以h为主,其他参数作为辅助验证。
3. 实战案例:从原始数据到平稳序列
让我们用一个完整的案例演示如何处理非平稳数据。假设我们有某上市公司2010-2022年的月度收盘价数据。
3.1 数据导入与初步分析
首先加载并可视化原始数据:
data = readtable('stock_price.csv'); price = data.Close; dates = datetime(data.Date); figure plot(dates, price, 'LineWidth', 1.5) title('原始股价序列') xlabel('日期') ylabel('价格') grid on如果看到明显的上升/下降趋势或波动幅度变化,这已经暗示了非平稳性。
3.2 执行ADF检验
对原始价格序列进行检验:
[h, p, stat, cv] = adftest(price); fprintf('h=%d, p=%.4f, stat=%.4f, cv=%.4f\n', h, p, stat, cv);预期结果很可能是h=0(非平稳),因为股价通常具有趋势。
3.3 差分处理与再检验
对数据进行一阶差分(计算收益率):
returns = diff(price) ./ price(1:end-1); % 简单收益率 figure plot(dates(2:end), returns) title('股价收益率序列') [h_ret, p_ret] = adftest(returns);收益率序列通常会通过平稳性检验。如果仍未通过,可以尝试:
- 更高阶差分
- 对数变换
- 季节差分(针对季节性数据)
4. 完整工作流代码模板
以下是一个可直接复用的ADF检验模板:
%% 数据准备 data = readtable('your_data.csv'); y = data.Value; % 替换为你的数据列 %% 可视化原始序列 figure subplot(2,1,1) plot(y, 'b', 'LineWidth', 1.5) title('原始时间序列') %% ADF检验 [h, p, stat, cv] = adftest(y, 'alpha', 0.05); disp(['原始序列检验结果: h=', num2str(h), ', p=', num2str(p)]) %% 差分处理 d = 1; % 差分阶数 ydiff = diff(y, d); subplot(2,1,2) plot(ydiff, 'r', 'LineWidth', 1.5) title([num2str(d), '阶差分序列']) %% 检验差分后序列 [h_diff, p_diff] = adftest(ydiff); disp(['差分序列检验结果: h=', num2str(h_diff), ', p=', num2str(p_diff)]) %% 自动寻找合适差分阶数(可选) maxD = 3; % 最大尝试差分阶数 for d = 1:maxD yd = diff(y, d); [hd, pd] = adftest(yd); if hd == 1 fprintf('找到平稳序列:d=%d\n', d); break; end end5. 进阶技巧与常见问题
5.1 检验模型形式选择
adftest支持三种回归形式,通过'model'参数指定:
'ARD'(默认):包含截距项和趋势项'TS':只包含趋势项'NC':不含常数项和趋势项
选择原则:
- 如果序列明显有趋势,用
'ARD' - 如果序列围绕非零均值波动但无趋势,用
'TS' - 如果序列围绕零均值波动,用
'NC'
5.2 滞后阶数选择
'lags'参数控制检验中的滞后阶数。实践中可以:
- 从0开始逐步增加,直到残差无自相关
- 使用信息准则(AIC/BIC)自动选择
% 自动选择滞后阶数 [h, p] = adftest(y, 'lags', 0:10); % 测试0-10阶5.3 典型问题排查
问题1:为什么差分后序列仍不平稳?
- 可能是季节性未被处理,尝试季节差分
- 数据可能存在结构性变化(如突变点)
问题2:ADF检验结果与KPSS检验矛盾怎么办?
- 这种情况确实会发生,通常更信任ADF结果
- 可能需要结合其他平稳化方法
问题3:如何处理高频金融数据?
- 原始价格序列几乎肯定非平稳
- 收益率/对数收益率通常是平稳的
- 超高频数据可能需要考虑微观结构噪声
在实际项目中,我通常会创建一个check_stationarity函数,封装ADF检验、可视化和自动差分逻辑,这样在分析新数据时可以快速调用。对于特别长的时间序列,还会考虑滚动窗口检验,因为数据的平稳性可能会随时间变化。