4、GARCH模型:波动率聚集效应、GARCH(1,1)模型原理、参数估计方法
做市这行,说白了就是跟波动率打交道。你赚的钱,本质上是在管理波动率风险后的剩余价值。我刚开始做量化做市那会儿,最头疼的就是波动率突然变大——明明前几分钟还风平浪静,突然一根大阴线下来,仓位直接被打穿。后来我才明白,这不是偶然,而是市场波动本身就有个特性:大波动后面往往跟着大波动,小波动后面跟着小波动。这就是我们今天要聊的——波动率聚集效应。
4.1 波动率聚集效应:市场为什么会“抱团”波动?
先看一个真实场景。你在做市ETH/USDT,前10分钟波动率只有0.2%,你觉得很安全,仓位加到了80%。结果第11分钟,一根大单砸下来,波动率瞬间飙到1.5%。你还没来得及减仓,第二根、第三根大单又来了——波动率连续5分钟维持在1%以上。这就是典型的波动率聚集。
为什么会这样?我个人的理解是:市场参与者的行为是相互传染的。一个大波动会触发止损单、爆仓单、跟风盘,这些订单又引发新的波动。就像多米诺骨牌,一个倒了,后面的跟着倒。你想想看,如果波动率是随机独立的,那大波动之后应该立刻回归平静才对——但现实不是这样。
核心结论:波动率存在正自相关性。高波动率时期倾向于持续,低波动率时期也倾向于持续。这就是波动率聚集效应。
我在做市策略里,专门加了一个波动率状态检测模块。如果当前波动率处于高位,我会强制降低仓位上限。这个逻辑的数学基础,就是GARCH模型。
4.2 GARCH(1,1)模型原理:用过去预测未来
GARCH模型,全称是广义自回归条件异方差模型。名字很吓人,但说白了就是:用过去的波动和过去的方差,来预测当前的方差。
GARCH(1,1)是最常用的形式。它的公式长这样:
σ²_t = ω + α * ε²_{t-1} + β * σ²_{t-1}
解释一下每个符号的意思:
- σ²_t:当前时刻的方差(波动率的平方),我们要预测的东西
- ω:长期平均方差,可以理解为波动率的“基准线”
- ε²_{t-1}:上一时刻的“冲击”平方,也就是上一时刻实际收益偏离均值的程度
- σ²_{t-1}:上一时刻的方差,也就是上一时刻预测的波动率水平
- α:冲击系数,衡量新信息对波动率的影响程度
- β:衰减系数,衡量波动率记忆性的强弱
这个模型的核心思想是:当前波动率 = 长期基准 + 近期冲击 + 历史惯性。α越大,说明市场对新信息越敏感;β越大,说明波动率越“粘”,不容易消退。
个人经验:我在做BTC做市时,发现BTC的β值通常在0.85-0.95之间,说明波动率记忆性很强。而一些山寨币的α值更高,说明它们对新闻、大单更敏感。这个差异在做市策略里非常重要——BTC适合用更平滑的仓位调整,山寨币则需要更激进的响应。
GARCH(1,1)还有一个重要约束:α + β < 1。如果α+β≥1,说明波动率会无限增长,模型就不稳定了。我见过一些新手直接用默认参数跑GARCH,结果α+β=1.02,预测的波动率越跑越大,最后仓位全乱了。嗯,这里要注意。
4.3 参数估计方法:怎么让模型“拟合”真实市场?
模型有了,参数怎么定?总不能拍脑袋吧。常用的方法是极大似然估计(MLE)。说白了就是:找一组参数,让模型在历史数据上出现的概率最大。
具体步骤是这样的:
- 准备数据:取过去N天的收益率序列,比如过去500个5分钟收益率
- 设定初始值:ω、α、β给个初始猜测,比如ω=0.01,α=0.1,β=0.8
- 迭代计算:用当前参数计算每一天的预测方差,然后计算似然函数值
- 优化求解:用数值优化算法(比如BFGS)调整参数,让似然函数最大
- 收敛判断:当参数变化小于某个阈值时,停止迭代
下面是一个用Python实现的GARCH(1,1)参数估计示例:
import numpy as np
from scipy.optimize import minimize
def garch_likelihood(params, returns):
omega, alpha, beta = params
T = len(returns)
sigma2 = np.zeros(T)
sigma2[0] = np.var(returns) # 初始方差用样本方差
# 递归计算方差
for t in range(1, T):
sigma2[t] = omega + alpha * returns[t-1]**2 + beta * sigma2[t-1]
# 计算对数似然(假设正态分布)
likelihood = -0.5 * np.sum(np.log(sigma2) + returns**2 / sigma2)
return -likelihood # 取负号,因为我们要最大化
# 模拟一些收益率数据
np.random.seed(42)
returns = np.random.randn(1000) * 0.02
# 初始参数猜测
initial_params = [0.01, 0.1, 0.8]
# 约束条件:α+β < 1,且所有参数>0
bounds = [(1e-6, None), (1e-6, 1), (1e-6, 1)]
constraints = {'type': 'ineq', 'fun': lambda x: 1 - x[1] - x[2]}
# 优化求解
result = minimize(garch_likelihood, initial_params, args=(returns,),
bounds=bounds, constraints=constraints, method='SLSQP')
omega_est, alpha_est, beta_est = result.x
print(f"估计结果: ω={omega_est:.6f}, α={alpha_est:.4f}, β={beta_est:.4f}")
print(f"α+β = {alpha_est + beta_est:.4f}")
避坑指南:我曾经在实盘中使用GARCH模型时,发现参数估计对数据长度非常敏感。用100个数据点和用1000个数据点,估计出来的α和β可能差很多。我的建议是:至少用500个数据点,并且每周重新估计一次参数。另外,如果市场结构发生重大变化(比如2020年3月的暴跌),一定要重新估计,不能沿用旧参数。
4.4 知识体系总览
为了让你更直观地理解GARCH模型在做市中的应用,我画了一张流程图:
这张图把整个流程串起来了:从市场数据出发,用GARCH模型估计参数,然后预测当前波动率,最后根据波动率水平动态调整做市仓位。我在实盘里就是这么干的——每天开盘前跑一次GARCH,得到当天的波动率基准,然后根据实时行情微调。
4.5 实战中的几个要点
最后,分享几个我在实战中总结的经验:
- 数据频率要匹配:如果你做的是高频做市(秒级),用5分钟收益率估计GARCH可能不合适。我一般用和交易频率相同的时间窗口。
- 不要迷信模型:GARCH假设收益率服从正态分布,但实际市场有厚尾。极端行情下,GARCH预测的波动率可能偏低。我会在GARCH基础上加一个“极端波动缓冲器”——如果预测波动率低于历史90%分位数,就取90%分位数。
- 参数更新频率:我个人习惯每周一重新估计一次参数。如果市场波动剧烈(比如VIX飙升),我会缩短到每天一次。
- 别忘了ω:很多新手只关注α和β,忽略了ω。其实ω代表长期波动水平,如果市场整体波动率中枢上移,ω也会变大。我见过有人用一年前的参数做今天的做市,结果ω严重偏低,仓位过大,一波行情就亏回去了。
一句话总结:GARCH模型就是帮你回答一个问题——“现在的波动率,到底该用多大的仓位去接?” 参数估计是基础,但真正值钱的是你如何把预测结果转化成仓位控制规则。
公众号:蓝海资料掘金营,微信deep3321