第27章 压力测试与情景分析:极端市场下的曲面变化
说实话,做期权交易最怕什么?
不是方向看错,也不是波动率算错。最怕的是——黑天鹅突然飞出来,你手里的曲面模型瞬间变成废纸。
我2015年股灾那会儿就吃过这个亏。当时用的还是标准的SVI参数化,结果市场一崩,曲面直接扭曲到没法看。从那以后,我养成了一个习惯:每次建仓前,先做一轮压力测试。
为什么压力测试如此重要?
你想想看,我们平时用的波动率曲面,都是基于历史数据或者市场报价拟合出来的。但历史数据里,极端行情出现的频率极低。低到什么程度?可能你回测三年,都遇不到一次像样的崩盘。
但问题是,期权是尾部风险暴露最大的衍生品。特别是卖期权的人,平时收权利金收得爽,一旦市场剧变,可能一天亏掉三个月的利润。
我个人习惯把压力测试分成两类:
- 历史情景重现:比如2008年雷曼倒闭、2020年新冠熔断、2015年A股暴跌
- 假设情景构造:比如VIX瞬间飙升20个点、标普500单日跌10%
核心观点:压力测试不是为了预测未来,而是为了知道——当最坏情况发生时,你的组合到底能扛多久。
曲面在极端行情下的典型变化
我总结了几种常见的曲面变形模式,你在实战中一定会遇到:
| 市场状态 | 曲面特征 | 隐含意义 |
|---|---|---|
| 恐慌性下跌 | 虚值put的IV飙升,skew急剧左倾 | 市场在疯狂对冲下行风险 |
| 流动性枯竭 | 曲面出现锯齿状,买卖价差拉大 | 做市商不敢报价,模型失效 |
| 波动率集群 | 整个曲面向上平移,且短期端更陡 | 市场预期波动将持续 |
| 尾部风险释放 | 远端skew也变陡,长期波动率上升 | 市场认为风险不会很快消退 |
嗯,这里要注意:不是所有极端行情都长一个样。2020年3月那次,曲面是整体上移+左偏;但2018年2月的Volmageddon,却是短期端爆拉,远端几乎没动。
如何构建压力测试框架?
我自己的做法是这样的,分享给你参考:
- 确定压力因子:标的价格、波动率水平、利率、期限结构斜率
- 设定冲击幅度:比如标的价格-10%、波动率+50%、skew偏移30%
- 重新定价组合:用新的曲面参数计算所有头寸的损益
- 评估风险指标:希腊值变化、最大回撤、保证金占用
我的小技巧:不要只做单因子压力测试。极端行情往往是多因子同时爆发。比如2020年3月,标的价格跌、波动率涨、相关性也变了。你至少要做双因子甚至三因子的联合冲击。
代码实现:压力测试引擎
下面是我写的一个简化版压力测试函数。它接受当前的曲面参数,然后施加一组冲击,输出新的曲面和组合损益。
import numpy as np
from scipy.interpolate import griddata
def stress_test_surface(base_surface, shocks):
"""
base_surface: dict, 包含 strike, expiry, iv 等字段
shocks: dict, 包含 'spot_change', 'vol_shift', 'skew_tilt' 等
"""
# 1. 复制基础曲面
stressed = base_surface.copy()
# 2. 施加标的价格冲击
if 'spot_change' in shocks:
stressed['spot'] *= (1 + shocks['spot_change'])
# 3. 施加波动率水平冲击
if 'vol_shift' in shocks:
stressed['iv'] += shocks['vol_shift']
# 4. 施加skew偏移(虚值put的IV额外增加)
if 'skew_tilt' in shocks:
moneyness = stressed['strike'] / stressed['spot']
# 虚值put: moneyness < 0.9
mask = moneyness < 0.9
stressed['iv'][mask] += shocks['skew_tilt'] * (0.9 - moneyness[mask])
# 5. 重新插值生成新曲面
points = np.column_stack([stressed['strike'], stressed['expiry']])
grid_strike = np.linspace(stressed['strike'].min(), stressed['strike'].max(), 50)
grid_expiry = np.linspace(stressed['expiry'].min(), stressed['expiry'].max(), 20)
grid_x, grid_y = np.meshgrid(grid_strike, grid_expiry)
new_surface = griddata(points, stressed['iv'], (grid_x, grid_y), method='cubic')
return new_surface, stressed
避坑指南:我曾经犯过一个错误——压力测试时只改了波动率,没改利率和股息率。结果在极端利率环境下,曲面定价完全偏离。记住:所有输入参数都要同步更新,特别是利率和借贷成本。
情景分析的实战应用
光有压力测试还不够,你得会解读结果。我个人习惯把情景分析分成三个层次:
- 第一层:单一情景——比如"如果明天VIX翻倍,我的组合亏多少?"
- 第二层:路径依赖情景——比如"如果市场先跌10%,然后反弹5%,再跌15%",这种路径对期权组合的影响和一次性暴跌完全不同
- 第三层:相关性突变情景——比如"如果所有资产的相关性都变成1",这对多资产期权组合是毁灭性的
我记得有一次做跨式组合的压力测试。单看一次性暴跌,亏损在可接受范围内。但换成路径依赖情景——先跌后涨再跌——结果亏损翻了三倍。为什么?因为路径中的反弹让之前的对冲全部失效了。
可视化:压力测试下的曲面变形
下面这张SVG图展示了正常曲面和压力情景下的曲面对比。你可以直观看到skew如何左倾、曲面如何上移。
压力测试的局限性
说了这么多,我得泼盆冷水。压力测试不是万能的。
第一,你永远无法测试所有情景。市场总能用你没想到的方式搞死你。2020年负油价事件,之前有几个人做过原油期货跌到负数的压力测试?
第二,压力测试假设市场结构不变。但极端行情下,做市商可能直接撤单,流动性瞬间消失。你的模型假设能成交,但实际根本平不了仓。
第三,相关性会突变。平时不相关的资产,在危机中可能变成高度相关。你的分散化策略瞬间失效。
我的建议:压力测试不是做一次就完事了。每周至少跑一次,每次换不同的情景。而且,永远留出20%的保证金余量。别把杠杆用满,给自己留条活路。
实战中的避坑指南
最后,分享几个我踩过的坑:
- 别只看希腊值——压力测试要看实际损益,不是看Delta、Gamma这些线性近似。极端行情下,高阶项的影响非常大。
- 注意时间衰减——压力测试通常假设瞬间冲击。但实际市场变化可能需要几天甚至几周。你的Theta会帮你,也会害你。
- 别忘了保证金——有些组合在压力测试下损益不大,但保证金要求暴涨。你可能被迫平仓,死在黎明前。
嗯,关于压力测试和情景分析,今天就聊到这儿。记住一句话:市场永远比你想象的更极端。做好最坏的打算,才能活得更久。
个人习惯:我每次建仓前,都会在脑子里过一遍"如果明天发生XX事件,我的组合会怎样"。这个习惯救了我好几次。你也试试看。