4、历史情景构建:基于历史极端事件
做压力测试,最怕什么?
怕你构造的场景不够「狠」。我见过太多团队,自己编的场景跑起来风平浪静,结果市场一波动,系统直接崩了。说白了,你编的场景再花哨,也不如真实发生过的事件有说服力。
所以这一章,咱们聊聊怎么用历史极端事件来构建情景。1987年股灾、2008年金融危机,这些都是绝佳的素材。
4.1 为什么要用历史事件?
我个人习惯,做压力测试第一件事就是翻历史。为什么?因为历史事件包含了真实的市场行为——恐慌、踩踏、流动性枯竭。这些是数学模型很难模拟出来的。
举个例子,1987年10月19日,道琼斯指数一天跌了22.6%。你想想看,这种级别的波动,你写代码模拟的时候,敢不敢设这么大的参数?但历史告诉你,它就是发生了。
核心原则:历史情景不是让你预测未来,而是验证你的系统在最坏情况下能不能扛住。
4.2 构建历史情景的步骤
嗯,这里要注意,不是把历史数据直接扔进去就完事了。你得做几步处理。
- 数据清洗——去掉停牌、异常跳空、数据缺失的部分
- 时间对齐——不同品种的交易时间不一样,得统一到同一时间轴
- 波动率缩放——把历史波动率映射到当前市场环境
- 相关性调整——历史的相关性结构可能已经变了,需要修正
我曾经在项目中遇到过一个问题:直接把2008年的数据拿来用,结果发现当时很多品种的流动性跟现在完全不是一个量级。跑出来的结果,系统显示没事,但我知道那是假的。
4.3 1987年股灾情景构建
1987年股灾有个特点:它是由程序化交易引发的。说白了,就是机器在互相踩踏。这个场景对现在的量化系统特别有参考价值。
具体怎么构建?我建议这样做:
- 提取1987年10月19日到10月20日的分钟级价格数据
- 计算当时的波动率曲线和相关性矩阵
- 把价格序列按比例缩放到当前市场水平
- 加入当时的成交量冲击模型
小技巧:1987年股灾中,标普500期货的跌幅比现货还大。如果你做期现套利,这个场景必须测。
4.4 2008年金融危机情景构建
2008年跟1987年不一样。它不是一天暴跌,而是持续几个月的流动性枯竭。这对系统的考验更大——不是瞬间崩盘,而是慢慢失血。
我个人习惯把2008年分成三个阶段:
| 阶段 | 时间 | 特征 |
|---|---|---|
| 第一阶段 | 2008年1月-3月 | 波动率上升,相关性开始变化 |
| 第二阶段 | 2008年6月-9月 | 流动性急剧下降,价差扩大 |
| 第三阶段 | 2008年10月-12月 | 全面恐慌,所有资产一起跌 |
构建情景时,我建议把这三个阶段的数据都提取出来。每个阶段对系统的压力点不一样。第一阶段考验风控的预警能力,第二阶段考验订单执行系统,第三阶段考验资金管理。
注意:2008年很多市场的涨跌停板制度跟现在不一样。直接套用历史数据,可能会触发当前系统的风控规则。一定要做规则适配。
4.5 核心逻辑框架
下面这张图,是我做历史情景构建时用的核心流程。你看一眼就明白了。
你看,整个流程是串行的。每一步都不能跳过。我见过有人图省事,直接拿历史数据跑,结果相关性没调整,跑出来的结果完全失真。
4.6 代码示例:提取历史事件数据
下面这段代码,是我常用的历史数据提取逻辑。你拿去改改就能用。
def extract_historical_event(start_date, end_date, symbols):
"""
提取历史事件期间的数据
"""
data = {}
for symbol in symbols:
# 从数据库或文件读取
raw = load_data(symbol, start_date, end_date)
# 清洗:去掉停牌和异常值
clean = raw[raw['volume'] > 0]
clean = clean[clean['close'] > 0]
# 计算收益率
clean['return'] = clean['close'].pct_change()
# 计算波动率
clean['volatility'] = clean['return'].rolling(20).std()
data[symbol] = clean
return data
提示:提取数据时,记得把当时的市场状态也记录下来——比如涨跌停板、熔断机制、交易时间。这些信息在后续的规则适配中会用上。
4.7 避坑指南
做历史情景构建,有几个坑我踩过,你注意一下:
- 不要直接复制历史价格——市场结构变了,价格水平也变了,必须做缩放
- 注意市场微观结构的变化——比如2008年还没有高频交易,现在的市场环境完全不同
- 相关性不是固定的——危机期间,所有资产的相关性都会趋向1,这个要单独处理
我曾经在项目中直接用了2008年的相关性矩阵,结果跑出来的情景显示风险对冲效果很好。但我知道这是假的——因为2008年后期,所有对冲策略都失效了。后来我加了一个「相关性漂移」的修正,才得到真实的结果。
4.8 小结
历史情景构建,说白了就是让历史重演一次。但你不能原封不动地搬过来,得做适配、做调整。1987年股灾和2008年金融危机是两个经典案例,一个测瞬时冲击,一个测持续压力。
我个人建议,每个季度至少更新一次历史情景库。市场在变,你的系统也在变,历史情景也得跟着迭代。
记住:历史不会简单重复,但它的韵律会再次响起。你的系统能不能听懂这个韵律,就看压力测试做得到不到位了。