4. 波动率估计方法(上):简单移动平均(SMA)、指数加权移动平均(EWMA)、GARCH(1,1)模型

做市策略里,波动率这东西,说白了就是市场的「脾气」。

脾气暴躁的时候,你价差得拉大,不然容易被抽脸。脾气温和的时候,价差收窄,多赚点周转的钱。我刚开始做做市那会儿,就吃过这个亏——固定价差跑了一整天,结果下午一波剧烈波动,库存全砸手里了。

所以,怎么实时、准确地估计波动率,是自适应调整的核心。今天咱们先聊三种最经典的方法:SMA、EWMA 和 GARCH(1,1)。

核心观点:波动率估计不是越复杂越好,关键看你的策略对「反应速度」和「平滑度」的取舍。

波动率估计方法体系 波动率估计 SMA(简单移动平均) EWMA(指数加权) GARCH(1,1) 等权重 · 平滑 · 滞后 指数衰减 · 反应快 波动聚集 · 自回归 低频策略 / 回测 中高频做市 波动率预测

4.1 简单移动平均(SMA)—— 最朴素的估计

SMA 的思路很简单:取过去 N 期的收益率,算个标准差,完事。

公式长这样:

σ²_sma = (1/N) * Σ (r_t - μ)²

其中 r_t 是每期的对数收益率,μ 是均值。N 是窗口长度。

我个人习惯用 20 期做日内 SMA。为什么是 20?因为差不多对应一个月的交易天数,能过滤掉一些随机噪音。

小技巧:如果你做的是 tick 级数据,N 别选太大。我试过 100 期 SMA,结果波动率曲线平得像一条直线,完全跟不上行情变化。

代码实现也很直白:

import numpy as np

def sma_volatility(returns, window=20):
    """
    简单移动平均波动率
    returns: 收益率序列
    window: 窗口大小
    """
    rolling_std = np.zeros_like(returns)
    for i in range(window, len(returns)):
        rolling_std[i] = np.std(returns[i-window:i])
    return rolling_std

优点:好理解,计算快,回测时用着很顺手。

缺点:所有数据权重一样。你想想看,10 天前的价格波动和 1 分钟前的波动,对当前的影响能一样吗?显然不能。但 SMA 偏要一视同仁。

注意:SMA 对异常值非常敏感。如果某根 K 线出现极端跳空,SMA 会在窗口期内一直「记住」这个异常,导致波动率被高估。我曾经因为这个在回测里亏了不少虚拟利润。

4.2 指数加权移动平均(EWMA)—— 给近期数据更多权重

EWMA 解决了 SMA 的痛点:近期的数据更重要。

公式:

σ²_t = λ * σ²_{t-1} + (1 - λ) * r²_{t-1}

λ 是衰减因子,通常取 0.94 或 0.97。λ 越大,历史数据衰减得越慢,曲线越平滑。

我建议高频做市用 λ=0.94。为什么?因为日内波动变化快,0.94 能让模型更快响应。如果是日线级别的策略,0.97 更合适。

def ewma_volatility(returns, lambda_=0.94):
    """
    指数加权移动平均波动率
    """
    sigma2 = np.zeros_like(returns)
    sigma2[0] = returns[0]**2
    for t in range(1, len(returns)):
        sigma2[t] = lambda_ * sigma2[t-1] + (1 - lambda_) * returns[t-1]**2
    return np.sqrt(sigma2)

实际效果对比:同样一段行情,SMA 的波动率曲线像被磨平了棱角,而 EWMA 能更敏锐地捕捉到波动率的突变。做市策略里,我更喜欢 EWMA,因为它能让我在波动率刚抬头时就调整价差。

不过 EWMA 也有坑。λ 的选取很主观。你想想看,0.94 和 0.97 之间差了 0.03,但长期运行下来,对策略收益的影响可能差好几个百分点。

避坑指南:我曾经在回测里用 EWMA 跑得风生水起,实盘一上线就崩了。后来发现是 λ 选得太小,模型对噪音过度反应。实盘时建议先用历史数据做参数扫描,找到最优 λ。

4.3 GARCH(1,1) 模型—— 让波动率自己「说话」

GARCH 比前两个要复杂一些。它假设今天的波动率不仅取决于昨天的收益率,还取决于昨天的波动率本身。

说白了,GARCH 认为波动率有「记忆」和「聚集」效应。大波动后面往往跟着大波动,小波动后面跟着小波动。这个特性在金融市场里非常常见。

GARCH(1,1) 的公式:

σ²_t = ω + α * r²_{t-1} + β * σ²_{t-1}

参数含义:

  • ω:长期平均波动率的基准
  • α:对近期收益率的敏感度(越大越敏感)
  • β:对历史波动率的记忆程度(越大越平滑)

通常要求 α + β < 1,否则模型不稳定。

from arch import arch_model

def garch_volatility(returns):
    """
    使用 arch 库拟合 GARCH(1,1)
    """
    model = arch_model(returns * 100, vol='Garch', p=1, q=1)
    res = model.fit(disp='off')
    # 返回条件波动率
    return res.conditional_volatility / 100

我的经验:GARCH(1,1) 在预测未来 1-2 步的波动率时表现不错,但计算开销大。做市策略里,我通常只在分钟级别或更高时间框架上用 GARCH,tick 级数据用 EWMA 就够了。

GARCH 的缺点也很明显:参数估计需要大量数据,而且对初始值敏感。如果数据量不够,拟合出来的参数可能毫无意义。

警告:别在数据量少于 500 个样本时用 GARCH。我曾经在 200 个 tick 上跑 GARCH,结果 α 和 β 估计值完全不合理,波动率预测值忽高忽低,差点把策略搞崩。

三种方法怎么选?

我直接给个参考表:

方法 计算速度 反应速度 适用场景
SMA 极快 回测、低频策略、波动率基准线
EWMA 中等 中高频做市、实时风控
GARCH(1,1) 快(预测能力强) 波动率预测、分钟级策略

嗯,这里要注意:没有万能的方法。我个人的习惯是,先用 SMA 搭个基线,然后用 EWMA 做实时调整,最后用 GARCH 做辅助预测。三者结合,效果最好。

最后一个小建议:不管你用哪种方法,记得定期重新校准参数。市场结构会变,去年的最优参数今年可能就是毒药。我每季度都会跑一次参数扫描,确保模型还活着。


专注资料整理