保证金螺旋:每家基金都在正确地降风险,市场却因此崩了
「波动率涨了我们降了仓」在任何风控例会上都无法被反驳,但 120 家基金同说这句话时,它就是下一轮下跌的原因。受控模拟把基本面路径外生给定,因此每个百分点的归因是构造已知而非估计:基本面最低 −12.88%,实际价格跌到 −16.98%,差的 −4.10 pp 完全是行业自己砸出来的(放大 1.35x;200 条路径均值 1.45x、95 分位 1.99x)。三条安慰剂锁死机制:只留 1 家基金内生塌到 −0.0146 pp(同样杠杆同样行为,一家和 120 家差 281 倍);价格冲击设为 0 与持仓零重叠均精确归零——螺旋 = 杠杆 × 拥挤 × 冲击,任一为零则消失,而公共讨论几乎只谈第一个。杠杆扫描出现拐点:5.0x 内生 −4.92 pp,6.0x 反而回落到 −3.63 pp,因为 36.7 家已爆仓退场——用历史最大跌幅校准高杠杆风险会低估危险,跌幅被爆仓截断了。同一个消息,200 条路径跌幅从 −27.33% 到 −6.35%(高阶)
核心结论:波动率目标风控对单个基金是无可指摘的纪律,对整个行业是一台放大器。 在 120 家基金同跑一套风控、持仓重叠 72% 的世界里,基本面路径最低 −12.88%,实际价格却跌到 −16.98%——差的那 −4.10 pp 完全是行业自己卖出来的,与基本面无关。而这不是「杠杆高」的错:把持仓重叠度设为 0,同样的杠杆、同样的风控、同样的冲击,内生跌幅精确归零。

一、一句每个风控总监都会说的话#
波动率涨了,我们降了仓。
这句话在任何一次风控例会上都无法被反驳。它符合波动率目标(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核心循环:
for t in range(T):
# 1) 外生基本面
fund_ret = shock if t == SHOCK_DAY else rng_f.normal(0, vol_state)
log_fund += fund_ret
# 2) 风险模型看到的是「观测价格」,不是基本面 —— 这是反馈环的入口
rvol = np.sqrt(0.90 * rvol**2 + 0.10 * obs_ret_prev**2)
ann_vol = rvol * np.sqrt(252)
# 3) 顺周期折扣率:波动率越高,经纪商要的保证金越多
haircut = np.clip(0.14 * (ann_vol / 0.18)**1.5, 0.14, 0.50)
max_lev = 1.0 / haircut
# 4) 每家基金的目标杠杆同时下调
desired_lev = np.minimum(target_vol / ann_vol * lev_i, max_lev)
sell = np.maximum(gross - desired_lev * equity, 0) * react_speed
# 5) 只有拥挤的那部分砸在同一个篮子上
impact = -(sell.sum() * crowding / depth) * 0.01
obs_ret = fund_ret + impact # ← 环闭合
log_obs += obs_retpython第 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 解决。
三、机制的四个横截面#

这四张图是同一件事的四个切面:
- 波动率:外生冲击让 EWMA 估计从冲击前夕的 26.2% 跳到峰值 42.8%,然后久久不回落——因为后续的价格波动里有一部分是抛压自己造成的,风险模型把它当成新的坏消息继续读
- 折扣率:经纪商把保证金要求从 14% 提到 50%(上限),可用杠杆从 7.1x 压到 2.0x。注意这一步是自动的,没有任何人做出「收紧信贷」的决策,合同里就是这么写的
- 卖出流量:峰值单日 80.7 亿美元砸在一个 26 亿深度的篮子上,全程累计卖出 336.6 亿
- 总敞口:行业去杠杆 76.6%,峰值 114 家(120 家里的 95%)在同一天收到追保通知
最后一点值得停一下:114/120 家同日追保。这在单家基金的风险报告里会被记录成「我们遭遇了市场压力」,但从系统视角看,这是 114 个独立主体在同一秒钟被同一个公式推向同一个方向。它们之间没有任何协调,效果却和串通好了一样。
四、三条安慰剂:螺旋到底是谁造成的#

安慰剂 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 家。
三条合起来给出一个非常干净的结论:
螺旋 = 杠杆 × 拥挤 × 冲击。三个因子任何一个为零,螺旋精确消失。
而公共讨论几乎只谈第一个因子。
五、剂量反应:拥挤度是那个被忽略的变量#

拥挤度扫描(40 条路径均值):
| 持仓重叠 | 内生跌幅 | 放大倍数 |
|---|---|---|
| 0% | 0.000 pp | 1.00x |
| 20% | −1.084 pp | 1.09x |
| 40% | −2.255 pp | 1.19x |
| 55% | −3.145 pp | 1.27x |
| 72% | −4.102 pp | 1.35x |
| 85% | −4.765 pp | 1.41x |
| 100% | −5.440 pp | 1.47x |
单调、近似线性、从精确零开始。这是本文最干净的一条剂量反应曲线。
杠杆扫描则出现了一个拐点:
| 平均杠杆 | 实际跌幅 | 内生跌幅 | 放大 | 平均爆仓家数 |
|---|---|---|---|---|
| 1.5x | −14.06% | −1.19 pp | 1.10x | 0.0 |
| 3.0x | −15.61% | −2.73 pp | 1.23x | 0.0 |
| 4.0x | −16.98% | −4.10 pp | 1.35x | 0.4 |
| 4.5x | −17.55% | −4.68 pp | 1.41x | 2.3 |
| 5.0x | −17.80% | −4.92 pp | 1.43x | 8.0 |
| 6.0x | −16.51% | −3.63 pp | 1.32x | 36.7 |
6.0x 时内生跌幅反而下降。这看起来像是「杠杆越高越安全」,当然不是。它是幸存者效应的镜像:36.7 家基金(30% 的行业)在冲击中直接爆仓退场,敞口被清零,它们没机会继续卖了。
这是一个很重要的解读陷阱。如果你只看「价格跌幅」这个指标,高杠杆世界看起来比中杠杆世界温和;但换个指标——48% 的路径出现基金爆仓,单路径最多 69 家——就完全是另一幅图景。跌幅小是因为死得快。
实践含义:用「历史最大跌幅」校准的风险模型,会系统性低估高杠杆环境的危险,因为跌幅本身被爆仓截断了。
六、顺周期折扣率:一个符号会翻转的检验#
这是全文最反直觉的一段,也是我一开始算错方向的地方。
直觉是:波动率挂钩的折扣率(vol-linked haircut)加剧螺旋,因为它在最坏的时候抽走杠杆。所以静态折扣率应该更温和。

实测(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,190 | 0.4 |
| 35% | −17.09% | −13.25% | 11,379 | 0.4 |
| 45% | −16.98% | −13.27% | 11,306 | 0.4 |
| 60% | −16.85% | −13.31% | 11,796 | 0.4 |
| 100% | −16.71% | −13.51% | 15,080 | 0.8 |
慢速减仓(10%/天)的最大跌幅比快速减仓(100%/天)更深(−17.69% vs −16.71%)。
原因是拖延让波动率反馈跑了更多轮:慢慢卖 → 价格慢慢跌 → 波动率持续维持高位 → 风险模型持续要求降仓 → 卖得更久。而一次性砍完的世界里,抛压在一两天内出清,波动率估计开始衰减,环就断了。
但峰值单日抛压呈 U 形:45% 处最低(11,306),两端都高。10% 时高是因为战线拉长后累积了更多需要卖的量,100% 时高是因为全部挤在一天。
这条曲线不适合直接搬到实盘:它假设市场深度恒定,没有建模「快速大单会临时抽干流动性」这个真实存在的非线性。现实中 100%/天 的执行会遇到深度塌陷,模型看不到这一层。慢速的劣势在模型里被夸大了。
八、分布:单条路径说明不了任何事#

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 的受控模拟,参数为定性校准,真实市场可能存在量级差异。