【维克】ARMA模型:AR与MA的完美结合

系列:年化20%量化笔记 · 第44篇

关键词:#ARMA #混合模型 #时间序列 #模型组合 #预测精度 #Box-Jenkins方法

前两篇,我们分别聊了AR模型和MA模型。

AR说:用"过去的值"预测未来------历史会重演。

MA说:用"过去的意外"预测未来------冲击会蔓延。

单独用AR,你只看到了"惯性"。单独用MA,你只看到了"回声"。

但真实世界的金融市场,既不是纯粹的惯性驱动,也不是纯粹的冲击驱动。它是两者的混合体。

今天,我们把AR和MA拼在一起,聊时间序列分析中第一个真正"完整"的模型------ARMA模型

它不是简单的"1+1=2",而是一种质变。就像你把发动机和底盘组合在一起,得到的不是一堆零件,而是一辆能跑的车。

WHY:为什么需要ARMA?

AR和MA各自的"盲区"

先回顾一下两个模型的局限。

AR(p)的盲区:

AR模型假设今天的值完全由过去p期的值决定。它隐含了一个假设------所有的"冲击"都是瞬间被吸收的,不会在后续产生余波。

但现实中,一次意外的政策发布、一场突如其来的黑天鹅事件,它的影响不会当天就消失。它会像石子投入水中产生的涟漪一样,持续扩散好几天。

AR模型无法描述这种"冲击的余波效应"。

MA(q)的盲区:

MA模型假设今天的值只受过去q期"冲击"的影响,与更早之前的"值"没有直接关系。

但很多金融数据存在"动量效应"------过去涨的股票,短期内倾向于继续涨。这种"值"的传递链条,MA模型捕捉不了。

两者的盲区,恰好是对方擅长的领域。

• AR擅长捕捉"历史重演"(动量/惯性)

• MA擅长捕捉"冲击余波"(反转/回声)

把两者结合起来,就能同时描述这两种效应。这就是ARMA。

一个生活化的类比

想象你在开车。

AR视角 :你通过看后视镜来判断接下来的路况。如果过去5秒的路都是直路(历史值),你预判接下来也是直路。这捕捉的是"道路特征的延续"。

MA视角 :你通过感受车身的颠簸来判断路况。如果刚才碾过了一个减速带(一次冲击),你预判接下来几秒车身还会晃动。这捕捉的是"外部冲击的余波"。

ARMA视角 :你同时看后视镜 + 感受车身。既知道路是直是弯,又知道车身有没有因为刚才的颠簸还在晃。两种信息同时使用,判断自然更准确。

数学表达:简洁而强大

ARMA(p,q)的公式是AR和MA的自然拼接:

Xt = c + φ₁·X {t-1} + ... + φp·X {t-p} + εt + θ₁·ε {t-1} + ... + θq·ε {t-q}

拆开来看:

AR部分 :φ₁·X*{t-1} + ... + φ* p·X_{t-p} → 过去p个"值"的加权和

MA部分 :θ₁·ε*{t-1} + ... + θ* q·ε_{t-q} → 过去q个"冲击"的加权和

ε_t :今天的随机冲击(无法预测的部分)

两个部分各司其职,共同决定了今天的值。

用一个更直观的方式理解:

今天的值 = 趋势惯性(AR) + 冲击余波(MA) + 新的意外(ε)

每一项都有明确的含义,没有多余的成分。

为什么ARMA是时间序列分析的"分水岭"?

在ARMA之前,AR和MA各自是"半模型"------只描述了数据生成过程的一部分。

ARMA的出现,标志着时间序列建模从"片面描述"进入了"完整描述"的阶段。

具体来说,ARMA的重要性体现在:

1. 表达能力质的飞跃

AR(p)模型只能描述p阶衰减的自相关结构。MA(q)模型只能描述q阶截断的自相关结构。

但ARMA(p,q)可以描述更灵活的自相关模式 ------既不是简单的拖尾,也不是简单的截断,而是各种形状的衰减曲线。

这意味着ARMA能拟合更多种类的数据特征。

2. 更精简的模型

很多时间序列,如果只用AR来拟合,可能需要很高的阶数(比如AR(10))。但如果用ARMA,可能只需要ARMA(1,1)就能达到同样的拟合效果。

参数更少 → 过拟合风险更低 → 样本外预测更稳健。

这在量化交易中意义重大。金融数据的信噪比极低,用更少的参数捕捉更多的信息,是模型稳健性的关键。

3. Box-Jenkins方法论的核心

ARMA是Box-Jenkins方法论(也叫ARIMA方法论)的核心组件。这套方法论提供了一套完整的、系统化的建模流程:

识别 → 估计 → 诊断 → 预测

这套流程至今仍是时间序列分析的"标准操作程序"。而ARMA,就是这套流程的中心舞台。

4. 实战中的基石地位

在量化交易中,ARMA模型(及其扩展ARIMA、ARMA-GARCH)是使用最广泛的基线模型之一。

即使你的最终策略不用ARMA做预测,你也需要理解它------因为:

• 它是很多复杂模型的"基础组件"

• 它是模型比较的"基准线"

• 它帮你建立对数据的基本理解

HOW:怎么构建和使用ARMA模型?

第一步:模型识别------确定p和q

构建ARMA模型,首先要确定两个关键参数:p(AR的阶数)和q(MA的阶数)。

方法一:通过ACF和PACF图做初步判断

这是Box-Jenkins方法论的经典方法:

• ACF拖尾 + PACF截断 → AR(p),截断位置 = p

• ACF截断 + PACF拖尾 → MA(q),截断位置 = q

ACF拖尾 + PACF也拖尾 → ARMA(p,q)

问题在于:当ACF和PACF都拖尾时,怎么确定p和q的具体值?

说实话,光看ACF/PACF图很难精确判断。它只能给你一个大概范围。

经验法则:

• 看PACF在前几阶(1-3阶)中有几个显著的lag → 给出p的上限

• 看ACF在前几阶(1-3阶)中有几个显著的lag → 给出q的上限

• 如果ACF和PACF都在lag=1后快速衰减 → 先试ARMA(1,1)

方法二:网格搜索 + 信息准则(实战首选)

实战中,更可靠的方法是:

  1. 设定一个p和q的搜索范围(比如p ∈ 0,3,q ∈ 0,3

  2. 遍历所有(p,q)组合,拟合ARMA(p,q)模型

  3. 用AIC或BIC评估每个模型的拟合效果

  4. 选择AIC/BIC最小的(p,q)

// python
import itertools
import numpy as np
import pandas as pd
from statsmodels.tsa.arima.model import ARIMA

设定搜索范围

p_range = range(0, 4) # 0到3
q_range = range(0, 4) # 0到3

best_aic = float('inf')
best_order = (0, 0)
best_bic = float('inf')
best_order_bic = (0, 0)

results = \[\]

for p, q in itertools.product(p_range, q_range):
if p == 0 and q == 0:
continue # 跳过白噪声模型
try:
model = ARIMA(data, order=(p, 0, q))
fit = model.fit()
aic = fit.aic
bic = fit.bic
results.append({'p': p, 'q': q, 'AIC': aic, 'BIC': bic})
if aic < best_aic:
best_aic = aic
best_order = (p, q)
if bic < best_bic:
best_bic = bic
best_order_bic = (p, q)
except:
continue

print(f"AIC最优: ARMA{best_order}, AIC={best_aic:.2f}")
print(f"BIC最优: ARMA{best_order_bic}, BIC={best_bic:.2f}")

AIC vs BIC:该选哪个?

AIC (赤池信息准则):偏向更复杂的模型(允许更多参数),预测精度通常更好

BIC (贝叶斯信息准则):偏向更简单的模型(惩罚参数更多),模型更parsimonious

在量化交易中,如果你的目标是预测 ,优先看AIC。如果你的目标是理解数据生成过程 ,优先看BIC。

第二步:模型估计

确定了(p,q)之后,用最大似然估计(MLE)拟合模型:

// python
from statsmodels.tsa.arima.model import ARIMA

拟合ARMA(1,1)

model = ARIMA(data, order=(1, 0, 1))
fit = model.fit()
print(fit.summary())

在输出结果中,重点关注:

1. AR系数的显著性

φ₁ 的 p值 < 0.05 → AR成分存在,"历史重演"效应显著

2. MA系数的显著性

θ₁ 的 p值 < 0.05 → MA成分存在,"冲击余波"效应显著

3. 系数的符号和大小

• φ₁ > 0:正的动量效应(昨天高,今天倾向于高)

• φ₁ < 0:负的动量效应(均值回归趋势)

• θ₁ > 0:正的冲击会延续

• θ₁ < 0:正的冲击会导致反转

4. 常系数c

c > 0 且显著 → 序列有非零均值(对收益率来说,意味着平均收益为正)

第三步:残差诊断------模型够好了吗?

这是最容易被跳过、但最重要的步骤。

拟合完ARMA模型后,你需要检查残差是否是白噪声。

为什么残差必须是白噪声?

如果残差中还有结构(还有自相关),说明模型没有把数据中的信息全部提取出来。就像你用滤网过滤沙子,滤完以后沙子里还有石子------说明你的滤网不够细。

检验方法:

// python
from statsmodels.stats.diagnostic import acorr_ljungbox

残差

residuals = fit.resid

Ljung-Box检验

原假设:残差是白噪声(无自相关)

lb_test = acorr_ljungbox(residuals, lags=10, 15, 20, return_df=True)
print(lb_test)

如果p值 > 0.05 → 不能拒绝原假设 → 残差是白噪声 → 模型合格

如果p值 < 0.05 → 拒绝原假设 → 残差还有结构 → 模型不够好

如果残差不是白噪声,怎么办?

几种可能:

  1. p或q不够大 :尝试更高阶的ARMA(p,q)

  2. 存在季节性 :加入季节性成分(SARIMA)

  3. 存在波动率聚集 :残差可能是"独立但不同分布"的------均值方程OK了,但波动率方程还有结构。这时需要ARMA-GARCH

  4. 存在非线性结构 :数据可能受非线性过程驱动,需要考虑非线性模型(如阈值ARMA)

第四步:模型诊断------过拟合检查

残差是白噪声不代表模型就OK了。你还需要检查是否过拟合。

方法:比较不同阶数的模型

// python

比较ARMA(1,1) vs ARMA(2,2)

model1 = ARIMA(data, order=(1, 0, 1)).fit()
model2 = ARIMA(data, order=(2, 0, 2)).fit()

print(f"ARMA(1,1) AIC: {model1.aic:.2f}")
print(f"ARMA(2,2) AIC: {model2.aic:.2f}")

AIC差异 > 2 → 有显著差异,选AIC更小的

AIC差异 < 2 → 没有显著差异,选更简单的(ARMA(1,1))

奥卡姆剃刀原则 :在拟合效果差不多的情况下,永远选更简单的模型。

参数越少 → 估计误差越小 → 样本外预测越稳健。

在金融数据这种低信噪比的环境中,简洁性比拟合度更重要。

第五步:用模型做预测

// python

向前预测5步

forecast = fit.get_forecast(steps=5)
predicted_values = forecast.predicted_mean
confidence_interval = forecast.conf_int(alpha=0.05)

print("预测值:", predicted_values.values)
print("95%置信区间:")
print(confidence_interval)

关键理解:ARMA的预测是什么样的?

对于平稳序列,ARMA的长期预测会收敛到序列的均值。

也就是说,你预测得越远,预测值越接近均值。预测的不确定性(置信区间)越来越大。

这和直觉一致:越远的未来,越难预测,最保守的估计就是"回归均值"。

在量化交易中的含义:

• ARMA预测更适合做短期 (1-5步)预测

• 长期预测的价值很有限

• 预测的核心价值不在于精确数值,而在于方向置信区间的宽度

第六步:ARMA模型的可逆性与平稳性

ARMA模型要能正常工作,需要满足两个条件:

平稳性条件 (针对AR部分):

AR(p)部分的特征方程的所有根,必须在单位圆外。

直觉理解:AR部分的系数不能"太强",否则序列会发散(越往后波动越大,不收敛)。

可逆性条件 (针对MA部分):

MA(q)部分的特征方程的所有根,必须在单位圆外。

直觉理解:MA部分的系数不能"太强",否则你无法从观测数据中还原出过去的冲击。

statsmodels在拟合时会自动检查这两个条件。如果不满足,它会报错或自动转换。你不需要手动处理,但需要理解这两个概念------因为在看模型输出时,它们决定了模型是否可靠。

一个完整的实战案例

假设我拿到了某只A股ETF的日收益率序列(过去2年的数据,约500个观测值)。

1. 画ACF/PACF图

观察结果:

• ACF:lag=1显著(0.12),lag=2勉强显著(0.06),lag=3起不显著

• PACF:lag=1显著(0.11),lag=2勉强显著(0.05),lag=3起不显著

判断 :ACF和PACF都拖尾 → ARMA结构。初步猜测ARMA(1,1)或ARMA(2,1)。

2. 网格搜索

|-----------|---------|---------|
| 模型 | AIC | BIC |
| ARMA(1,0) | -1205.3 | -1196.9 |
| ARMA(0,1) | -1203.8 | -1195.4 |
| ARMA(1,1) | -1210.7 | -1198.1 |
| ARMA(2,1) | -1211.2 | -1194.4 |
| ARMA(2,2) | -1211.5 | -1190.5 |
| ARMA(3,2) | -1211.8 | -1186.6 |

AIC最优是ARMA(3,2),但AIC的差异很小(最大差6.5)。

BIC最优是ARMA(1,1),因为BIC更惩罚参数数量。

根据"简洁性优先"原则,我选ARMA(1,1)

3. 拟合ARMA(1,1)

coef std err z P>|z|
const 0.0003 0.0001 2.85 0.004 ← 日均收益0.03%,显著
ar.L1 0.085 0.042 2.02 0.043 ← AR系数显著
ma.L1 -0.065 0.043 -1.51 0.131 ← MA系数不显著

4. 解读

• φ₁ = 0.085(显著)→ 存在正的动量效应:昨天收益高1%,今天预期高0.085%

• θ₁ = -0.065(不显著)→ MA成分证据不强

• 这个结果提示:数据可能更接近AR(1),MA成分贡献有限

5. 残差诊断

Ljung-Box检验(lag=10):p值 = 0.23 > 0.05 → 残差是白噪声,模型合格。

6. 结论

这个ETF的日收益率存在微弱的短期动量效应(AR(1)系数=0.085),但没有显著的MA成分。

对交易含义:

• 动量效应存在但很弱,单独使用价值有限

• 可以作为多因子策略中的一个信号

• 效应强度:昨日收益1% → 今日预期额外收益0.085%,扣除交易成本后利润空间极小

WHAT:ARMA模型给你的实战工具箱

行动一:建立你的"ACF/PACF看图直觉"

ARMA模型的核心技能是快速看图判断

建议你做以下练习:

  1. 用Python生成不同参数的AR、MA、ARMA模拟数据

  2. 画ACF/PACF图

  3. 反复看,直到你能在3秒内判断"这是AR、MA还是ARMA"

// python
import numpy as np
from statsmodels.tsa.arima.model import ARIMA
import matplotlib.pyplot as plt
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

模拟ARMA(1,1)

np.random.seed(42)
ar_params = 0.7
ma_params = 0.3
data = ARIMA(np.zeros(500), order=(1, 0, 1)).simulate(
nsimulations=500, ar_params=ar_params, ma_params=ma_params
)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
plot_acf(data, ax=ax1, lags=20)
plot_pacf(data, ax=ax2, lags=20)
plt.show()

反复做这个练习,你的"模式识别"能力会快速提升。

行动二:用ARMA做"基准模型"

在你的量化策略研发流程中,ARMA应该作为基准模型 (baseline):

  1. 先拟合ARMA,看看数据中的线性结构有多少

  2. 计算ARMA能解释多少方差(R²)

  3. 如果你的复杂策略(深度学习、集成方法等)不能显著超越ARMA → 说明你的复杂策略可能在拟合噪声

这是一个非常重要的检验。很多人直接上复杂模型,结果发现性能还不如一个简单的ARMA。这说明什么?说明数据中只有线性结构,你的复杂模型只是在过拟合。

ARMA是量化策略的"最低标准"。你的策略如果打不过ARMA,就不值得实盘。

行动三:ARMA + GARCH = 量化标配

在实战中,纯粹的ARMA很少直接使用。更常见的组合是ARMA-GARCH

• ARMA建模收益率的均值方程 (预期收益)

• GARCH建模收益率的波动率方程 (风险/不确定性)

为什么这个组合如此经典?

因为金融收益率有两个特征需要同时建模:

  1. 均值层面的动力学 :动量、反转等 → ARMA捕捉

  2. 波动率层面的动力学 :波动率聚集、杠杆效应等 → GARCH捕捉

这两个层面是独立的------均值方程的残差可能不是白噪声(在波动率层面),需要GARCH来进一步建模。

下一篇我们讲GARCH,届时你会看到两者如何完美配合。

行动四:用ARMA做异常检测

ARMA模型不仅可以预测,还可以做异常检测

思路:

  1. 拟合ARMA模型

  2. 计算残差 ε_t = 实际值 - 模型预测值

  3. 如果某个时刻的 |ε_t| 异常大(比如超过3倍标准差),说明发生了"意外事件"

这些"意外事件"可能就是:

• 突发新闻(政策变化、黑天鹅事件)

• 市场结构性变化(regime switch)

• 数据错误(需要清洗)

在量化交易中,异常检测是风控的重要组成部分。ARMA提供了一个简单有效的框架。

行动五:警惕ARMA的局限

ARMA虽然强大,但它有明确的边界:

  1. 只能建模线性关系 :ARMA是线性模型。如果数据中存在非线性结构(如阈值效应、状态切换),ARMA无法捕捉。

  2. 假设平稳性 :ARMA要求序列是平稳的。金融数据经常不满足这个条件(趋势、结构性断点)。

  3. 不能直接建模波动率 :ARMA建模的是均值,不是波动率。如果你的目标是预测风险,需要结合GARCH。

  4. 短期预测有效,长期预测无效 :对平稳序列,长期预测收敛到均值,没有交易价值。

  5. 参数估计的不确定性 :在短序列或低信噪比数据中,参数估计可能非常不精确。

理解局限不是为了否定ARMA,而是为了在正确的场景中使用它。

行动六:Box-Jenkins方法论的完整流程

最后,把Box-Jenkins方法论的完整流程梳理一遍,作为你建模时的checklist:

  1. 数据准备
    ├── 画图观察序列特征
    ├── 检查是否有缺失值、异常值
    └── 判断是否需要变换(对数、差分等)

  2. 平稳性检验
    ├── ADF检验 / KPSS检验
    ├── 如果非平稳 → 差分直到平稳 → 进入ARIMA
    └── 如果平稳 → 继续

  3. 模型识别
    ├── 画ACF/PACF图
    ├── 初步判断AR、MA还是ARMA
    └── 网格搜索 + AIC/BIC确定最优(p,q)

  4. 模型估计
    ├── 拟合ARMA(p,q)
    ├── 检查系数显著性
    └── 检查平稳性/可逆性条件

  5. 残差诊断
    ├── Ljung-Box检验(残差是否白噪声?)
    ├── 画残差图(是否有异方差?)
    └── 如果残差不是白噪声 → 返回步骤3调整模型

  6. 模型评估
    ├── 比较多个候选模型的AIC/BIC
    ├── 样本外预测测试
    └── 选择最优模型

  7. 预测与应用
    ├── 生成预测值和置信区间
    ├── 结合实际业务做决策
    └── 持续监控预测效果,必要时重新拟合

这套流程不仅适用于ARMA,也适用于所有Box-Jenkins框架下的模型(ARIMA、SARIMA等)。掌握它,你就掌握了时间序列建模的"标准操作程序"。

写在最后:组合的力量

ARMA模型教给我们的,不仅是时间序列分析的技术,更是一种思维方式------组合的力量

AR描述了"历史重演",MA描述了"冲击余波"。两者单独看,都是片面的。

但把它们组合在一起,就能描述更完整、更真实的动态过程。

这个逻辑在量化交易中无处不在:

• 趋势跟踪 + 均值回归 = 更稳健的策略

• 基本面分析 + 技术面分析 = 更全面的判断

• 多个弱信号的组合 = 一个强信号

不要追求"一个模型解决所有问题"。要追求"多个简单模型的优雅组合"。

ARMA就是这个理念的最佳示范。

下一篇,我们进入GARCH模型------专门捕捉金融市场"波动率聚集"现象的利器。你会发现,ARMA + GARCH的组合,才是量化交易中真正的"标配"。

我们下篇见。

本文是「年化20%量化笔记」系列的第44篇。本系列记录一个量化交易从业余到进阶的真实成长路径------不卖课、不造神,只分享真正有用的知识和踩过的坑。

如果这篇文章对你有帮助,欢迎转发给同样在量化路上摸索的朋友。你的支持是我持续写下去的动力。

相关推荐
是未才2 小时前
从输入 URL 到页面返回:DNS、路由、TLS 与 HTTP 完整链路
java·后端·计算机网络
DevUI团队3 小时前
从“即兴创作”到“规格先行”,华为云码道(CodeArts)代码智能体持续深耕企业级规范驱动开发能力
前端·人工智能·后端
weixin_431600444 小时前
NestJS 入门(3):Guard 如何挡住未登录请求?
前端·后端·学习·nest.js
IT_陈寒5 小时前
JavaScript类型转换把我坑惨了,这破玩意真该早点搞明白
前端·人工智能·后端
用户938515635075 小时前
手写一个 LLM Harness 框架:用工程化手段把大模型幻觉踩在脚下
javascript·人工智能·后端
wei_shuo6 小时前
KES 云原生部署与弹性扩展:容器化、Kubernetes编排与自动伸缩
后端
一木之林6 小时前
Python.五.(一)--1. 并发编程、异步IO与多进程
后端
feng尘6 小时前
# 彻底搞懂 ReentrantLock 与 tryLock:从秒杀实战到 AQS 独占模式源码剖析
后端
啷里格啷6 小时前
Linux进程管理完全指南:从基础到云原生编排
后端·架构