halo 的技术博客

返回

先说结论:把 RNN 的输出层从 1 个头换成 5 个头、把 MSE 换成 pinball 损失,你就从「预测明天涨多少」升级成「预测明天收益的整条区间」——隐状态自动记住波动聚集,区间宽度随市场松紧收放。 在 GARCH-t 合成收益上,QRNN 的区间宽度与真实波动相关 0.861,样本外 pinball 损失 0.2720,距离知道真实波动率的 Oracle(0.2694)只差 1%。但也要诚实:只有 2 个参数的 EWMA-高斯(RiskMetrics)拿到 0.2717,跟 QRNN 打平——因为这个数据生成过程恰好是它的主场。什么时候神经网络才真的值得上,是这篇文章的核心。

QRNN 分位数区间预测:区间宽度随波动聚集自动收放

一、点预测的失职:风险管理要的是分布,不是均值#

「模型说明天涨 0.3%」——这句话对风控毫无用处。风控要问的是:

  • 95% 置信下,明天最坏跌多少?(VaR)
  • 收益区间有多宽?该配多少仓位?
  • 现在是高波动状态还是低波动状态?

这些问题的答案都藏在条件分布 p(rt+1Ft)p(r_{t+1} \mid \mathcal{F}_t) 里,而不是条件均值里。之前《分位数回归森林》一文用森林解决了「给出分布」的问题,但 QRF 是横截面模型——特征进、分位数出,没有记忆。金融收益最顽固的规律恰恰是时序性的:波动聚集(大波动跟着大波动)。今天市场惊涛骇浪,明天的收益区间就该更宽——这需要一个能记住「最近市场有多疯」的状态变量。

RNN 的隐状态天生就是干这个的。

二、方法:RNN 隐状态 + pinball 损失#

2.1 pinball 损失:让网络输出「分位数」而不是「均值」#

对目标分位数 τ,pinball(分位)损失定义为:

Lτ(y,q^)={τ(yq^)yq^(τ1)(yq^)y<q^L_\tau(y, \hat{q}) = \begin{cases} \tau \cdot (y - \hat{q}) & y \geq \hat{q} \\ (\tau - 1) \cdot (y - \hat{q}) & y < \hat{q} \end{cases}

直觉:τ=0.05 时,预测偏高(y < q̂)罚 0.95 倍、偏低只罚 0.05 倍——最优解会压到「只有 5% 的观测落在下方」的位置,这正是 5% 分位数的定义。MSE 的最优解是条件均值,pinball 的最优解是条件分位数——换损失函数就是换你在估计分布的哪个泛函

次梯度非常干净,反向传播只需要一行:

u  = y[:, None] - Q                     # Q: (T, 5) 五个分位数头
dQ = (np.where(u < 0, 1.0, 0.0) - TAUS[None, :]) / (T * len(TAUS))
python

2.2 网络结构:一个隐状态,五个输出头#

ht=tanh(Wxzt+Whht1+bh),q^t(τ)=Wo(τ)ht+bo(τ)h_t = \tanh(W_x z_t + W_h h_{t-1} + b_h), \qquad \hat{q}_t^{(\tau)} = W_o^{(\tau)} h_t + b_o^{(\tau)}

输入特征 zt=[rt,rt,rt2]z_t = [r_t, |r_t|, r_t^2](当期收益及其幅度),隐状态 16 维,输出 5 个头对应 τ ∈ {0.05, 0.25, 0.5, 0.75, 0.95}。关键设计:五个分位数共享同一个隐状态,只在最后一层分家——这不仅省参数,还大幅缓解分位数交叉(后面实测)。

训练用截断 BPTT:64 步一段,段内完整反传,段间只传状态不传梯度,配合梯度范数裁剪(阈值 5.0)防爆炸:

for s0 in range(0, T, 64):                 # 截断 BPTT
    # 前向:段内逐步递推,段首状态从上一段热启动
    ...
    dh_next = np.zeros(HID)
    for i in range(L - 1, -1, -1):         # 段内反传
        dh = dQ[i] @ Wo.T + dh_next
        da = dh * (1 - Hs[i] ** 2)         # tanh 导数
        gWx += np.outer(Xc[i], da); gWh += np.outer(hprev, da)
        dh_next = da @ Wh.T
    if grad_norm > 5.0: clip()             # 梯度裁剪
    h = hh                                  # 状态跨段传递,梯度截断
python

2.3 测试床:GARCH(1,1) + t(5)——已知正确答案的考场#

合成 4000 步收益:σt2=0.02+0.09rt12+0.89σt12\sigma_t^2 = 0.02 + 0.09\, r_{t-1}^2 + 0.89\, \sigma_{t-1}^2,新息为标准化 t(5)(重尾)。持续性 α+β=0.98,波动聚集强烈。前 3000 步训练、后 1000 步测试。

用合成数据的唯一理由:我们知道每一步的真实 σ_t,所以能构造 Oracle 基线(真实 σ_t × t(5) 理论分位数)——任何模型的 pinball 损失都不可能显著低于它,它是这场考试的满分线。

三、结果:逼近 Oracle,但没能甩开 EWMA#

样本外 1000 步,五个模型的成绩单:

模型pinball ↓90% 区间覆盖率区间宽度-真σ相关 ↑
Oracle(真σ + t 分位)0.26940.8881.000
EWMA-高斯(RiskMetrics λ=0.94)0.27170.8970.965
QRNN(联合训练+重排)0.27200.8850.861
滚动历史分位(250 日窗口)0.27940.8790.137
线性分位回归(同特征)0.29010.7220.521

分位数校准图与 pinball 损失对比

四个层次的解读:

第一,QRNN 真的学会了波动聚集。 区间宽度与真实 σ_t 相关 0.861——隐状态在没有任何 GARCH 先验的情况下,纯靠 pinball 损失 + 递推结构学出了「市场越疯、区间越宽」。对照组滚动历史分位的相关只有 0.137:250 日窗口把新旧波动一锅炖,区间几乎不随状态变化,高波动来临时反应慢半年。封面图里 QRNN 的蓝色扇形随黑点的密集程度收放,就是这 0.861 的直观形态。

第二,距离满分线只差 1%。 Oracle 0.2694 vs QRNN 0.2720。剩下的 1% 是隐状态对 σ_t 的估计误差 + 有限样本的分位数估计误差——16 维 tanh 递推逼近了乘性方差递推,这是结构上的胜利。

第三,诚实的败仗:EWMA 打平了 QRNN。 RiskMetrics 只有 λ=0.94 和一个正态假设,pinball 0.2717 还略优于 QRNN。原因不神秘:数据生成过程就是 GARCH,而 EWMA 是 IGARCH 的特例——考题恰好出自它的教材。它的软肋在分布形状:正态假设 + t(5) 重尾,让它的 5% 分位在校准图上系统性偏窄(尾部低估),只是 pinball 对中段权重大、把这笔账摊薄了。换一个尾部更极端或波动结构非 GARCH 的市场,两者会立刻拉开——但在你验证这一点之前,别默认神经网络赢。

第四,线性分位回归输得最惨(0.2901,覆盖率 72%)。 同样的特征 [rt,rt,rt2][r_t, |r_t|, r_t^2],没有隐状态就没有记忆——单步特征只能看到昨天,看不到「最近两周都在震荡」。QRNN 对线性 QR 的 6% 优势全部来自递推记忆,而不是非线性激活。

四、两个真实翻车#

翻车一:独立训练五个分位数模型 → 1.9% 的时间步分位数交叉#

第一直觉的架构是每个 τ 训一个独立模型(很多教程就这么写)。实测:1.9% 的测试时间步出现 q̂₀.₀₅ > q̂₀.₂₅ 之类的交叉——「95% 置信下界比 75% 下界还高」,这种输出给风控看会被当场退回。

而联合训练(共享隐状态+5 输出头)的交叉率是 0.0%。原因:五个头从同一个 h_t 线性读出,输出天然强相关,pinball 损失又各自把头压向正确的高度差,交叉在训练动力学上就不容易发生。工程守则:联合训练 + 推理时单调重排(rearrangement,直接对 5 个输出排序)双保险。重排在数学上还被证明不会变差(Chernozhukov et al. 2010)。

分位数交叉与高波动 regime 覆盖率

翻车二:高波动 regime 覆盖率掉队#

把测试集按真实 σ_t 三分位切开,统计 90% 区间的经验覆盖率:

模型低波动中波动高波动
QRNN0.9220.8650.868
EWMA-高斯0.9010.8860.904
滚动历史2500.9520.8920.793

QRNN 总体覆盖 88.5% 看着达标,拆开看是「低波动过覆盖 92.2% + 高波动欠覆盖 86.8%」的平均假象。滚动历史分位更惨:高波动时只剩 79.3%——每 5 天就有 1 天破区间,而它承诺的是每 10 天 1 次。总体覆盖率是最容易骗人的指标:模型完全可以靠平静期的过度保守,掩盖风暴期的系统性低估——而风控恰恰只在风暴期用得上你。 验收区间预测必须按波动状态分层。

五、五个陷阱#

陷阱一:「五条分位数=完整分布」。 5 个点只是分布的 5 个采样,两点之间的插值形状、95% 分位以外的极端尾部,模型一无所知。要 99% VaR 就得把 τ=0.01 放进训练目标,别拿 τ=0.05 外推。

陷阱二:「覆盖率 90% 就是校准好」。 见翻车二——总量达标可以由「平静期过宽 + 风暴期过窄」拼出来。至少按波动三分位分层验收,有条件再做 Christoffersen 条件覆盖检验(破区间是否独立发生,还是连环破)。

陷阱三:「深度模型必胜 EWMA」。 本文实测打平。EWMA/GARCH 是波动预测的超强基线:参数少、无过拟合、天生单调。QRNN 的增量价值在于(a)重尾/非对称分布形状不用假设正态,(b)能吃 |r|、成交量、隐含波动率等多特征。你的场景若两条都不占,2 个参数的模型没理由输给 2000 个参数的。

陷阱四:「分位数交叉是小概率,不用管」。 1.9% 听着小,但交叉集中出现在波动切换期——恰好是风控最依赖模型的时刻。联合训练 + 单调重排两道锁都要上,重排零成本且理论上只会更好。

陷阱五:「合成数据赢了=实盘能用」。 本文的考场是 GARCH-t:平稳、参数恒定、无跳空。真实市场有结构断裂(政策夜、熔断、财报跳空),隐状态在没见过的 regime 里外推没有任何保证。上实盘前至少要做滚动重训 + 样本外分层覆盖率监控,覆盖率连续偏离就降级回 EWMA。

六、结论#

  • pinball 损失 + RNN 隐状态 = 会呼吸的收益区间:区间宽度与真实波动相关 0.861,样本外 pinball 距离 Oracle 满分线仅差 1%,完胜没有记忆的滚动历史分位(相关 0.137)与线性分位回归。
  • 对 EWMA 打平是诚实的结果:在 GARCH 主场上,2 参数基线就是难以超越;QRNN 的价值要到重尾形状、多特征、非 GARCH 波动结构里去找。
  • 两条工程铁律:分位数联合训练 + 单调重排(独立训练实测 1.9% 交叉);覆盖率必须按波动 regime 分层验收(总体 88.5% 掩盖了高波动 86.8%)。

风险预测这条线到这里连成了串:QRF 给横截面分布、GARCH/EWMA 给波动递推、QRNN 把两者接起来——用递推状态驱动整条分位数曲线。下一步自然是把它接进仓位管理:区间宽度倒数做风险预算,但那是另一篇文章的实验了。

风险提示:本文全部实验基于合成数据,用于验证方法性质而非产生投资信号。实盘应用前需在真实收益序列上做滚动样本外验证与分层覆盖率监控。文中代码为教学实现,未考虑生产环境要求。

分位数 RNN 风险预测:用分位损失给出收益区间
https://blog.halo26812.eu.org/blog/qrnn-quantile-forecast
Author halo
Published at 2026年7月24日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨