4. 市场风险模型(上):波动率建模与相关性建模

各位同学,欢迎来到第四章。这一章我们聊点硬核的——波动率和相关性。说实话,这两个东西是做市风控的命根子。你想想看,如果连资产价格的波动幅度和资产之间的联动关系都搞不清楚,那你的风控模型基本就是在裸奔。

我个人习惯把市场风险拆成两块:单资产的风险多资产之间的风险。单资产看波动率,多资产看相关性。今天我们先啃下这两块硬骨头。

市场风险模型(上) 波动率建模 相关性建模 GARCH系列 已实现波动率 波动率预测 DCC-GARCH Copula 应用:VaR计算 → 保证金定价 → 压力测试 → 对冲策略

4.1 波动率建模:GARCH系列

波动率这东西,说白了就是资产价格的"脾气"。有的资产温顺得像只猫,有的暴躁得像头狮子。而且最要命的是——波动率会聚集。什么意思?就是大涨大跌之后往往跟着更大的波动,平静期之后还是平静。

我第一次做跨境做市的时候,就吃过这个亏。当时用简单历史波动率去算保证金,结果遇到 Brexit 事件,波动率瞬间翻了三倍,保证金直接穿仓。嗯,从那以后我再也不敢用静态波动率了。

4.1.1 GARCH(1,1) 模型

GARCH 模型的核心思想很简单:今天的波动率 = 昨天的波动率 + 昨天的冲击。用数学表达就是:

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

其中:
- σ²_t:今天的条件方差
- ω:长期平均方差
- ε²_{t-1}:昨天的市场冲击(残差平方)
- σ²_{t-1}:昨天的方差
- α + β < 1:保证模型平稳

我个人习惯用 Python 的 arch 包来拟合。给你看个实际代码:

import arch
import numpy as np
import pandas as pd

# 假设 returns 是日收益率序列
returns = pd.Series(...)

# 拟合 GARCH(1,1)
model = arch.arch_model(returns, vol='Garch', p=1, q=1)
result = model.fit(update_freq=5)

# 查看参数
print(result.params)
# 输出类似:omega=0.0001, alpha=0.08, beta=0.91

# 预测未来10天的波动率
forecast = result.forecast(horizon=10)
vol_forecast = np.sqrt(forecast.variance.values[-1, :])
我的经验:GARCH(1,1) 在大多数外汇和股票数据上表现都不错。但如果你遇到加密货币这种"妖孽"资产,建议试试 GARCH(1,2) 或者 EGARCH。EGARCH 能捕捉杠杆效应——就是下跌时波动率上升比上涨时更猛。

4.1.2 已实现波动率

GARCH 用的是日频数据,但高频数据里其实藏着更多信息。已实现波动率(Realized Volatility)就是利用日内高频数据来估计波动率。

公式很简单:

RV_t = Σ r²_{t,i}

其中 r_{t,i} 是第 t 天第 i 个五分钟收益率

为什么用五分钟?太短了有微观结构噪声,太长了又丢失信息。五分钟是个业界公认的 sweet spot。

我记得有一次做 EUR/USD 的做市模型,用 GARCH 预测的波动率总是偏小,导致报价太激进。后来换成已实现波动率 + HAR-RV 模型,效果立竿见影。

# 计算已实现波动率
def compute_rv(price_series, freq='5T'):
    # 重采样到5分钟
    resampled = price_series.resample(freq).last()
    # 计算对数收益率
    log_ret = np.log(resampled / resampled.shift(1))
    # 计算已实现方差
    rv = (log_ret ** 2).sum()
    return np.sqrt(rv * 252)  # 年化
避坑指南:我曾经在某个新兴市场货币上直接用5分钟数据算 RV,结果波动率异常大。后来发现是因为那个市场流动性差,很多5分钟区间根本没有成交。解决方案是:先做流动性过滤,只保留有成交的区间。

4.2 相关性建模:DCC-GARCH 与 Copula

单资产的风险好办,但做市商手里通常握着几十个资产。这些资产之间会互相影响——美元涨了,黄金可能跌;股市崩了,避险货币可能涨。所以,相关性建模比波动率建模更关键

4.2.1 DCC-GARCH 模型

DCC-GARCH(Dynamic Conditional Correlation)是 Engle 在2002年提出的。它的核心思想是:相关性不是固定的,它会随时间变化

模型分两步走:

  1. 第一步:对每个资产单独拟合 GARCH,得到标准化残差
  2. 第二步:用这些标准化残差来估计动态相关性
from arch import arch_model
import numpy as np

# 假设有两个资产的收益率序列
returns_1 = pd.Series(...)
returns_2 = pd.Series(...)

# 第一步:分别拟合 GARCH
model_1 = arch_model(returns_1, vol='Garch', p=1, q=1).fit(disp='off')
model_2 = arch_model(returns_2, vol='Garch', p=1, q=1).fit(disp='off')

# 提取标准化残差
std_resid_1 = model_1.resid / model_1.conditional_volatility
std_resid_2 = model_2.resid / model_2.conditional_volatility

# 第二步:估计动态相关性
# 这里用滚动窗口计算
window = 60
corr_series = []
for i in range(window, len(std_resid_1)):
    corr = np.corrcoef(std_resid_1[i-window:i], 
                       std_resid_2[i-window:i])[0,1]
    corr_series.append(corr)
关键点:DCC-GARCH 的优势在于它能捕捉到"相关性突变"。比如 2008 年金融危机时,几乎所有资产的相关性都趋近于1——这就是所谓的"相关性崩溃"。如果你用固定相关性模型,那段时间你的风控模型会完全失效。

4.2.2 Copula 模型

DCC-GARCH 有个局限——它假设资产之间的依赖关系是线性的、对称的。但现实世界不是这样的。比如:

  • 尾部依赖:市场暴跌时,资产一起跌的概率远大于一起涨的概率
  • 非对称依赖:熊市里的相关性比牛市里高得多

Copula 就是来解决这个问题的。它把联合分布拆成两部分:边缘分布(每个资产自己的分布)和依赖结构(Copula 函数)。

常用的 Copula 有几种:

Copula 类型 特点 适用场景
Gaussian Copula 对称、无尾部依赖 正常市场环境
t-Copula 对称、有尾部依赖 有极端事件的市场
Clayton Copula 下尾依赖强 熊市、危机时期
Gumbel Copula 上尾依赖强 牛市、泡沫时期
# 使用 Python 的 copulae 库
from copulae import ClaytonCopula
import numpy as np

# 假设有两个资产的标准化残差
u = np.column_stack([std_resid_1, std_resid_2])

# 拟合 Clayton Copula
cop = ClaytonCopula()
cop.fit(u)

# 查看参数
print(cop.params)
# theta 越大,下尾依赖越强

# 模拟联合分布
simulated = cop.random(10000)
我的建议:在实际做市风控中,我通常同时跑三个模型:Gaussian Copula(日常用)、t-Copula(压力测试用)、Clayton Copula(极端情景用)。然后取最保守的那个结果来定保证金。虽然保守了点,但安全第一。

4.3 实战中的坑与经验

讲了这么多理论,最后分享几个我在实战中踩过的坑:

  1. 数据频率不匹配:别用日频数据算波动率,然后用分钟频数据算相关性。频率不一致会导致模型自相矛盾。
  2. 参数稳定性:GARCH 参数不是一成不变的。我习惯每三个月重新拟合一次,遇到重大事件(比如央行议息)会手动触发重拟合。
  3. 相关性不等于因果关系:两个资产相关性高,不代表一个涨另一个就一定涨。做对冲策略时一定要注意这个。
  4. 计算效率:如果你有50个资产,DCC-GARCH 要算1225对相关性。我建议用 PCA 降维,先提取主要因子,再对因子做相关性建模。

好了,这一章的内容就到这里。波动率和相关性是市场风险模型的基石,下一章我们会在此基础上搭建完整的 VaR 和压力测试框架。记住:模型越复杂,不代表越准确。有时候一个简单的 GARCH(1,1) + 滚动相关性,比那些花里胡哨的深度学习模型靠谱得多。


专注资料整理