第24章 曲面在压力测试中的应用:极端情景模拟、尾部风险度量
压力测试,说白了就是问自己一个问题:“如果市场疯了,我的头寸会怎样?”
我见过太多交易员,平时赚得盆满钵满,一次黑天鹅事件就回到解放前。为什么?因为他们只看了平值附近的波动率,忽略了曲面的尾部信息。今天我们就聊聊,怎么用波动率曲面来做压力测试,度量那些“万一”的风险。
为什么曲面比单一波动率更适合压力测试?
单一波动率(比如ATM IV)只能告诉你“市场平均恐慌程度”。但极端行情下,不同行权价、不同期限的期权反应完全不同。
举个例子:2008年金融危机时,标普500的虚值看跌期权(OTM Put)的隐含波动率飙升到80%以上,而平值期权只有40%左右。如果你只用ATM波动率做压力测试,你会严重低估尾部风险。
曲面包含了偏斜(Skew)和期限结构(Term Structure)的信息。压力测试的核心,就是让这些维度同时“变形”,模拟极端情景。
极端情景模拟的三种方法
我个人习惯把情景模拟分成三类。每种方法都有它的适用场景,我一个个说。
1. 历史重现法
找历史上最极端的几次事件,把当时的曲面变化直接套用到当前市场。
比如:
- 2008年雷曼倒闭
- 2010年闪电崩盘
- 2020年新冠疫情
- 2023年SVB事件
具体做法:
- 提取事件发生前后N天的曲面数据
- 计算曲面每个节点的变化量(Delta IV)
- 将变化量叠加到当前曲面上
- 重新定价组合中的所有期权
2. 参数扰动法
这种方法更灵活。我们直接对曲面的关键参数进行扰动,比如:
- 整体波动率水平(Level)上移/下移
- 偏斜(Skew)变陡/变平
- 期限结构(Term Structure)倒挂/陡峭
我常用SVI参数化模型来做这件事。SVI模型把曲面分解成几个参数:
# SVI参数化示例
def svi_vol(k, a, b, rho, m, sigma):
"""
k: 对数行权价 ln(K/F)
a, b, rho, m, sigma: SVI参数
"""
return a + b * (rho * (k - m) + np.sqrt((k - m)**2 + sigma**2))
# 压力情景:偏斜变陡(rho增大,b增大)
stress_params = {
'a': 0.15, # 整体水平
'b': 0.30, # 偏斜幅度(正常0.2,压力0.3)
'rho': -0.7, # 偏斜方向(正常-0.5,压力-0.7)
'm': 0.0,
'sigma': 0.1
}
# 生成压力曲面
k_values = np.linspace(-0.5, 0.5, 50)
stress_vols = [svi_vol(k, **stress_params) for k in k_values]
这样做的好处是:你可以精确控制“哪个维度”发生极端变化。比如只让尾部变贵,但平值不动。
3. 随机情景生成法
这是最“量化”的方法。我们用历史数据拟合曲面参数的联合分布,然后蒙特卡洛模拟。
步骤大致如下:
- 收集过去5年的曲面参数时间序列(比如SVI参数)
- 拟合参数之间的相关性矩阵
- 用Copula或PCA生成大量随机情景
- 筛选出尾部情景(比如损失最大的5%)
# 简化的蒙特卡洛模拟
import numpy as np
from scipy.stats import multivariate_normal
# 假设我们有3个参数:level, skew, term
mean = [0.20, -0.50, 0.05] # 均值
cov = [[0.01, -0.003, 0.001],
[-0.003, 0.02, -0.002],
[0.001, -0.002, 0.005]] # 协方差矩阵
# 生成10000个情景
n_scenarios = 10000
scenarios = multivariate_normal.rvs(mean=mean, cov=cov, size=n_scenarios)
# 筛选尾部情景(比如level > 均值+3倍标准差)
tail_scenarios = scenarios[scenarios[:, 0] > mean[0] + 3*np.sqrt(cov[0,0])]
print(f"尾部情景数量: {len(tail_scenarios)}")
尾部风险度量:不止是VaR
说到尾部风险,很多人第一反应是VaR(Value at Risk)。但VaR有个致命缺陷:它只告诉你“最差情况下损失不超过X”,却不告诉你“超过X之后会亏多少”。
我建议至少用以下三个指标:
| 指标 | 含义 | 压力测试中的用法 |
|---|---|---|
| VaR (95%/99%) | 在给定置信水平下的最大损失 | 设定止损线、保证金要求 |
| CVaR (Expected Shortfall) | 超过VaR后的平均损失 | 评估尾部风险的严重程度 |
| 最大回撤 | 历史或模拟中的最大亏损幅度 | 心理承受能力测试 |
用曲面做压力测试时,我特别关注CVaR。为什么呢?因为期权组合的损失分布往往不是对称的。比如你卖了一堆虚值看跌期权,平时收权利金很开心,但一旦市场暴跌,损失可能是指数级的。VaR可能只告诉你“95%的情况下损失不超过100万”,但CVaR会告诉你“剩下5%的情况下,平均损失是500万”。
一个完整的压力测试流程
说了这么多,我们来走一遍完整的流程。假设你管理一个期权做市商的投资组合。
- 获取当前曲面:从市场数据中提取当前所有期限和行权价的隐含波动率
- 定义压力情景:选择3-5个极端情景,比如:
- 情景A:市场暴跌20%,波动率整体上移30%
- 情景B:偏斜变陡,虚值看跌IV飙升50%
- 情景C:期限结构倒挂,短期IV高于长期IV
- 生成压力曲面:用前面讲的方法,把当前曲面变形
- 重新定价:用压力曲面重新计算所有期权头寸的价值
- 计算损益:新价值 - 当前价值 = 压力损失
- 汇总报告:按情景、按期限、按行权价汇总损失
# 压力测试核心逻辑(伪代码)
def run_stress_test(portfolio, current_surface, stress_scenarios):
results = {}
for scenario_name, transform_func in stress_scenarios.items():
# 生成压力曲面
stress_surface = transform_func(current_surface)
# 重新定价
total_pnl = 0
for position in portfolio:
new_price = price_option(position, stress_surface)
total_pnl += (new_price - position.current_price) * position.quantity
results[scenario_name] = total_pnl
return results
曲面压力测试的SVG知识框架
下面这张图总结了本章的核心逻辑。我画的时候特意把“输入-处理-输出”分成了三层,方便你理解。
实战中的几个坑
最后,分享几个我在实战中踩过的坑,希望能帮你少走弯路。
- 不要只做单一情景:我曾经只测试了“波动率上升”这一个情景,结果市场暴跌时波动率确实上升了,但偏斜变得极其陡峭,我的组合还是亏了大钱。多情景测试是必须的。
- 注意流动性风险:压力测试假设你能在当前价格平仓。但极端行情下,很多期权可能根本没有买盘。你的压力损失可能比模型算出来的大得多。
- 定期更新历史数据:2020年之前,很多人觉得“波动率到50%就是极端了”。结果2020年3月,VIX飙到82。历史数据窗口要足够长,而且要包含最近的极端事件。
- 压力测试不是预测:它只是告诉你“如果...会怎样”。不要因为压力测试结果好就放松警惕,也不要因为结果差就过度对冲。保持平衡。
公众号:蓝海资料掘金营,微信deep3321