市场极端情景构建:从历史中学习,为未来做准备
做市策略的压力测试,说白了就是问自己一个问题:「如果市场疯了,我的策略还能扛得住吗?」
我个人习惯把极端情景分成两类:一类是已经发生过的历史灾难,另一类是还没发生但理论上可能出现的噩梦。这两类都得覆盖,缺一不可。
历史极端行情复盘:那些年我们经历过的闪崩
先聊聊历史复盘。为什么要看历史?因为历史会重演,虽然不会完全一样,但人性不变。
我记得2010年5月6日的「闪电崩盘」——道琼斯指数在几分钟内暴跌近1000点,然后又迅速反弹。当时很多做市商的订单流直接被打穿,止损单像多米诺骨牌一样倒下。我有个朋友当时在芝加哥做期权做市,他说那几分钟里,他的风险模型完全失效了,因为价格波动超出了模型训练数据的10个标准差。
还有2015年8月24日的A股闪崩。那天早上开盘,很多股票直接跌停,流动性瞬间消失。做市商想对冲都找不到对手盘。嗯,这里有个关键点:历史极端行情往往伴随着流动性枯竭,这两者是孪生兄弟。
我建议你至少复盘以下三类历史事件:
- 闪崩类:2010美股闪电崩盘、2015A股股灾、2019年日本闪崩
- 黑天鹅类:2008年雷曼倒闭、2020年新冠爆发、2022年Luna崩盘
- 流动性危机类:1998年LTCM倒闭、2007年量化基金崩盘、2021年Archegos爆仓
你想想看,这些事件虽然起因不同,但最终的表现形式高度相似:波动率飙升、买卖价差扩大、深度骤降。说白了,就是市场从「正常模式」切换到了「生存模式」。
合成极端情景设计:制造你还没见过的噩梦
光靠历史数据是不够的。为什么?因为下一次黑天鹅可能跟以前都不一样。所以我个人习惯在历史数据基础上,再合成一些极端情景。
合成情景的核心思路是:把市场状态参数推到极端值。我常用的几个维度:
| 参数维度 | 正常范围 | 极端情景 |
|---|---|---|
| 波动率(年化) | 15%-30% | 100%-300% |
| 买卖价差 | 0.01%-0.05% | 0.5%-2% |
| 订单簿深度 | 正常水平 | 降至10%以下 |
| 相关性 | 0.3-0.7 | 趋近1或-1 |
举个例子,我曾经设计过一个「波动率飙升+流动性枯竭」的合成情景。具体做法是:把历史数据中的波动率乘以3,同时把订单簿深度除以10。你猜怎么着?我的做市策略在这种情景下,最大回撤直接翻了5倍。嗯,这就是压力测试的意义——提前知道你的策略在极端情况下会死得多惨。
核心原则:合成情景不是瞎编,而是基于市场微观结构的合理外推。比如波动率飙升时,流动性一定会下降,这是市场规律。
情景参数化方法:把极端情景变成可计算的模型
光有情景描述还不够,得把它们变成参数化的模型,这样才能跑回测。我常用的参数化方法有三种:
1. 历史情景直接回放
最简单粗暴的方法:把历史极端行情的数据直接喂给策略。比如把2010年5月6日那天的逐笔成交数据拿出来,让策略在那样的市场环境下跑一遍。
# 伪代码示例:历史情景回放
historical_data = load_flash_crash_data('2010-05-06')
for tick in historical_data:
strategy.on_tick(tick)
check_risk_metrics()
2. 参数扰动法
对历史数据的某些参数进行扰动,生成新的情景。比如把波动率提高50%,同时把买卖价差扩大一倍。
# 参数扰动示例
original_vol = 0.25
stressed_vol = original_vol * 3.0 # 波动率飙升3倍
original_spread = 0.0002
stressed_spread = original_spread * 10 # 价差扩大10倍
3. 蒙特卡洛模拟
这是最灵活的方法。通过随机生成市场状态参数,模拟出成千上万种极端情景。我建议至少跑10万次模拟,才能覆盖到尾部风险。
避坑指南:我曾经犯过一个错误——只用了历史数据做压力测试,结果策略在2020年3月的流动性危机中直接崩了。后来我才意识到,历史数据只能覆盖「已知的未知」,而合成情景才能覆盖「未知的未知」。
知识体系框架图
下面这张图展示了极端情景构建的完整逻辑:
重要提醒:参数化不是终点,而是起点。你设计完情景后,一定要跑回测验证。我见过太多人设计出漂亮的极端情景,结果回测时发现策略根本跑不通——因为参数设得太极端了,超出了市场微观结构的物理极限。
最后说一句:极端情景构建没有标准答案。每个做市商、每个策略都有自己的脆弱点。关键是要系统性地思考:你的策略最怕什么?是波动率飙升?还是流动性枯竭?还是两者同时发生?想清楚这些,你才能设计出真正有意义的压力测试。
公众号:蓝海资料掘金营,微信deep3321