halo 的技术博客

返回

EWMADCC-GARCH 的思路是让风险估计跟上市场——市场变了,参数跟着变。这一篇换一个更简单粗暴的问题:能不能直接给”今天的市场有多不正常”打个分? 分数高就减仓,分数低就满仓。不建模波动率演化,不估计 regime 转移概率,只做一件事:把今天的跨资产收益向量,和历史分布比一比。

打分的工具是统计学的老古董——马氏距离(Mahalanobis, 1936),Chow et al.(1999)把它引入金融,Kritzman & Li(2010)给它起了个响亮的名字:金融湍流指数(Financial Turbulence Index):

dt2=(rtμ)Σ1(rtμ)d_t^2 = (r_t - \mu)' \Sigma^{-1} (r_t - \mu)

其中 r_t 是今天 N 个资产的收益向量,μ 和 Σ 是历史均值和协方差。如果收益服从多元正态,d² 服从自由度为 N 的卡方分布,均值恰好等于 N——这给了它一个天然的标尺:d² 远超 N 的日子,就是历史分布”没见过”的日子

为什么不直接看波动率#

因为 Σ⁻¹ 这个夹心让马氏距离同时看到两种异常,而波动率只能看到第一种:

幅度异常:某资产单日暴动 3σ——任何波动率指标都能抓到。

相关性破坏:每个资产的单日变动都不大,但组合方向违反了历史几何。最经典的例子是股债同跌:历史上股债负相关,债券是股票的对冲;当两者同时下跌时,哪怕各自只跌 1.8σ,这个方向在历史分布里也是极小概率事件——而这恰恰是对多资产组合最致命的一种日子,对冲失效的日子

马氏距离的核心:B 点单看波动率毫不起眼,但它违反了股债负相关的历史几何

上图用二维股债市场(历史相关 −0.5)把这件事画透了:A 点是股票大涨 2.5σ、债券小跌——幅度大但方向顺着历史相关的长轴走,d²=6.3;B 点是股债同跌各 1.8σ——每条边的幅度都比 A 小,但方向横穿椭圆的短轴,d²=13.0,反而高出一倍。等 d² 线是椭圆而不是圆,这就是 Σ⁻¹ 的全部作用:顺着历史相关方向的大波动被宽容,垂直于历史相关方向的小波动被重罚

只用波动率做风险开关,B 类日子完全漏网。而 2022 年的美股美债双杀、2013 年的 taper tantrum,都是 B 类日子。

实验:8 资产两 regime 市场#

模拟设定刻意贴近多资产配置的现实:8 资产(4 类股票、2 类债券、REIT、商品),15 年日频,两个 regime 由 Markov 链驱动——平静期股债相关 −0.1、湍流期波动率放大 2.5 倍且股债相关翻正到 +0.35(对冲失效);湍流平均持续约 33 天,占全样本 8.1%。

湍流指数用滚动 504 天窗口估 μ 和 Σ,逐日计算 d²:

import numpy as np

def turbulence_index(returns, window=504):
    """滚动窗口金融湍流指数"""
    T, N = returns.shape
    d2 = np.full(T, np.nan)
    for t in range(window, T):
        hist = returns[t-window:t]          # 只用 t 之前的数据
        mu = hist.mean(axis=0)
        S = np.cov(hist, rowvar=False)
        x = returns[t] - mu
        d2[t] = x @ np.linalg.solve(S, x)   # solve 而非显式求逆
    return d2
python

金融湍流指数:尖峰与真实湍流段高度重合

结果比预期还干净:d² 判别真实 regime 的 AUC 达 0.941,湍流日 d² 中位数 21.8,平静日 6.5(理论均值 N=8 附近)——两个分布几乎不重叠。图中红色底纹(真实湍流段)与蓝色尖峰逐段对齐,而下方等权组合净值的每一段回撤,也都发生在红色底纹里。

湍流的两个可交易性质#

指数能识别 regime 只是第一步。要让它变成能赚钱的风险开关,还需要两个性质,缺一不可:

湍流有记忆:高湍流日之后的市场更险恶

性质一:湍流有记忆。 今天 d² 超过 90% 分位,明天仍超过的概率是 52%——是无条件概率(10%)的五倍多,且这个条件概率随滞后天数缓慢衰减,20 天后仍高于基准。这意味着看到高湍流再减仓不算太晚:你错过的只是第一天,后面还有一整段。

性质二:高湍流日之后的收益分布更险恶。 高湍流日后 10 日组合收益:均值 −0.22% vs 平静日后的 +0.22%,波动 4.19% vs 1.90%。均值更低、方差翻倍——减仓避开的不是随机的 10 天,是期望为负且波动加倍的 10 天。

这两个性质合起来就是风险开关的全部逻辑:湍流可以被识别、会持续、且持续期内市场系统性更差。三者同时成立,事后减仓才有正期望。

风险开关回测#

规则刻意简单到没有调参空间:昨日 d² 超过滚动 90% 分位(分位数只用截至昨日的历史计算,无 look-ahead)→ 今日仓位降到 30%;否则满仓等权组合

pos = np.ones(T)
for t in range(start, T):
    hist_d2 = d2[warmup:t]                       # 截至 t-1 的 d² 历史
    threshold = np.nanquantile(hist_d2, 0.90)    # 滚动分位,无未来信息
    if d2[t-1] > threshold:                      # 昨日信号,今日执行
        pos[t] = 0.3
strategy_ret = port_ret * pos
python

湍流风险开关回测:Sharpe 0.49 → 0.92,最大回撤 −21.2% → −13.6%

指标买入持有湍流开关
年化收益5.12%8.10%
年化波动11.51%8.94%
Sharpe0.490.92
最大回撤−21.2%−13.6%
减仓时间占比11.2%

只在 11% 的时间里降到三成仓位,年化收益反而更高——因为砍掉的恰好是期望为负的时段,减仓不但降波动还加收益,两头赚。图下方的仓位指示条和真实 regime 底纹的重合度,就是这台开关的工作证明。

必须马上自己拆台:这是对该策略最友好的实验环境。模拟市场的湍流期期望收益是明确的深负(年化 −25% 量级)、regime 平均持续 33 天、切换由干净的 Markov 链驱动。真实市场的湍流没这么深、没这么持久、边界没这么清晰——Kritzman & Li 在真实数据上报告的改善集中在回撤端,收益端远没有这么慷慨。把上表读成”上界演示”而不是”业绩预告”。

A. 实现细节#

数据:8 资产两 regime 合成市场,15 年日频,regime 由 Markov 链驱动(平静→湍流 0.4%/天,湍流→平静 3%/天),湍流期波动率 ×2.5、相关性抬升、股债相关由 −0.1 翻正至 +0.35、股票类期望收益转深负。湍流指数:滚动 504 天窗口估 μ 与 Σ,d² 用 np.linalg.solve 计算避免显式求逆。开关规则:昨日 d² 与”截至昨日的 d² 历史滚动 90% 分位”比较,超过则今日仓位 0.3,否则 1.0——信号滞后一天执行,阈值无未来信息。业绩统计从窗口期+1 年后开始,避免 warmup 段污染。

B. 已知偏差#

环境偏袒是头号问题:合成市场的 regime 差异(波动 ×2.5、收益转深负、持续 33 天)比真实市场极端,AUC 0.941 和收益反超买入持有都不应外推到实盘;真实数据上这类开关的主要贡献在回撤压缩,Sharpe 改善通常温和。Σ⁻¹ 的维度诅咒被回避了:N=8、W=504(T/N=63)非常宽裕;资产数上到 50+ 时样本逆矩阵会把 d² 变成噪声放大器,必须先对 Σ 做 Ledoit-Wolf 收缩再求逆。参数未调优也未做敏感性:90% 分位、30% 仓位、504 天窗口全是第一次拍脑袋的值,没有扫描——这既是诚实(没有过拟合)也是缺陷(不知道稳不稳)。交易成本未建模,但开关平均每年只切换数次,成本影响有限。

C. 结果解读#

结论一:马氏距离的独门价值是”相关性破坏检测”,这是波动率指标的盲区。 股债同跌各 1.8σ 的 d²(13.0)是股票单边大涨 2.5σ(6.3)的两倍——对冲失效的日子恰恰是多资产组合最危险的日子,而它们在波动率雷达上不显形。

结论二:风险开关赚的是湍流的持续性,不是预知力。 开关永远在急跌第一天迟到,它的正期望全部来自两个实测性质:高湍流次日仍高湍流的概率 52%(5 倍于基准),高湍流后 10 日期望 −0.22%、波动翻倍。任何 regime 持续性弱的市场(快涨快跌、V 型反转频繁),这台开关会反复被打脸——减在最低点、加在反弹后。

结论三:减仓时间占比 11%,换来回撤从 −21.2% 到 −13.6%——性价比在回撤端,不在收益端。 本实验收益端也赢是因为模拟湍流期期望深负;真实市场更常见的结局是收益略降、回撤大降、Sharpe 靠分母改善。给它定位:组合的保险丝,不是发动机

结论四:d² 的质量上限就是 Σ 的质量上限。 这个指标把协方差估计的所有问题原样继承——维度诅咒、时变性、厚尾。高维场景先收缩再求逆;如果想让 μ 和 Σ 也跟上市场,可以用 EWMA 版本的湍流指数,但那又引入了 λ 的选择问题。没有免费的午餐,只有把复杂度放在哪一层的选择。

马氏距离金融湍流指数:用历史分布的『异常度』做风险开关
https://blog.halo26812.eu.org/blog/mahalanobis-turbulence-index
Author halo
Published at 2026年7月28日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨