halo 的技术博客

返回

核心结论:波动率目标风控对单个基金是无可指摘的纪律,对整个行业是一台放大器。 在 120 家基金同跑一套风控、持仓重叠 72% 的世界里,基本面路径最低 −12.88%,实际价格却跌到 −16.98%——差的那 −4.10 pp 完全是行业自己卖出来的,与基本面无关。而这不是「杠杆高」的错:把持仓重叠度设为 0,同样的杠杆、同样的风控、同样的冲击,内生跌幅精确归零

基本面路径与实际价格路径对比:外生冲击 −5.5% 发生在第 20 日,基本面最低 −7.44%,实际价格最低 −9.72%,阴影区为内生跌幅 −2.27 pp

一、一句每个风控总监都会说的话#

波动率涨了,我们降了仓。

这句话在任何一次风控例会上都无法被反驳。它符合波动率目标(vol targeting)的教科书定义,符合监管期待,符合投资者的风险预算约定。单独看它是对的

问题是:如果全行业 120 家基金在同一天、用同一个 EWMA 波动率估计、看着同一个价格序列、说了同一句话——那么「降仓」这个动作本身就变成了下一轮价格下跌的原因。

这条链路每一环都平淡无奇:

坏消息 → 价格跌 → 实现波动率上升 → 风险模型要求降杠杆
      → 卖出 → 价格进一步跌 → 波动率进一步上升 → 继续降杠杆 ...
plaintext

真正的困难在于归因:一次 −17% 的下跌里,多少是坏消息本身,多少是大家争着卖出来的?真实数据回答不了这个问题。因为你观测不到「如果没人被迫卖,价格会在哪里」的反事实。

所以本文用受控模拟:基本面路径由外生给定,任何偏离基本面的价格变动,按构造 100% 归因于被迫卖出。这不是估计,是恒等式。

二、世界设定与那个必须避开的初始化陷阱#

120 家基金,每家初始权益 1 亿美元,平均杠杆 4.0x(横截面标准差 22%)
40 个标的,每家基金 72% 的仓位落在共同篮子里(拥挤度)
市场深度:26 亿美元净流出压低共同篮子 1%
外生冲击:第 20 日 −5.5%,随后实现波动率跳升 2.4 倍
经纪商初始保证金 14%(对应 7.1x 杠杆上限),随波动率顺周期上调
plaintext

核心循环:

第 2 步是整个模型的关键,也是最容易写错的地方。 我第一版把 fund_ret 喂给了波动率估计器:

rvol = 0.86 * rvol + 0.14 * abs(fund_ret)   # 错:风控看不到基本面
python

这样写反馈环是断的——卖出会压低价格,但压低的价格不会反过来抬高测得的波动率,于是没有第二轮。跑出来的内生跌幅只有 −1.07 pp,看起来「螺旋效应很小」。这不是发现,是 bug:现实中没有任何风险系统能看到基本面,它们只能看到成交价。

第二个陷阱更隐蔽:初始状态必须是均衡的。我最初把 target_vol 设成 10%,而环境波动率是 18%,结果全行业在第 1 天就开始猛烈去杠杆——等到第 20 日冲击到来时,杠杆早就降完了,实验测了个寂寞(杠杆扫描全线接近 0,爆仓 0 家)。修正是让目标波动率默认等于环境波动率,并让每家基金瞄准自己的起始杠杆:

# 错:所有基金第 1 天就被强行拉到同一个杠杆水平
desired_lev = np.minimum(target_vol / ann_vol * lev, max_lev)
# 对:保留横截面差异,且 t=0 时 desired == actual,没人在卖
desired_lev = np.minimum(target_vol / ann_vol * lev_i, max_lev)
python

第三个:基本面随机流必须与基金人口随机流分开。否则「只留 1 家基金」的安慰剂会同时改变新闻本身,两个世界不可比。用两个独立的 default_rng 解决。

三、机制的四个横截面#

四面板:① 风险模型看到的波动率从冲击前夕 26.2% 跳到峰值 42.8%;② 折扣率从 14% 上调到上限 50%,可用杠杆从 7.1x 压到 2.0x;③ 被迫卖出流量峰值 8,071 百万美元;④ 行业总敞口去杠杆 76.6%,峰值 114 家同日追保

这四张图是同一件事的四个切面:

  1. 波动率:外生冲击让 EWMA 估计从冲击前夕的 26.2% 跳到峰值 42.8%,然后久久不回落——因为后续的价格波动里有一部分是抛压自己造成的,风险模型把它当成新的坏消息继续读
  2. 折扣率:经纪商把保证金要求从 14% 提到 50%(上限),可用杠杆从 7.1x 压到 2.0x。注意这一步是自动的,没有任何人做出「收紧信贷」的决策,合同里就是这么写的
  3. 卖出流量:峰值单日 80.7 亿美元砸在一个 26 亿深度的篮子上,全程累计卖出 336.6 亿
  4. 总敞口:行业去杠杆 76.6%,峰值 114 家(120 家里的 95%)在同一天收到追保通知

最后一点值得停一下:114/120 家同日追保。这在单家基金的风险报告里会被记录成「我们遭遇了市场压力」,但从系统视角看,这是 114 个独立主体在同一秒钟被同一个公式推向同一个方向。它们之间没有任何协调,效果却和串通好了一样。

四、三条安慰剂:螺旋到底是谁造成的#

三面板安慰剂:① 只有 1 家基金去杠杆,实际 vs 基本面几乎重合,内生 −0.0146 pp;② 价格冲击设为 0,内生 +0.000000 pp;③ 持仓零重叠,内生 +0.000000 pp

安慰剂 1:只留一家基金#

保留全部机制——同样的杠杆、同样的风控、同样的追保、同样的价格冲击函数——只是把基金数量从 120 减到 1。

结果:内生跌幅 −0.0146 pp,放大倍数 1.0020x

这条检验证明:问题不在「杠杆」这个属性上。一家 4 倍杠杆的基金在深市场里去杠杆,对价格的影响约等于零。同样的杠杆放在 120 家身上,内生跌幅变成 −4.10 pp——放大了 281 倍,而每家基金的行为一模一样

这也是为什么单基金层面的压力测试永远看不到这个风险:你的模型里没有别人。

安慰剂 2:价格冲击设为 0#

impact = 0,保留所有卖出行为。

结果:内生跌幅 +0.000000 pp,实际价格与基本面路径的偏差 0.00e+00(精确到浮点零)。

这条锁死了传导渠道:全部的内生跌幅通过价格冲击这一条路径产生,没有任何别的隐藏通道在贡献数字。如果我的代码里有第二条泄漏路径(比如爆仓时错误地记了额外损失),这条检验会立刻暴露。

安慰剂 3:持仓零重叠#

保留杠杆、保留冲击函数,只把拥挤度设为 0(每家基金持有完全不同的东西)。

结果:内生跌幅 +0.000000 pp,爆仓 0 家。

三条合起来给出一个非常干净的结论:

螺旋 = 杠杆 × 拥挤 × 冲击。三个因子任何一个为零,螺旋精确消失。

而公共讨论几乎只谈第一个因子。

五、剂量反应:拥挤度是那个被忽略的变量#

左:杠杆扫描,1.5x 内生 −1.19 pp 升到 5.0x 的 −4.92 pp,6.0x 回落到 −3.63 pp,同时爆仓家数飙到 36.7 家;右:拥挤度扫描,0% 内生 0.000 pp 单调升到 100% 的 −5.440 pp

拥挤度扫描(40 条路径均值)

持仓重叠内生跌幅放大倍数
0%0.000 pp1.00x
20%−1.084 pp1.09x
40%−2.255 pp1.19x
55%−3.145 pp1.27x
72%−4.102 pp1.35x
85%−4.765 pp1.41x
100%−5.440 pp1.47x

单调、近似线性、从精确零开始。这是本文最干净的一条剂量反应曲线。

杠杆扫描则出现了一个拐点

平均杠杆实际跌幅内生跌幅放大平均爆仓家数
1.5x−14.06%−1.19 pp1.10x0.0
3.0x−15.61%−2.73 pp1.23x0.0
4.0x−16.98%−4.10 pp1.35x0.4
4.5x−17.55%−4.68 pp1.41x2.3
5.0x−17.80%−4.92 pp1.43x8.0
6.0x−16.51%−3.63 pp1.32x36.7

6.0x 时内生跌幅反而下降。这看起来像是「杠杆越高越安全」,当然不是。它是幸存者效应的镜像:36.7 家基金(30% 的行业)在冲击中直接爆仓退场,敞口被清零,它们没机会继续卖了。

这是一个很重要的解读陷阱。如果你只看「价格跌幅」这个指标,高杠杆世界看起来比中杠杆世界温和;但换个指标——48% 的路径出现基金爆仓,单路径最多 69 家——就完全是另一幅图景。跌幅小是因为死得快。

实践含义:用「历史最大跌幅」校准的风险模型,会系统性低估高杠杆环境的危险,因为跌幅本身被爆仓截断了。

六、顺周期折扣率:一个符号会翻转的检验#

这是全文最反直觉的一段,也是我一开始算错方向的地方。

直觉是:波动率挂钩的折扣率(vol-linked haircut)加剧螺旋,因为它在最坏的时候抽走杠杆。所以静态折扣率应该更温和。

左:静态 14% 折扣率 vs 波动率挂钩折扣率,在基准杠杆 4.0x 和高杠杆 5.5x 两组下的平均最大跌幅与爆仓家数对比;右:单日减仓执行比例从 10% 到 100% 的扫描,最大跌幅、期末收益与峰值单日卖出

实测(40 条路径均值)

情形静态折扣率 14%波动率挂钩差额
基准杠杆 4.0x−16.67%−16.98%多跌 0.31 pp
高杠杆 5.5x−17.52%−17.45%少跌 0.07 pp

符号翻转了。 在基准杠杆下顺周期折扣率确实更糟(多跌 0.31 pp,符合直觉);在高杠杆下它反而略好(少跌 0.07 pp)。

为什么?看爆仓数据:5.5x 下静态折扣率爆仓 20.8 家,动态折扣率爆仓 19.0 家。顺周期折扣率通过提前强制降杠杆,反而救下了一部分基金——它把损失从「少数基金彻底归零」重新分配成「所有基金多亏一点」。

同时它的代价体现在另一个指标上:峰值单日抛压从 11,221 mm 涨到 22,497 mm(正好翻倍)。也就是说,顺周期折扣率把同样的总去杠杆量压缩到更短的时间里——对市场冲击更集中,对个体基金的存活反而更友好。

这个结果不能被简化成「顺周期折扣率是好是坏」。它的诚实版本是:它是一个把系统性冲击强度换取个体存活率的旋钮,而这两者的相对权重取决于你是监管者还是基金经理

七、减仓速度:慢反而跌得多#

单日减仓比例平均最大跌幅平均期末收益峰值单日卖出平均爆仓
10%−17.69%−13.17%13,1900.4
35%−17.09%−13.25%11,3790.4
45%−16.98%−13.27%11,3060.4
60%−16.85%−13.31%11,7960.4
100%−16.71%−13.51%15,0800.8

慢速减仓(10%/天)的最大跌幅比快速减仓(100%/天)更深(−17.69% vs −16.71%)。

原因是拖延让波动率反馈跑了更多轮:慢慢卖 → 价格慢慢跌 → 波动率持续维持高位 → 风险模型持续要求降仓 → 卖得更久。而一次性砍完的世界里,抛压在一两天内出清,波动率估计开始衰减,环就断了。

但峰值单日抛压呈 U 形:45% 处最低(11,306),两端都高。10% 时高是因为战线拉长后累积了更多需要卖的量,100% 时高是因为全部挤在一天。

这条曲线不适合直接搬到实盘:它假设市场深度恒定,没有建模「快速大单会临时抽干流动性」这个真实存在的非线性。现实中 100%/天 的执行会遇到深度塌陷,模型看不到这一层。慢速的劣势在模型里被夸大了。

八、分布:单条路径说明不了任何事#

左:200 条路径的最大跌幅分布,均值 −16.46%,5 分位 −27.33%;右:放大倍数分布,均值 1.45x,95 分位 1.99x,5.0% 的路径放大超 2 倍

200 条路径,外生冲击恒定为 −5.5%

  • 最大跌幅均值 −16.46%,5 分位 −27.33%,95 分位 −6.35%
  • 放大倍数均值 1.45x,95 分位 1.99x,最小 1.00x
  • 内生跌幅占总跌幅均值 28.5%
  • 5.0% 的路径放大超过 2 倍
  • 8.0% 的路径出现基金爆仓,单路径最多 69 家

同一个坏消息,跌幅从 −6% 到 −27% 都有可能。决定结果的不是消息,是消息到达时行业的杠杆和拥挤状态,以及运气

这也解释了为什么事后复盘总是充满争议:分析师看到 −27% 的那条路径会说「市场对基本面反应过度」,看到 −6% 的那条会说「市场消化良好」——而两条路径的基本面完全相同

顺带说明单条路径的不可靠性:我在做参数扫描时最初只用一个种子,结果某个种子恰好在冲击当天见底(风控还没来得及反应),内生跌幅精确为 0,整条扫描曲线全是零。风险模型有一天的反应滞后,所以「市场当天就见底反弹」的路径按构造不可能有螺旋。所有扫描因此改为 40 条路径均值。

九、陷阱与局限(诚实版)#

1. 市场深度是常数,这是最大的简化

现实中危机时深度会塌陷,做市商撤退。本文用固定的 26 亿深度,系统性低估了螺旋强度。真实的 2008 年、2020 年 3 月,深度在几小时内蒸发到平时的 1/5。

2. 没有反向力量

模型里没有任何逢低买入的资金——没有价值投资者、没有央行、没有做市商回补。现实中这些力量的进场是螺旋终止的主要机制,本文完全没有建模,所以路径尾部的深度是偏悲观的。

3. 折扣率函数的指数 1.5 是我设的

haircut = 0.14 × (vol/0.18)^1.5 这个形式和参数没有公开数据校准。经纪商的实际保证金规则是阶梯式、带自由裁量、且因客户而异。指数改成 1.0 或 2.0 会显著改变第六节的结论。

4. 所有基金用同一个波动率估计器

现实中不同机构的 lookback、衰减因子、风险模型都不同,这种异质性本身就是缓冲。本文假设完全同质,高估了同步性。不过考虑到主流风险系统的趋同程度,这个假设未必离谱。

5. 爆仓门槛(权益跌到 10%)是任意的

真实的基金清盘取决于赎回条款、LP 行为、经纪商关系,不是一个净值阈值。第五节的爆仓数字应当看作定性指标,不是可以引用的定量结论。

6. 只有一个共同因子

真实的拥挤是多维的(行业、风格、因子暴露)。本文用一个标量 crowding 概括,捕捉不到「部分重叠」的复杂结构。

十、实践 Checklist#

  • 在压力测试里加入别人。单基金 VaR 永远看不到这个风险——安慰剂 1 显示同样的杠杆下,一家和 120 家的差别是 281 倍
  • 监控拥挤度,不只是杠杆。拥挤度是唯一给出干净单调剂量反应的变量(0% → 100% 对应 0.000 → −5.440 pp),而它在大多数风险报告里根本不出现
  • 算一下「如果和我持仓相似的人同时降仓」。可用的粗略代理:13F 重叠度、拥挤度因子、同类策略 AUM 占标的 ADV 的比例
  • 别用历史最大跌幅校准高杠杆情形。6.0x 的跌幅反而小于 5.0x,因为 30% 的参与者已经爆仓退场——跌幅被截断了,风险没有
  • 和经纪商谈折扣率的路径依赖,不只是水平。顺周期折扣率在高杠杆下把峰值抛压翻倍(11,221 → 22,497 mm),这个条款的系统性代价不在你的费率表上
  • 减仓速度不是越慢越好。模型里 10%/天 比 100%/天 跌得更深,但这条结论的前提是深度恒定——实盘要在「拖长战线」和「打穿深度」之间自己找平衡点
  • 同一个消息,跌幅分布是 [−27%, −6%]。不要用单次事件的复盘去校准模型,也不要相信任何声称「市场对 X 反应过度」的事后叙事

本文所有数字基于 SEED=20260806 的受控模拟,参数为定性校准,真实市场可能存在量级差异。

保证金螺旋:每家基金都在正确地降风险,市场却因此崩了
https://blog.halo26812.eu.org/blog/margin-spiral-deleveraging
Author halo
Published at 2026年8月6日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨