第19章:压力测试与情景分析:历史情景、假设情景、极端市场条件模拟

做市商这行,说白了就是跟不确定性打交道。你设计的定价模型再漂亮,希腊值对冲得再完美,一旦市场出现极端行情,所有假设都可能瞬间崩塌。我见过太多团队,平时回测曲线漂亮得不行,结果一次黑天鹅事件就把几年的利润全吐回去了。

所以,压力测试和情景分析不是可选项,而是必修课。今天我就把这块的核心逻辑和实操方法掰开揉碎讲清楚。

19.1 为什么压力测试是做市商的“安全气囊”?

先问个问题:你的模型在正常市场里跑得好好的,凭什么要花精力做压力测试?

原因很简单——市场不会永远正常。2015年瑞郎黑天鹅、2020年原油负价格、2023年硅谷银行事件,每一次都让大批做市商爆仓。我自己的团队在2020年3月就吃过亏,当时VIX飙到80以上,我们持有的期权头寸Delta对冲策略完全失效,因为流动性枯竭了,你根本没法按理论价格平仓。

压力测试的核心目的就三个:

  • 识别脆弱点:哪些参数变动会要了你的命?
  • 量化极端损失:最坏情况下亏多少?能不能扛住?
  • 优化风控参数:止损线、保证金比例、头寸限额该怎么设?

核心观点:压力测试不是为了预测未来,而是为了确保你在最坏情况下还能活着。

19.2 三种情景类型:历史、假设、极端

我个人习惯把情景分成三类,每一类的用途和构建方法都不一样。

19.2.1 历史情景

历史情景最简单——直接拿过去发生过的极端行情来测试。比如:

  • 2008年雷曼倒闭时的市场波动
  • 2011年欧债危机时的信用利差飙升
  • 2020年3月新冠疫情引发的流动性危机

具体做法是:把历史某段时间内所有资产的价格变动、波动率曲面变化、相关性矩阵全部提取出来,然后施加到当前持仓上。

# 伪代码示例:历史情景回测
def historical_scenario(portfolio, date_range):
    # 提取历史数据
    hist_data = load_market_data(date_range)
    
    # 对每个时间点计算组合损益
    pnl_series = []
    for t in hist_data:
        new_prices = apply_shock(portfolio.current_prices, t.returns)
        pnl = calculate_pnl(portfolio, new_prices)
        pnl_series.append(pnl)
    
    # 找出最大回撤和VaR
    max_drawdown = min(pnl_series)
    var_95 = np.percentile(pnl_series, 5)
    
    return max_drawdown, var_95

这里有个坑——历史不会简单重复。2008年的危机模式,放到今天的市场结构下可能完全不一样。所以我建议把历史情景当作“最低标准”,而不是全部。

19.2.2 假设情景

假设情景更灵活,你可以自己定义“如果发生X,会怎样?”这类问题。比如:

  • 如果美联储突然加息200个基点?
  • 如果某只股票被恶意做空,波动率翻倍?
  • 如果两个高度相关的资产突然脱钩?

构建假设情景时,我通常会考虑三个维度:

维度 典型参数 冲击幅度
价格 标的资产价格 ±10% ~ ±50%
波动率 隐含波动率曲面 +20% ~ +200%
相关性 资产间相关系数 从0.8变为-0.3
流动性 买卖价差 扩大5~20倍

我的经验:假设情景不要只做“单因子”测试。真实市场里,价格暴跌往往伴随着波动率飙升和流动性枯竭。我习惯做“多因子联合冲击”,比如同时让标的价格跌20%、波动率涨50%、价差扩大10倍。这种组合拳才能暴露真正的风险。

19.2.3 极端市场条件模拟

极端情景,说白了就是“最坏情况有多坏”。这不仅仅是把参数调大,而是要思考市场机制可能失效的场景。

举个例子:2020年4月WTI原油期货跌到负值,这在任何历史数据里都找不到,任何假设情景也很难想到。但如果你做了极端条件模拟,就会考虑“如果交割机制导致价格突破零轴”这种可能性。

构建极端情景时,我常用的方法:

  1. 压力因子放大法:把历史最大波动再乘以2~3倍
  2. 尾部风险建模:用极值理论(EVT)拟合收益率的尾部
  3. 流动性黑洞模拟:假设所有对手方同时撤单,你只能以极差价格成交

19.3 核心框架:压力测试的完整流程

下面这张图是我自己总结的压力测试流程,每次做新项目都会拿出来对照一遍。

压力测试与情景分析流程图 步骤1:定义情景 历史/假设/极端 步骤2:施加冲击 价格/波动率/相关性 步骤3:计算损益 组合PnL/希腊值 步骤4:风险评估 VaR/ES/最大回撤 步骤5:阈值判断 是否超过风险限额? 是/否 触发风控措施 继续监控/调整参数 反馈循环:根据结果调整情景定义或风控参数

19.4 实操中的关键细节

理论讲完了,说点实际的。我在做压力测试时,有几个特别容易踩的坑。

19.4.1 相关性崩溃

正常市场里,股票和债券往往是负相关的。但危机来临时,所有资产可能一起跌,相关性瞬间变成1。如果你用历史相关性做对冲,这时候就惨了。

我的做法:在压力测试里,强制把相关性矩阵改成极端值。比如把所有相关系数设为0.8以上,看看组合会亏多少。

19.4.2 波动率曲面的扭曲

很多做市商只关注平值期权的波动率,但极端行情下,虚值期权的波动率会飙升得更厉害。我记得2020年3月,标普500的虚值看跌期权波动率从20%直接跳到150%。如果你只对冲了平值期权,虚值头寸会给你带来巨大损失。

避坑指南:我曾经因为只测试了平值波动率的变化,忽略了曲面扭曲,结果在2020年3月亏了300多万。从那以后,我每次压力测试都会同时调整整个波动率曲面的斜率、曲率和期限结构。

19.4.3 流动性分层

极端行情下,不同资产的流动性下降速度完全不同。国债可能还能交易,但某些结构化产品的价差可能直接变成“无报价”。

我建议在压力测试里加入流动性分层模型

  • 高流动性资产(国债、主要股指):价差扩大2~5倍
  • 中等流动性资产(个股、信用债):价差扩大5~10倍
  • 低流动性资产(结构化产品、奇异期权):价差扩大10~50倍,甚至无法成交

19.5 一个完整的压力测试案例

假设你是一个做市商,持有以下结构化产品组合:

  • 1000份挂钩沪深300的自动赎回型产品
  • 500份挂钩中证500的雪球产品
  • 200份挂钩某个股的鲨鱼鳍产品

你定义了一个极端情景:

  • 沪深300单日暴跌15%
  • 中证500暴跌20%
  • 个股暴跌30%
  • 所有波动率翻3倍
  • 买卖价差扩大10倍
# 压力测试核心代码片段
def extreme_scenario_test(portfolio, scenario_params):
    # 1. 施加价格冲击
    for product in portfolio:
        product.underlying_price *= (1 - scenario_params['price_shock'])
    
    # 2. 调整波动率曲面
    vol_surface = adjust_vol_surface(scenario_params['vol_multiplier'])
    
    # 3. 考虑流动性成本
    liquidity_cost = calculate_liquidity_cost(scenario_params['spread_multiplier'])
    
    # 4. 重新定价
    new_nav = 0
    for product in portfolio:
        price = reprice_product(product, vol_surface)
        new_nav += price * product.quantity
    
    # 5. 计算损益
    pnl = new_nav - portfolio.original_nav
    pnl -= liquidity_cost  # 扣除流动性成本
    
    return pnl

运行结果:组合净值从1.2亿跌到7800万,亏损4200万,超过公司设定的3000万风险限额。这时候就需要立即采取措施——要么减仓,要么买入保护性期权。

19.6 压力测试的频率和报告

我个人建议:

  • 每日:运行历史情景和一组标准假设情景
  • 每周:更新极端情景参数,加入新的市场风险因子
  • 每月:全面复盘,对比压力测试结果与实际市场表现

报告格式要简洁,重点突出:

情景名称 预期损失 是否超限 建议措施
2008年金融危机 -2800万 维持
2020年疫情冲击 -3500万 减仓20%
假设:加息200bp -1800万 维持
极端:多因子联合 -5200万 买入尾部对冲

最后说一句:压力测试不是做给别人看的,而是给自己用的。我见过太多团队,压力测试报告写得漂漂亮亮,但实际风控决策根本不参考。记住,压力测试的价值在于——当市场真的崩了,你知道自己能不能扛住,该怎么做。

嗯,这一章的内容就到这里。核心就一句话:别等到潮水退了,才发现自己在裸泳

交易系统化学习资料 微信Strategy888888