← 返回文章列表
July 10, 2026
5 分钟阅读

GARCH(1,1):预测加密货币波动率

#volatility
#GARCH
#risk
#forecasting
#crypto
#algorithmic-trading

打开一张BTC日收益率图表,你会注意到关于随机游走的教科书从未让你准备好面对的现象:平静与混乱是成簇出现的。下跌6%的一天很少是孤立的。它往往处于一整周4-8%的剧烈波动之中,然后市场喘口气,度过沉闷的1%行情的一个月,直到下一场风暴来临。收益率本身看起来近乎不可预测——你无法可靠地判断明天是涨是跌——但它们的幅度却具有高度可预测性。今天的动荡能告诉你很多关于明天的信息。

交易者手边几乎所有的风险工具都在默默地假设这一点不成立。Black-Scholes用单一常数σ\sigma给期权定价。静态的风险价值(Value-at-Risk)指标用一个波动率估计值乘以正态分位数。固定3%的止损把一个死气沉沉的周二和FOMC公告或某交易所脱锚事件前后的几个小时一视同仁。这些工具都以同样的方式失效:它们把一个随时间变化的量塌缩成一个常数,然后在这个常数偏偏动起来的时候大吃一惊。

本文是加密货币波动率建模四部曲系列的第一部分。它搭建基础:GARCH(1,1)模型、为什么它能很好地拟合加密货币收益率、如何用arch库通过最大似然法诚实地估计它,以及如何将条件方差预测转化为两个立即有用的东西——一个仓位规模和一个都能随市场"呼吸"的止损宽度。第二部分加入非对称性和厚尾特征,第三部分转向多元建模,第四部分组装完整的波动率目标回测。这里我们刻意让应用部分保持简单;诚实的、经过前向验证的策略是第四部分的主题。

加密货币收益率的典型化事实

在建模任何东西之前,先精确弄清楚我们要复现的是什么才划算。经验金融收益率——股票、外汇,尤其是加密货币——共享一小组已被记录了几十年的稳健统计规律。它们通常被称为典型化事实(stylized facts),其中三个驱动了后面的一切。

1. 波动率聚集。 大幅波动之后往往跟着大幅波动(不论方向),小幅波动之后往往跟着小幅波动。曼德尔布罗特(Mandelbrot)在1963年研究棉花价格时注意到了这一点。形式上,虽然收益率rtr_t几乎不存在序列相关性,但平方收益率rt2r_t^2(已实现方差的一个代理指标)表现出强烈的、缓慢衰减的正自相关性。

2. 厚尾(尖峰峭度)。 收益率的无条件分布在极端值处的质量远多于高斯分布。正态分布的峰度为3,而BTC的日对数收益率通常在8-10以上,更高频的加密货币收益率可能更糟。按正态模型的说法,六西格玛的日子应该大约百万年才出现一次,但实际上每十年就会出现好几次。

3. 收益率无线性自相关,平方收益率有强自相关。 这正是区分真正的波动率过程与平凡趋势的标志。如果你把rtr_t对其自身滞后项回归,得不到任何可利用的东西。如果你把rt2r_t^2对其滞后项回归,会发现清晰、持久的信号。这正是一个方差模型应该捕捉的结构——也正是常数σ\sigma模型所丢弃的东西。

我们可以用几行代码目测这三点。这里不需要特殊的数据源;生产环境用ccxt,但对于一个可复现的代码片段,yfinance就够用了。

import numpy as np
import pandas as pd
import yfinance as yf
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.stats.diagnostic import acorr_ljungbox
from scipy import stats

px = yf.download("BTC-USD", start="2021-01-01", end="2025-12-31")["Close"]
ret = np.log(px / px.shift(1)).dropna()          # log returns
ret = ret.rename("btc")

print(f"Observations:     {len(ret)}")
print(f"Ann. volatility:  {ret.std() * np.sqrt(365):.2%}")
print(f"Skewness:         {stats.skew(ret):.2f}")
print(f"Excess kurtosis:  {stats.kurtosis(ret):.2f}")   # 0 == Gaussian

lb_ret  = acorr_ljungbox(ret,        lags=[10], return_df=True)
lb_ret2 = acorr_ljungbox(ret ** 2,   lags=[10], return_df=True)
print("\nLjung-Box p-value, returns   (lag 10):", float(lb_ret["lb_pvalue"].iloc[0]))
print("Ljung-Box p-value, squared   (lag 10):", float(lb_ret2["lb_pvalue"].iloc[0]))

典型的读数(仅作说明——你的窗口结果会有所不同):超额峰度远高于3,原始收益率的Ljung-Box p值未能拒绝"无自相关"的假设,而平方收益率的p值实际上接近于零。最后这个对比就是全部关键所在。在日线级别上,收益率的符号没有什么可交易的,但它们的方差中存在大量结构,而且这种结构是可预测的。

关于加密货币24/7运行特性的说明:与股票不同,加密货币没有隔夜跳空也没有周末休市,所以"一天"是一根干净的24小时K线,年化系数是365\sqrt{365},而不是252\sqrt{252}。波动率聚集在日内尺度上也同样存在,如果你在小时线上运行GARCH,这一点很重要——资金费率翻转和清算连锁反应会注入尖锐的、聚集性的方差爆发,而日线模型会把这些抹平。

从ARCH到GARCH

现在问题已经被清晰地提出:对一个不是常数而是依赖于近期历史的方差建模。第一个正确做到这一点的模型是恩格尔(Engle)的ARCH(自回归条件异方差,1982年),这为他赢得了2003年诺贝尔奖。

把收益率写成条件均值加一个冲击:

rt=μt+εt,εt=σtzt,zti.i.d. (0,1)r_t = \mu_t + \varepsilon_t, \qquad \varepsilon_t = \sigma_t z_t, \qquad z_t \sim \text{i.i.d. } (0, 1)

这里σt2\sigma_t^2条件方差——即在已知截至t1t-1时刻的所有信息条件下rtr_t的方差——而ztz_t是一个标准化的新息(在最简单的情形下服从标准正态分布)。"条件"这个词承载了全部的关键:无条件而言方差可能是常数,但以昨天为条件时它会变动。

恩格尔的ARCH(qq)将今天的方差表示为最近qq个平方冲击的加权和:

σt2=ω+i=1qαiεti2\sigma_t^2 = \omega + \sum_{i=1}^{q} \alpha_i \, \varepsilon_{t-i}^2

其中ω>0\omega > 0αi0\alpha_i \ge 0以保证方差为正。这直接捕捉了聚集现象:一个大的冲击εt12\varepsilon_{t-1}^2会推高σt2\sigma_t^2,从而提高再次出现大冲击的概率,使方差持续保持高位。问题在于经验上的衰减方式。真实市场中的波动率持续性会跨越很多个滞后期,因此要拟合它,ARCH模型需要很大的qq——通常是8、10甚至更多——这意味着要估计一个很长、不稳定、容易过拟合的αi\alpha_i向量。

Bollerslev在1986年的洞见是加入一个用单一参数吸收所有这种持续性的项。GARCH(1,1)——广义ARCH——的递归式为:

σt2=ω+αεt12+βσt12\sigma_t^2 = \omega + \alpha \, \varepsilon_{t-1}^2 + \beta \, \sigma_{t-1}^2

三个参数,三种清晰的解释:

  • ω>0\omega > 0 —— 基线或底线。锚定方差长期水平的常数。方差永远不会衰减到ω\omega所支撑的水平以下。
  • α0\alpha \ge 0 —— 对新信息的反应。方差对昨天的意外εt12\varepsilon_{t-1}^2反应有多剧烈。较大的α\alpha意味着条件方差跳跃且对冲击敏感。
  • β0\beta \ge 0 —— 持续性或记忆。昨天的方差有多少会延续到今天。较大的β\beta意味着波动率平滑且消退缓慢——平静保持平静,风暴保持风暴。

其精妙之处在于这个递归本身。因为σt12\sigma_{t-1}^2本身就包含一个βσt22\beta \sigma_{t-2}^2项,向后展开可以看出GARCH(1,1)其实是一个ARCH(\infty),对过去平方冲击的权重呈几何衰减αβk\alpha \beta^{k}

σt2=ω1β+αk=0βkεt1k2\sigma_t^2 = \frac{\omega}{1-\beta} + \alpha \sum_{k=0}^{\infty} \beta^{k}\, \varepsilon_{t-1-k}^2

所以单单一个β\beta就买到了对过去冲击的无限、指数加权记忆。这正是为什么区区一个GARCH(1,1)——三个参数——经常胜过拥有十个参数的ARCH模型,也是它成为应用波动率建模主力工具的原因。事实上,它是RiskMetrics EWMA方差估计器的近亲,后者正是ω=0\omega = 0α+β=1\alpha + \beta = 1β\beta固定为0.94的特例。GARCH的推广之处在于让数据自己选择α\alphaβ\beta以及一个真正的均值回归水平。

性质:平稳性、长期方差与半衰期

GARCH(1,1)递归有几个值得推导的性质,因为正是它们让你能够推理这个模型,而不仅仅是盲目地拟合它。

无条件(长期)方差。 假设该过程是协方差平稳的,因此无条件方差σˉ2=E[εt2]=E[σt2]\bar{\sigma}^2 = \mathbb{E}[\varepsilon_t^2] = \mathbb{E}[\sigma_t^2]存在且随时间保持不变。对递归式两边取期望。由于E[εt12]=E[σt12]=σˉ2\mathbb{E}[\varepsilon_{t-1}^2] = \mathbb{E}[\sigma_{t-1}^2] = \bar{\sigma}^2

σˉ2=ω+ασˉ2+βσˉ2        σˉ2=ω1αβ\bar{\sigma}^2 = \omega + \alpha\, \bar{\sigma}^2 + \beta\, \bar{\sigma}^2 \;\;\Longrightarrow\;\; \bar{\sigma}^2 = \frac{\omega}{1 - \alpha - \beta}

这就是波动率均值回归所趋向的水平。它只有在α+β<1\alpha + \beta < 1时才存在——且才为正。

平稳性条件。 同一个不等式,

α+β<1,\alpha + \beta < 1,

正是GARCH(1,1)的协方差平稳性条件。量α+β\alpha + \beta是方差过程的持续性:它是支配方差冲击如何衰减回σˉ2\bar{\sigma}^2的AR(1)系数。如果α+β1\alpha + \beta \ge 1,无条件方差为无穷大(或未定义),冲击永远不会完全消失。

我们可以显式地看到均值回归。定义方差偏差σt2σˉ2\sigma_t^2 - \bar{\sigma}^2。对递归式做一点代数变换(代入ω=σˉ2(1αβ)\omega = \bar{\sigma}^2(1 - \alpha - \beta)),在期望意义下得到:

Et[σt+k2]σˉ2=(α+β)k(σt+12σˉ2)\mathbb{E}_t[\sigma_{t+k}^2] - \bar{\sigma}^2 = (\alpha + \beta)^{k}\,\bigl(\sigma_{t+1}^2 - \bar{\sigma}^2\bigr)

当前方差与其长期水平之间的差距,每一步都以(α+β)(\alpha + \beta)的比例收缩。这正是我们后面用到的多步预测。

波动率半衰期。 一个方差冲击需要多长时间才能衰减到正常水平的一半?令(α+β)h=1/2(\alpha+\beta)^{h} = 1/2并求解:

h1/2=ln0.5ln(α+β)h_{1/2} = \frac{\ln 0.5}{\ln(\alpha + \beta)}

对于α+β=0.95\alpha + \beta = 0.95,半衰期约为13.5天;对于0.980.98,约为34天;对于0.990.99,约为69天。这个单一数字通常比原始参数更直观——它以你所用K线的单位告诉你波动率有多"粘"。

加密货币中的近IGARCH问题。 这里是加密货币特有的一个曲折之处。当你把GARCH(1,1)拟合到BTC或ETH的收益率上时,几乎总会发现α+β\alpha + \beta非常接近1——0.98、0.99,有时甚至0.995这样的值都很常见。这就是近IGARCH(整合GARCH)状态。它带来实际后果:

  • 半衰期变得极长(数周到数月),因此模型会把波动率视为高度持续、几乎不做均值回归。
  • σˉ2=ω/(1αβ)\bar{\sigma}^2 = \omega/(1-\alpha-\beta)的估计变得极其敏感:α+β\alpha+\beta从0.99变到0.995这样一个微小变化,就会使隐含的长期方差翻倍。在这种状态下,永远不要在没有置信区间的情况下相信长期波动率的点估计。
  • 多步预测的均值回归速度极慢,以至于在几周以内的实用视野中,GARCH的行为几乎和方差随机游走(这正是EWMA所假设的)一样。

近整合现象究竟是真实存在的,还是结构性断点的假象(波动率水平的永久性转变被模型误读为一段长期持续的插曲),这是一个真正存在争议的问题。这也是为什么应该在滚动窗口上重新拟合,而不是一次性用全部历史数据拟合的又一个理由,我们在后面讨论陷阱时会再谈到这一点。状态结构问题最好由显式的切换模型来处理——参见用隐马尔可夫模型进行状态检测,它与GARCH互补而非替代关系。

最大似然估计

GARCH参数通过最大似然法估计。逻辑很直接:给定θ=(μ,ω,α,β)\theta = (\mu, \omega, \alpha, \beta),该递归会产生一整条条件方差路径σt2(θ)\sigma_t^2(\theta),在假设新息ztz_t服从某分布的前提下,我们可以写出观测到的收益率序列的似然。然后我们选择θ\theta使该似然最大化。

假设新息服从高斯分布ztN(0,1)z_t \sim \mathcal{N}(0,1),则rtFt1N(μ,σt2)r_t \mid \mathcal{F}_{t-1} \sim \mathcal{N}(\mu, \sigma_t^2)。单个观测值的条件密度为

f(rtFt1)=12πσt2exp ⁣((rtμ)22σt2).f(r_t \mid \mathcal{F}_{t-1}) = \frac{1}{\sqrt{2\pi\sigma_t^2}}\exp\!\left(-\frac{(r_t - \mu)^2}{2\sigma_t^2}\right).

因为模型是以条件形式写出的,联合似然可以分解为一系列一步预测密度的乘积,对数似然则是一个简单的求和:

(θ)=12t=1T[ln(2π)+lnσt2(θ)+(rtμ)2σt2(θ)].\ell(\theta) = -\frac{1}{2}\sum_{t=1}^{T}\left[\ln(2\pi) + \ln \sigma_t^2(\theta) + \frac{(r_t - \mu)^2}{\sigma_t^2(\theta)}\right].

有两个结构性事实值得注意。第一,σt2\sigma_t^2既作为惩罚项出现(lnσt2\ln \sigma_t^2——模型因声称高方差而受到惩罚),又出现在标准化残差中((rtμ)2/σt2(r_t-\mu)^2/\sigma_t^2——模型因被"意外"而受到惩罚)。最优解在两者之间取得平衡,这正是使方差得以跟踪的原因。第二,该递归需要一个初始种子σ12\sigma_1^2;通常的选择是收益率的样本方差,在有几千个观测值的情况下,这个种子几乎无关紧要。

极大化没有解析解,所以我们进行数值优化(arch使用带解析或数值梯度的拟牛顿法)。对于GARCH(1,1)而言,似然曲面通常表现良好,但实践中有两点会带来麻烦:正性约束(ω,α,β0\omega,\alpha,\beta \ge 0),以及当α+β1\alpha+\beta \to 1时接近边界处的行为,此时优化器可能爬行得很慢。这两者都由一个好的库替你处理好了——你也应该用现成的库。手写一个GARCH最大似然估计是个不错的学习练习,但不是好的生产选择。

arch

Kevin Sheppard开发的arch包是Python中的标准工具。整个拟合过程只需四行代码。

from arch import arch_model

r = ret * 100.0

model  = arch_model(r, mean="Constant", vol="Garch", p=1, q=1, dist="normal")
res    = model.fit(disp="off")
print(res.summary())

关于参数名有一点值得说明,因为这是常见的混淆来源。在arch中,p是滞后方差项的数量(β\beta项,即GARCH阶数),q是滞后平方残差项的数量(α\alpha项,即ARCH阶数)。所以p=1, q=1就是我们推导的GARCH(1,1)。(Bollerslev原始的记号将其写作GARCH(p,qp,q),其中pp代表ARCH阶数——两种约定的顺序是颠倒的。相信库自己的文档,而不是你的记忆。)

阅读摘要输出时,系数表大致如下(这是BTC日收益率的示意数值,不是真实实验):

                     Volatility Model
==========================================================
                 coef    std err      t      P>|t|
----------------------------------------------------------
omega          0.4821     0.201    2.40    0.016
alpha[1]       0.0912     0.021    4.34    0.000
beta[1]        0.8994     0.024   37.5     0.000
==========================================================

该如何解读:

  • alpha[1] + beta[1] = 0.0912 + 0.8994 = 0.9906。持续性略低于1——正如警告的那样,正是近IGARCH状态。半衰期ln(0.5)/ln(0.9906)73\approx \ln(0.5)/\ln(0.9906) \approx 73天。
  • omega = 0.4821,所以长期方差0.4821/(10.9906)=51.30.4821 / (1 - 0.9906) = 51.3(百分比平方单位),即长期日波动率51.37.2%\sqrt{51.3} \approx 7.2\%,或大约7.2%×365138%7.2\%\times\sqrt{365}\approx 138\%年化。这是一个合理的BTC数字。
  • alphabeta都高度显著。alpha相对于beta较小是典型现象:加密货币的方差主要来自持续性(记忆),对新冲击有适度但真实的反应。

×100缩放的坑

这是从arch得到无意义结果的最常见方式,值得单独开一小节讨论。优化器在处理数量级为O(1)O(1)O(100)O(100)的数字时表现最好。日对数收益率的数量级是O(0.01)O(0.01),所以它们的平方是O(0.0001)O(0.0001),而ω\omega则要落在约10610^{-6}的数量级——在这个范围内,数值梯度会失去精度,拟合可能悄悄地无法收敛,或返回垃圾般的标准误。

解决方法是在放大100倍(即以百分比为单位)的收益率上进行拟合,如上所示。如果你忘了这样做,arch甚至会发出DataScaleWarning警告。此后从模型中读出的一切都是以百分比或百分比平方为单位的,你必须一致地反缩放

sigma_pct     = np.sqrt(res.forecast(horizon=1).variance.iloc[-1, 0])
sigma_decimal = sigma_pct / 100.0
print(f"1-day-ahead conditional vol: {sigma_decimal:.2%}")

混用已缩放和未缩放的量——比如把一个百分比波动率输入一个预期是小数的仓位规模计算公式——会产生恰好100倍的误差,而且很容易被忽略,因为代码照样能跑通。选定一个约定(我个人的做法是在拟合之外始终保持小数形式,只在arch的边界处做缩放),然后永远不要跨越它。

预测条件方差

一个拟合好的模型只有能预测才有用。GARCH能在任意时间跨度上给出清晰的解析预测。

一步预测。 在时刻TT(样本末尾)我们已知εT\varepsilon_TσT2\sigma_T^2,所以下一个方差是确定性的:

σT+12=ω+αεT2+βσT2.\sigma_{T+1}^2 = \omega + \alpha\,\varepsilon_T^2 + \beta\,\sigma_T^2.

不需要求期望——右边的一切都是已观测到的。

多步预测。 对于h2h \ge 2,我们还不知道中间的冲击,所以要取条件期望。利用ET[εT+k2]=ET[σT+k2]\mathbb{E}_T[\varepsilon_{T+k}^2] = \mathbb{E}_T[\sigma_{T+k}^2](因为E[z2]=1\mathbb{E}[z^2]=1),该递归塌缩为预测方差上一个简单的AR(1)形式:

ET[σT+h2]=ω+(α+β)ET[σT+h12].\mathbb{E}_T[\sigma_{T+h}^2] = \omega + (\alpha + \beta)\,\mathbb{E}_T[\sigma_{T+h-1}^2].

从一步预测开始迭代,可以得到闭式解,这正是我们前面推导出的均值回归结果的显式写法:

ET[σT+h2]=σˉ2+(α+β)h1(σT+12σˉ2),σˉ2=ω1αβ.\mathbb{E}_T[\sigma_{T+h}^2] = \bar{\sigma}^2 + (\alpha+\beta)^{\,h-1}\bigl(\sigma_{T+1}^2 - \bar{\sigma}^2\bigr), \qquad \bar{\sigma}^2 = \frac{\omega}{1-\alpha-\beta}.

请仔细读这个式子,因为它是每一次GARCH预测的几何结构。方差的期限结构从今天的条件方差σT+12\sigma_{T+1}^2出发,几何式地衰减向长期水平σˉ2\bar{\sigma}^2。如果今天比平均水平更平静,预测曲线会向σˉ2\bar\sigma^2上升;如果今天是危机时刻,它会向下衰减到σˉ2\bar\sigma^2。这个衰减速度完全由(α+β)(\alpha+\beta)决定——在加密货币的近IGARCH状态下,α+β0.99\alpha+\beta \approx 0.99,衰减速度极慢,以至于在两周以内的预测视野中,预测值几乎不会偏离今天的水平。这一点值得牢记:对于短持仓周期,加密货币GARCH预测本质上就是"明天看起来和今天差不多,只是非常缓慢地回归均值"。

聚合到持仓视野。 交易者很少关心未来某一天的方差。如果你持有一个仓位HH天,并且收益率在条件上不相关(这是开篇提到的典型化事实),那么累积HH天收益率的方差就是逐日预测方差之和:

σT2(H)=h=1HET[σT+h2],σT(H)=σT2(H).\sigma_{T}^{2}(H) = \sum_{h=1}^{H} \mathbb{E}_T[\sigma_{T+h}^2], \qquad \sigma_T(H) = \sqrt{\sigma_T^2(H)}.

这才是你真正应该据以确定仓位规模的数字——即你持仓周期内盈亏的波动率。请注意,这绝不是朴素的HσT+1\sqrt{H}\,\sigma_{T+1}缩放,后者只有在方差恒定时才成立。当今天的方差高于σˉ2\bar\sigma^2时,均值回归的预测使真实的HH天波动率低于平方根法则的结果;当今天很平静时,则高于该结果。搞对这一点,就是一个尊重期限结构的止损和一个不尊重的止损之间的差别。

代码实现:

H = 10
fc = res.forecast(horizon=H, reindex=False)

var_path_pct2 = fc.variance.iloc[-1].values        # [E_T sigma^2_{T+1}, ..., T+H]
var_path      = var_path_pct2 / (100.0 ** 2)       # back to decimal variance

daily_vol   = np.sqrt(var_path)
print("Forecast daily vol path:", np.round(daily_vol * 100, 2), "%")

H_day_var = var_path.sum()
H_day_vol = np.sqrt(H_day_var)
print(f"{H}-day holding-period vol: {H_day_vol:.2%}")

naive = np.sqrt(var_path[0] * H)
print(f"Naive sqrt(H) * sigma_1:   {naive:.2%}")

对于更长的时间跨度,GARCH还支持模拟预测(method="simulation"),它将新息分布向前传播,给出完整的预测密度,而不仅仅是其方差——当新息不是高斯分布时(一旦我们在第二部分转向Student-t和偏态分布,情况就会是这样)这非常有用。对于上面这些方差线性相关的量,解析路径是精确且免费的。

诊断:模型真的起作用了吗?

拟合一个模型不等于验证它。GARCH的整个意义在于吸收条件异方差——即波动率聚集——使得剩下的部分(接近)独立同分布。因此正确的检验是查看标准化残差

z^t=rtμ^σ^t\hat{z}_t = \frac{r_t - \hat\mu}{\hat\sigma_t}

并追问:聚集现象消失了吗?如果模型捕捉到了方差动态,z^t\hat z_t应具有单位方差,而且关键是,它们的平方z^t2\hat z_t^2应不再表现出自相关性。我们运行三项检验。

1. 标准化残差上的Ljung-Box检验。 检查z^t\hat z_t水平上是否还残留线性自相关(这实际上是在检验均值模型,而非方差模型)。应不拒绝原假设。

2. 平方标准化残差上的Ljung-Box检验。 这是重要的一项。如果z^t2\hat z_t^2仍存在显著自相关,说明方差模型未能去除聚集现象——存在GARCH(1,1)未能捕捉的结构,你可能需要更高阶模型、非对称变体,或不同的新息分布。应不拒绝原假设。

3. ARCH-LM检验(恩格尔的拉格朗日乘子检验)。z^t2\hat z_t^2对其自身滞后项回归,检验联合显著性。它本质上是检验2的正式版本,直接回答"是否还残留ARCH效应"这个问题。不显著的结果确认条件异方差已被模型消除。

from statsmodels.stats.diagnostic import acorr_ljungbox
from arch.unitroot.cointegration import engle_granger  # (unrelated; shown for import clarity)

z  = res.std_resid.dropna()          # standardized residuals
z2 = z ** 2

lb_z  = acorr_ljungbox(z,  lags=[10, 20], return_df=True)
lb_z2 = acorr_ljungbox(z2, lags=[10, 20], return_df=True)
print("Ljung-Box on standardized residuals:\n", lb_z, "\n")
print("Ljung-Box on SQUARED standardized residuals:\n", lb_z2, "\n")

lm = res.arch_lm_test(lags=10, standardized=True)
print(lm)

print(f"\nStd-resid excess kurtosis: {stats.kurtosis(z):.2f}")

良好的输出应该是什么样子:z^t2\hat z_t^2上的Ljung-Box p值从(原始平方收益率上的)接近零跳升到舒适地高于0.05,并且ARCH-LM检验未能拒绝原假设。这就是模型在二阶矩上完成了任务的证据。

不完美的输出——也是你在加密货币上使用普通高斯GARCH(1,1)时应该预期看到的情况——是聚集性检验通过了,但标准化残差的峰度仍然偏高(比如4-6而不是0)。GARCH去除了聚集现象,但单一厚尾无条件分布依然存在,因为高斯新息无法复现尾部特征。这种残留的厚尾性不是这里要修复的缺陷;它正是第二部分的动机所在,参见加密货币中的非对称GARCH与杠杆效应,其中Student-t和偏态t分布新息以及GJR/EGARCH非对称项正是用来解决这个问题的。

应用:波动率缩放的仓位规模与止损

现在我们有了明天(以及未来HH天)波动率的预测。我们该拿它做什么?两个最简单、最有价值的用途是仓位规模和止损设置。这里我们刻意让两者都保持基础水平——包含全部实际操作细节的完整波动率目标策略是第四部分的内容。

波动率目标仓位规模

其思路是持有一个风险贡献随时间大致恒定的仓位,而不是名义规模恒定的仓位。如果你总是投入相同的美元规模,你的风险会在高波动状态下膨胀,在平静状态下萎缩——这恰恰与你想要的相反。波动率目标法则反其道而行之:以固定的目标盈亏波动率为目标,让预测来决定规模。

对于目标年化波动率σtarget\sigma_{\text{target}}(比如20%)和预测年化波动率σ^t\hat\sigma_t,仓位权重为

wt=σtargetσ^t.w_t = \frac{\sigma_{\text{target}}}{\hat{\sigma}_t}.

当预测波动率高时,你缩小仓位;当预测波动率低时,你放大仓位。这就是全部机制。因为σ^t\hat\sigma_t预测值——在t+1t+1时刻的收益实现之前,在tt时刻就已知——所以不存在前视偏差,前提是你在时机把握上足够严谨(关于这一点,后面陷阱部分会详谈)。

def vol_target_weight(sigma_forecast_annual, sigma_target_annual=0.20,
                      w_max=3.0):
    """Volatility-scaled position weight. Inputs/outputs in decimals.
    w_max caps leverage so a tiny forecast vol can't demand insane size."""
    w = sigma_target_annual / sigma_forecast_annual
    return float(np.clip(w, 0.0, w_max))

sigma_1d   = np.sqrt(res.forecast(horizon=1).variance.iloc[-1, 0]) / 100.0
sigma_ann  = sigma_1d * np.sqrt(365)
w          = vol_target_weight(sigma_ann, sigma_target_annual=0.20)
print(f"Forecast annual vol: {sigma_ann:.1%}  ->  position weight: {w:.2f}x")

这是与真正的资金分配规则一脉相承的近亲。波动率目标法回答的是"风险应该随波动率如何缩放",而凯利准则回答的是"风险应该随优势如何缩放"——在一个完整的仓位规模体系中两者相乘:规模 \propto 优势 / 方差。请注意凯利公式中的方差项正是你刚刚计算出的GARCH预测值,这也是为什么一个实时波动率模型能够比静态历史估计显著改善凯利仓位规模。如果你的优势估计本身带有已量化的不确定性,保形预测提供了一种不依赖分布假设的方式来相应地放大或缩小仓位,并且它与波动率目标法能够干净地组合使用。

上限w_max不是可选项。在近IGARCH状态下,一段平静期可能会把预测波动率压得很低,此时σtarget/σ^t\sigma_{\text{target}}/\hat\sigma_t会要求一个理论上很好、但一旦平静破裂就会很致命的杠杆水平——而根据波动率聚集现象,平静迟早会破裂,往往还很突然。给杠杆设上限是一种粗糙但有效的做法,承认你的预测只是一个条件均值,而不是保证,而且犯错的代价是不对称的。这种不对称性——账户爆仓无法靠一次对称的盈利来挽回——正是亏损与盈利的不对称性,它应当让你系统性地比一个只考虑方差的规则更保守。

波动率缩放止损

固定百分比止损和固定仓位规模患有同一种病:3%的止损在平静市场中是一触即发的敏感开关,在剧烈波动市场中却是舍入误差。它会在高波动状态下让你被普通噪音震出好仓位,而在状态转换期又回吐太多利润。解决方法是以预测波动率为单位设定止损距离。

stop distancet=kσ^t(H)\text{stop distance}_t = k \cdot \hat\sigma_t^{(H)}

其中σ^t(H)\hat\sigma_t^{(H)}是你预期持仓视野HH上的预测波动率(即预测一节中得到的聚合量),kk是一个倍数——通常在1.5到3之间——选取的原则是让止损位于正常波动之外,但仍在一次真正的不利行情之内。

def vol_scaled_stop(entry_price, side, sigma_H, k=2.0):
    """
    entry_price : fill price
    side        : +1 long, -1 short
    sigma_H     : forecast volatility over the holding horizon (decimal)
    k           : stop width in vol units
    Returns the stop price.
    """
    stop_frac = k * sigma_H
    return entry_price * (1.0 - side * stop_frac)

var_path = res.forecast(horizon=10, reindex=False).variance.iloc[-1].values / (100.0 ** 2)
sigma_H  = np.sqrt(var_path.sum())

entry = float(px.iloc[-1])
stop  = vol_scaled_stop(entry, side=+1, sigma_H=sigma_H, k=2.0)
print(f"Entry {entry:,.0f}  |  10-day vol {sigma_H:.2%}  |  2-sigma stop {stop:,.0f}")

因为σ^t(H)\hat\sigma_t^{(H)}使用的是均值回归的期限结构预测而不是一个扁平的历史数字,止损会在进入动荡状态时自动放宽,在波动率消退时自动收紧——期限结构替你完成了这种自适应。这里驱动仓位规模和止损的是同一个预测,这是一个特性而非巧合:在高波动状态下,你同时持有更小的仓位,并给这个仓位更多的空间,两种效应叠加在一起,大幅降低了尾部风险。仓位规模和止损是同一个波动率视角的两种投影,而不是两个独立的旋钮。

第一部分的应用探讨到此为止。一个真正的策略还必须处理频繁再平衡带来的交易成本、预测计算时刻与交易执行时刻之间的时机问题、换手率控制,以及最重要的——诚实的样本外评估。所有这些都在第四部分:波动率目标GARCH策略中,我们会在那里构建并进行前向测试完整流程。

陷阱

GARCH容易拟合,也容易让人自我欺骗。失败模式相当一致。

收益率缩放。 前面已经讲过,但它是头号bug,值得再强调一次:在放大100倍的收益率上拟合arch,并把每一个输出都反缩放(方差除以1002100^2,波动率除以100100)。这里一个悄无声息的100倍错误,会污染后续每一个仓位规模和止损计算。

拟合中的前视偏差。 这是最隐蔽的杀手。如果你在全部历史数据上拟合模型,然后在同一段历史上计算"预测",那么每一次预测都偷偷看到了未来——参数是用预测日之后的数据估计出来的。样本内拟合看起来会很漂亮,但实盘表现会完全不像它。每一次回测预测都必须来自一个仅用该时刻可获得数据拟合的模型:在一个扩展或滚动窗口上重新拟合,预测一步,向前滚动。这一点不容商量,也正是前向优化这篇文章的全部主题。样本内GARCH和真正前向验证的GARCH之间的差距,就是演示样品和一个能在实盘市场中存活下来的系统之间的差距——另见回测-实盘一致性

预测的时机问题。 与前一点相关但不同。第t+1t+1天的预测必须从第tt收盘(或者你的K线收盘时刻)可获得的信息中计算出来,而且仓位必须能以你实际能拿到的价格执行。用第t+1t+1天的收盘价计算预测,然后在第t+1t+1天的开盘价"交易",这是一种前视偏差,会悄悄地虚增每一个结果。

高阶模型的过拟合。 GARCH(1,1)几乎总是够用的。因为GARCH(2,2)或GARCH(3,1)能把样本内对数似然略微推高一点就去拟合它,这种诱惑通常是在拟合噪音;额外的参数很少能改善样本外预测,反而经常让优化器在边界附近变得不稳定。优先选择简约模型,如果必须比较不同阶数,应通过前向切分上的样本外预测损失来比较,而不是用样本内AIC。当残差诊断仍显示问题时,通常应该改进的是新息分布或加入非对称项(第二部分内容),而不是提高阶数。

结构性断点被误读为持续性。 如前所述,波动率水平的永久性转变(新的市场状态、市场微观结构的变化)可能被GARCH吸收为虚假的高持续性,把α+β\alpha+\beta推向1。如果你的长期波动率估计在不同窗口间显得不稳定,应怀疑存在断点,而不是相信近IGARCH的点估计。滚动重新拟合,以及在适当情况下使用显式的状态模型,可以防范这一点。

把波动率预测当作收益率预测。 GARCH预测的是波动的幅度,而不是方向。它告诉你明天的波动可能有多大,而不是往哪个方向。这正是为什么它的天然用武之地是风险管理——仓位规模、止损、VaR——而不是信号生成。不要把一个良好的方差预测误认为是优势(edge)。

后续走向

GARCH(1,1)是基础,而且它刻意保持不完整。本系列在三个方向上对它加以扩展:

  • 非对称性与厚尾 —— 真实的加密货币波动率对下跌行情的反应比对上涨行情更强烈(杠杆效应),而高斯新息无法复现尾部特征。GJR-GARCH、EGARCH以及Student-t/偏态t新息在第二部分中讨论。
  • 多元波动率 —— 加密货币资产之间的相关性本身也是随时间变化的,并且在崩盘中会飙升。对整个协方差矩阵进行动态建模是第三部分:DCC-GARCH的内容,它与Markowitz均值方差理论以及一旦协方差变为动态后的基于CVaR的资产配置直接相连。
  • 完整策略 —— 仓位规模、止损、成本、换手率,以及诚实的前向评估,都汇聚在第四部分中。

以及GARCH边际分布如何汇入联合风险:这里的单变量条件方差模型正是GARCH-EVT-copula组合投资组合VaR/CVaR流程的第一阶段。一旦你从逐资产GARCH拟合中得到标准化残差,你就对它们做变换,再用copula把它们粘合在一起——边际分布是GARCH给出的,依赖结构是copula给出的。这一构造,包括尾部依赖和EVT尾部处理,在加密货币联合风险的copula模型一文中有深入介绍;本文正是支撑它的单变量引擎。

总结

  • 加密货币收益率表现出波动率聚集、厚尾,以及收益率无自相关但平方收益率有强自相关的特征。任何假设波动率恒定的工具——单一σ\sigma的Black-Scholes、静态VaR、固定百分比止损——都与这些事实不相符。
  • GARCH(1,1)σt2=ω+αεt12+βσt12\sigma_t^2 = \omega + \alpha\varepsilon_{t-1}^2 + \beta\sigma_{t-1}^2,用三个参数对随时间变化的条件方差建模:基线ω\omega、冲击反应α\alpha、持续性β\beta。它是一个具有几何衰减记忆的ARCH(\infty),这正是它优于高阶ARCH的原因。
  • 平稳性要求α+β<1\alpha+\beta<1;长期方差为ω/(1αβ)\omega/(1-\alpha-\beta),持续性为α+β\alpha+\beta,波动率半衰期为ln0.5/ln(α+β)\ln 0.5 / \ln(\alpha+\beta)。加密货币处于近IGARCH状态(α+β0.99\alpha+\beta\approx 0.99):高度持续、均值回归缓慢,且长期方差估计脆弱。
  • 用最大似然法估计。 高斯对数似然是一系列一步密度之和;用arch_model(r*100, vol="Garch", p=1, q=1)拟合它。记住**×100缩放**,并一致地反缩放每一个输出。
  • 预测以(α+β)h1(\alpha+\beta)^{h-1}的速率几何式地均值回归到长期方差。将逐日方差预测聚合起来得到持仓视野波动率——而不是用朴素的H\sqrt{H}法则。
  • 用平方标准化残差上的Ljung-Box检验和ARCH-LM检验来验证。 通过这些检验证实聚集现象已被建模消除;残留的厚尾特征则是第二部分的动机所在。
  • 将其应用于波动率目标仓位规模(wt=σtarget/σ^tw_t = \sigma_{\text{target}}/\hat\sigma_t,带上限)和波动率缩放止损(kσ^t(H)k\cdot\hat\sigma_t^{(H)})。同一个预测同时驱动两者,所以高波动状态下仓位会同时变小止损同时放宽。
  • 重要的陷阱:收益率缩放、拟合中的前视偏差(只在过去数据上拟合,始终前向验证)、预测时机、阶数过高,以及永远不要把方差预测误认为方向预测。

References:

  • Engle, R. F. (1982). Autoregressive Conditional Heteroscedasticity with Estimates of the Variance of United Kingdom Inflation. Econometrica, 50(4), 987-1007. DOI
  • Bollerslev, T. (1986). Generalized Autoregressive Conditional Heteroskedasticity. Journal of Econometrics, 31(3), 307-327. DOI
  • Mandelbrot, B. (1963). The Variation of Certain Speculative Prices. The Journal of Business, 36(4), 394-419. DOI
  • Nelson, D. B. (1991). Conditional Heteroskedasticity in Asset Returns: A New Approach. Econometrica, 59(2), 347-370. DOI
  • Katsiampa, P. (2017). Volatility estimation for Bitcoin: A comparison of GARCH models. Economics Letters, 158, 3-6. DOI
  • Chu, J., Chan, S., Nadarajah, S., & Osterrieder, J. (2017). GARCH Modelling of Cryptocurrencies. Journal of Risk and Financial Management, 10(4), 17. DOI
  • Sheppard, K. (2023). arch: Autoregressive Conditional Heteroskedasticity (ARCH) and other tools for financial econometrics in Python. GitHub.
免责声明:本文提供的信息仅用于教育和参考目的,不构成财务、投资或交易建议。加密货币交易涉及重大损失风险。

MarketMaker.cc Team

量化研究与策略

在 Telegram 中讨论
Newsletter

紧跟市场步伐

订阅我们的时事通讯,获取独家 AI 交易见解、市场分析和平台更新。

我们尊重您的隐私。您可以随时退订。