halo 的技术博客

返回

你的风控报告写着:5% 日 VaR = -3.8%。翻译过来:「正常市况下,只有 5% 的交易日会亏超过 3.8%。」

你信了。直到某个月,市场连续紧张,你发现有 16.6% 的日子亏得比 -3.8% 还多。

这不是你运气差,是 VaR 模型在说谎。传统 VaR 默认两件事:

  1. 波动率恒定(今天和暴涨那天一样);
  2. 收益分布对称(左右尾一样胖)。

可真实市场是:紧张时波动放大、左尾(亏损)更肥。「恒定 + 对称」的假设,让传统 VaR 在最危险的时候最危险地低估了风险

分位数回归(Quantile Regression, QR) 是解药之一。它不假设分布形状,直接去拟合「在给定条件下,第 5 个百分位是多少」——于是 VaR 可以随市场状态自适应移动。本文从零实现它,并把「正态 VaR 在高压区失灵 vs QR-VaR 自适应」跑出数字。

一、从「均值」到「分位」#

普通最小二乘(OLS)拟合的是条件均值 E[yx]E[y\mid x],目标是最小化平方误差——它只描述「平均水平」,对尾部视而不见。

分位数回归拟合的是条件分位数 Qτ(yx)Q_\tau(y\mid x),目标是最小化检查损失(Pinball / Check loss)

\begin{cases} \tau\cdot u, & u\ge 0\\[4pt] (\tau-1)\cdot u, & u<0 \end{cases},\qquad u = y - \hat q_\tau(x)$$ 其中 $\tau\in(0,1)$ 是目标分位(VaR 取 $\tau=0.05$ 看左尾)。 直觉:当真实值**跌破**你的分位预测($_u<0$,即发生亏损超预期的「突破」),损失的权重是 $1-\tau=0.95$——**对尾部突破惩罚极重**;当真实值高于预测,权重只有 $\tau=0.05$。于是 QR 被「拽」去准确圈住那 5% 的尾部,而不是去追均值。 ## 二、合成数据:异方差 + 左偏 真实风险有个铁律:**越紧张,波动越大;且亏损尾比盈利尾更肥**。造一组带这个结构的收益: ```python import numpy as np from scipy import stats from scipy.optimize import minimize rng = np.random.default_rng(20260719) N = 2000 x = rng.uniform(0, 1, N) # 压力代理: 0=平静, 1=紧张 # 左偏冲击(负偏 => 肥左尾) + 波动率随压力放大 z = stats.skewnorm.rvs(-4, size=N, random_state=12345) scale = 0.5 + 2.0 * x # 紧张时波动翻数倍 r = -0.05 * x + scale * z / np.std(z) * 0.8 # 轻微负漂移 + 压力缩放 Xdes = np.column_stack([np.ones(N), x]) # 设计矩阵 [1, x] ``` 注意 `scale = 0.5 + 2.0*x`:平静时波动小、紧张时波动大——这是**异方差**;`skewnorm(-4)` 让亏损尾更肥——这是**偏度**。传统正态 VaR 同时忽略这两者。 ## 三、从零实现 QR(不依赖 statsmodels) ```python def pinball_loss(beta, x, y, tau): u = y - x @ beta return np.mean(np.where(u >= 0, tau * u, (tau - 1) * u)) def fit_qr(x, y, tau): res = minimize(pinball_loss, np.zeros(x.shape[1]), args=(x, y, tau), method="Nelder-Mead", options={"maxiter": 5000, "xatol": 1e-6}) return res.x b_qr = fit_qr(Xdes, r, 0.05) # VaR 线 b_med = fit_qr(Xdes, r, 0.50) # 中位数线 b_hi = fit_qr(Xdes, r, 0.95) # 右尾线 ``` 拟合出的系数: | 分位 τ | 截距 β₀ | 斜率 β₁(压力敏感度) | |---|---|---| | 0.05(VaR) | **-1.2323** | **-5.0085** | | 0.50(中位数) | -0.4021 | -1.7342 | | 0.95(右尾) | 0.1356 | -0.0098 | 关键读出:**三条线不平行**。中位数线随压力只轻微下移(β₁=-1.73),而 5% VaR 线随压力**陡峭下移**(β₁=-5.01)——紧张时 VaR 比中位数跌得快得多。这正是「左尾随压力变肥」的非对称结构,OLS(只估一条均值线)根本看不见。 ![分位数回归:条件分位随压力非平行移动,捕捉非对称/异方差尾部](/images/quantile-regression-var/qr_quantile_lines.png) > 图 1:灰点=样本收益,红/黑/蓝线分别是 τ=0.05/0.50/0.95 的 QR 拟合。三条线斜率明显不同——紧张(x 大)时,5% 左尾比中位数跌得猛得多,证明尾部风险是「状态依赖」的。 ## 四、QR-VaR vs 正态 VaR:回测见真章 传统 5% VaR 用全局正态假设: $$\text{VaR}_{0.05} = \hat\mu + \hat\sigma\cdot\Phi^{-1}(0.05)$$ 它给出一个**固定数字**,不随压力变化。QR-VaR 则是 $\hat q_{0.05}(x)=\hat\beta_0+\hat\beta_1 x$,**随压力 x 移动**。 ```python mu_n, sd_n = np.mean(r), np.std(r) var_norm = mu_n + sd_n * stats.norm.ppf(0.05) # 全局固定 VaR qr_var = b_qr[0] + b_qr[1] * x # 自适应 QR-VaR # 分区回测: 低压 (<0.33) / 中压 / 高压 (>0.66) low, mid, high = x < 0.33, (x>=0.33)&(x<0.66), x >= 0.66 def exc(var): return np.mean(r < var) # 正态VaR各压区超限率 vs QR-VaR各压区超限率 ... ``` ![VaR 回测:QR 自适应 vs 正态固定,高压区正态严重低估](/images/quantile-regression-var/var_backtest.png) > 图 2:左=按压力排序的收益(灰)、QR VaR 红线(随压力下移)、正态 VaR 蓝虚线(固定不动)、红点是 QR 下突破;右=三个压力区的超限率。绿虚线是目标 5%。 实跑数字(超限率 = 收益低于 VaR 的占比,应≈5%): | 压力区 | 正态 VaR 超限率 | QR-VaR 超限率 | 目标 | |---|---|---|---| | 低压区 | **0.2%** | 5.8% | 5% | | 中压区 | 2.9% | 3.3% | 5% | | **高压区** | **16.6%** | **6.1%** | 5% | | 全局 | 6.4% | — | 5% | 这组数字把全文结论砸死了: - **正态 VaR 在高压区超限率 16.6%**,是目标 5% 的 **3 倍多**——它以为「只有 5% 日子亏更多」,实际六分之一的日子都在亏更多。**最危险的时刻,它最危险地低估了风险**。 - **QR-VaR 在三个压区都贴近 5%**(5.8%/3.3%/6.1%),虽然中压区略低(抽样噪声),但结构上远远稳过正态。 - 连全局正态 VaR 的超额率都有 6.4%(偏离 5%),因为它假设对称、被左偏尾轻微拖累——侧面印证「对称假设本身就在骗你」。 ## 五、为什么 QR 比「直接给每个区算分位」强 你可能会说:既然风险随压力变,我干脆把数据按压力切成三堆,每堆各算一个经验分位不就行了? 能,但有代价: 1. **切块浪费样本**:每堆只有 1/3 数据,尾部(5%)每堆只剩约 33 个观测,分位估计方差爆炸; 2. **切块边界生硬**:x=0.33 左边和右边用两个不同 VaR,在边界处 VaR 会**跳变**,不可微、难优化; 3. **QR 用全部样本**拟合一条连续的分位线,用「线性结构假设」换取「样本效率高 + 平滑可微」。 代价是你要相信「分位随 x 线性移动」这个**参数假设**——这恰好引出下面的陷阱 2。 ## 六、四类真实陷阱 1. **压力变量 x 的选择是命门**:QR-VaR 好不好,全看 x 有没有抓住「风险状态」。用错了 x(比如用成交量当压力代理,而真正驱动波动的是 VIX 或利差),QR 线就是歪的,回测照样失灵。实务里 x 常用:**已实现波动率、VIX、信用利差、资金利率、甚至前一日收益的绝对值**。本文用合成 x 只是演示;真实落地要先做「x 与未来波动的相关性」筛选。 2. **线性假设与过拟合**:本文用 `q = β₀ + β₁·x` 线性 QR。若真实关系是非线性的(风险在某阈值后骤升),线性会系统性低估。解决:用**样条 QR(spline QR)**或把 x 做分段;但加复杂度就要更多数据,否则过拟合——小样本下复杂 QR 的回测会「看起来完美、实盘崩」。 3. **尾部外推与极端分位**:τ=0.05 已属尾部,τ=0.01(99% VaR)更极端。样本里低于 1% 的观测极少,QR 在这个分位上**估计方差极大**,且对那几个极端点极度敏感。对策:τ 别太低(实务常用 0.05/0.025)、用更厚样本、或对 QR 残差做 bootstrap 置信区间而非单点。 4. **回归法 vs 模拟法(蒙特卡洛)的定位**:QR 给的是**条件分位的参数化估计**,快、可解释、能归因「风险来自哪个状态」;但它依赖 x 的选择和线性假设。真正的压力测试常配合**蒙特卡洛模拟**(从残差分布重抽样 + 给定压力情景)交叉验证。两者不是替代关系:QR 做日常监控,模拟做极端情景压测。 ## 七、结论 正态 VaR 的致命伤是「恒定 + 对称」:它在平静时虚高、在紧张时(最该准的时候)严重低估——本文高压区超限率 **16.6% vs 目标 5%** 就是铁证。 分位数回归换了一套思维: - 用 **Pinball 检查损失**直接拟合条件分位,对尾部突破惩罚权重高达 $1-\tau$,**不被均值牵着走**; - 让 **VaR 随压力代理 x 自适应移动**,三条 QR 线(5%/50%/95%)**不平行**,把异方差 + 偏度一并装下; - 回测证明 QR-VaR 在三个压区都贴近 5%,而正态 VaR 在高压区失控; - 但代价是:x 必须选对、线性假设可能误判非线性、极端分位方差大——这些坑任一踩中,QR 也会翻车。 一句话:**VaR 不是算出来一个数就完了,而是要让这个数「知道现在市场有多紧张」。QR 给了你一把会随市场呼吸的尺子——前提是你知道该让它看哪个温度计。** --- *本文数据均为自洽合成(异方差 + 左偏收益),仅用于演示方法。真实落地时:审慎选择压力变量 x、非线性用样条 QR、极端分位(τ≤0.01)配 bootstrap 置信区间、QR 日常监控与蒙特卡洛压测交叉验证。代码已全部跑通,系数与各区超限率即输出。*
分位数回归在 VaR 估计中的应用:捕捉非对称尾部风险
https://blog.halo26812.eu.org/blog/quantile-regression-var
Author halo
Published at 2026年7月19日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨