halo 的技术博客

返回

波动率本身看不见。我们能观测到的是每天的收益率,而背后的「波动水平」是一个隐状态——它缓慢漂移、聚类、偶尔暴冲,却从不直接现身。随机波动率(Stochastic Volatility, SV)模型把这件事写成一个状态空间模型;而要「看见」这个隐状态,经典做法是 MCMC 全路径抽样。但 MCMC 有个尴尬:它每次迭代都要重抽整条长度为 TT 的路径,序列一长就慢得离谱。

本文给出另一条路:粒子滤波(Particle Filter / 序贯蒙特卡洛)。它不回头重抽整条路径,而是让一团粒子随时间「流」下去,在线、序贯、O(NT)O(NT) 地估计隐状态,并且顺手免费给出边际似然——这正是 MCMC 最不擅长、而参数估计和模型选择最需要的量。

一、随机波动率模型:隐状态为何看不见#

标准 SV 模型写成两行:

ht=μ+ϕ(ht1μ)+ηt,ηtN(0,ση2)h_t = \mu + \phi(h_{t-1}-\mu) + \eta_t,\qquad \eta_t\sim\mathcal N(0,\sigma_\eta^2)

rt=eht/2εt,εtN(0,1)r_t = e^{h_t/2}\,\varepsilon_t,\qquad \varepsilon_t\sim\mathcal N(0,1)

  • hth_t对数波动率,一个不可观测的隐状态,服从均值回复的 AR(1)。ϕ\phi 越接近 1,波动聚类越强。
  • rtr_t 是观测到的日收益。eht/2e^{h_t/2} 就是当天的波动尺度。

关键难点在于:给定 rtr_t,你无法直接反推出 hth_t。因为 rtr_t 里混着 εt\varepsilon_t 的纯噪声,单看一天的收益,它可能是「低波动上的大扰动」,也可能是「高波动上的小扰动」——二者观感相近。这正是隐状态估计的本质困难。

真实隐状态与一团实时追踪它的粒子

二、为什么 MCMC 在这里慢#

估计 SV 的黄金标准是 数据增强 Gibbs 采样:把隐路径 h1:Th_{1:T} 当辅助变量,交替抽样 h1:T数据,θh_{1:T}\mid \text{数据}, \thetaθh1:T\theta\mid h_{1:T}。问题是:

  1. 每次迭代都要重抽整条路径。对 T=800T=800 的日度数据,就是每次迭代在 800 维空间里走一步。而 hth_tht+kh_{t+k} 高度相关(因为 ϕ\phi 接近 1),这种强相关迫使 Gibbs 走很多很多步才能收敛。
  2. 不直接给边际似然。MCMC 给你的是 h1:Th_{1:T} 的后验样本,不是 p(数据θ)p(\text{数据}\mid\theta)。但做**参数估计(MLE / 贝叶斯边际)和模型比较(边际似然比)**恰恰需要这个量。要从 MCMC 里抠出边际似然,还得额外上桥式采样(bridge sampling)之类的技巧。
  3. 无法在线。MCMC 必须拿到全样本才能跑;来一个新观测,你得从头再跑一遍。

当序列变长(高频、多年数据)或你要扫很多组参数时,这三点是实打实的瓶颈。

三、粒子滤波:把后验变成一团会飞的粒子#

粒子滤波的思路完全不同。它不维护「整条路径的联合后验」,而是维护每一步的滤波后验 p(htr1:t)p(h_t\mid r_{1:t}),用 NN 个带权重的粒子近似:

预测(propagation):把上一刻的每个粒子按状态方程推一步 ht(i)N(μ+ϕ(ht1(i)μ),  ση2)h_t^{(i)} \sim \mathcal N\big(\mu+\phi(h_{t-1}^{(i)}-\mu),\;\sigma_\eta^2\big)

更新(weighting):用当天观测 rtr_t 给每个粒子打分 wt(i)p(rtht(i))w_t^{(i)} \propto p(r_t\mid h_t^{(i)}) 归一化后得到权重 Wt(i)W_t^{(i)}

重采样(resampling):当有效样本数 ESS=1/i(Wt(i))2\text{ESS}=1/\sum_i (W_t^{(i)})^2 跌到阈值(如 N/2N/2)以下,按权重重新抽 NN 个粒子,避免「少数粒子权重趋近 1、其余死亡」的退化。

SMC 边际似然(免费附赠):滤波过程中,每一步的权重和正好是当步的预测似然: logp(r1:T)t=1Tlog ⁣(1Ni=1Nwt(i))\log p(r_{1:T}) \approx \sum_{t=1}^T \log\!\left(\frac{1}{N}\sum_{i=1}^N w_t^{(i)}\right) 这一项不需要任何额外计算,却正是 MCMC 最难给的量。

四、Python:Bootstrap 粒子滤波(可直接运行)#

下面这段代码与生成图表的是同一套逻辑。注意观测似然这一坑:我们不用原始收益 rtN(0,eht)r_t\sim\mathcal N(0,e^{h_t})(它在 hh 上几乎不可追踪,详见第六节),而是用对数平方收益 zt=log(rt2)=ht+log(εt2)z_t=\log(r_t^2)=h_t+\log(\varepsilon_t^2),其噪声 log(χ12)\log(\chi^2_1) 用 Gaussian 近似(均值 1.2704-1.2704、方差 π2/2\pi^2/2),这是 SV 滤波的标准做法。

在这组参数(μ=9.0,  ϕ=0.97,  ση=0.60,  T=800,  N=2000\mu=-9.0,\;\phi=0.97,\;\sigma_\eta=0.60,\;T=800,\;N=2000)下,输出约为:

滤波 RMSE(h_t)=1.378, SMC 对数似然=-1961.89
plaintext

隐状态自身的波动 std(ht)2.50\text{std}(h_t)\approx 2.50,而滤波 RMSE 只有 1.381.38,相当于解释了约 70% 的隐状态波动——一团粒子真的把看不见的波动「追」了出来。

粒子滤波均值紧贴真值,90% 可信带覆盖波动起伏

五、诚实地说:RMSE 的天花板在哪#

上图里滤波均值紧贴真值,但 90% 可信带并不窄。这不是粒子不够,而是数据里就这么多信息:每一天只有一个收益 rtr_tzt=ht+log(εt2)z_t=h_t+\log(\varepsilon_t^2) 里那个噪声 log(χ12)\log(\chi^2_1) 的标准差高达约 2.22.2——比隐状态自身的波动还大。所以滤波对 hth_t逐期点估计天然带误差,再多粒子也「造」不出数据里没有的信息。

这恰恰是粒子滤波相对 MCMC 的诚实定位:它给的是滤波/平滑后验(每一步的分布),不是无偏的逐点真值。如果你要的是「每一步 hth_t 的精确数值」,请记住这个噪声地板;如果你要的是「隐状态的低频走向 + 一个可用于参数估计的边际似然」,粒子滤波已经够用且快得多。

六、Bootstrap 的退化与 APF 的解药#

朴素 Bootstrap 滤波有个老毛病:退化(degeneracy)。权重逐期被观测刷新,几步之后往往只有少数粒子持有显著权重,其余「死掉」,多样性崩溃。我们用 ESS 监控它,ESS 越低说明退化越严重。

辅助粒子滤波(Auxiliary Particle Filter, APF) 的解法是:在预测之前,先用「预测均值处的似然」给粒子做一个一阶段预筛选,优先保留那些「下一步更可能被观测支持」的祖先,再传播、再用修正权重收尾。结果 ESS 显著更高:

Bootstrap 与 APF 的有效样本数对比

在同样 N=2000N=2000 下,Bootstrap 的平均 ESS1483\text{ESS}\approx 1483,APF 提升到约 17601760——退化更慢、重采样更少,滤波也更稳。实盘或长序列里,APF 几乎是默认选项。

七、粒子数越多越好吗?看收敛#

把粒子数 NN 从 128 扫到 4096,看两件事:

随粒子数 N 的收敛:对数似然收敛、RMSE 触顶

  • SMC 对数似然NN 稳步收敛到稳定值——这才是粒子滤波真正的「收敛」对象,也是你拿去做参数 MLE 的量。务必用足够大的 NN 估计边际似然,否则会系统性偏掉。
  • 滤波 RMSE 很快就触顶(约 1.361.381.36\sim1.38)不再下降。这不是 bug,而是第五节说的观测噪声地板:更多粒子降低蒙特卡洛方差,但无法突破单日收益带来的信息上限。

一句话:NN 决定边际似然的精度,不决定隐状态估计的信息上限。

八、真实陷阱(别把「序贯」当「万能」)#

1. 观测似然的坑(最致命)。 直接对 rtN(0,eht)r_t\sim\mathcal N(0,e^{h_t}) 做滤波,单观测落在重尾 χ12\chi^2_1 上,滤波器几乎不追踪隐状态(RMSE 会等于信号自身的波动,等于没滤波)。必须改成对数平方收益 zt=log(rt2)z_t=\log(r_t^2),并用 log(χ12)\log(\chi^2_1) 的似然(精确或 Gaussian 近似)。

2. 水平偏差。 精确 log(χ12)\log(\chi^2_1) 的众数在 00、均值在 1.27-1.27。若用精确似然、又叠一个有信息量的先验,滤波均值会被先验往上拉、产生系统性水平偏差。用其 Gaussian 近似(均值 1.2704-1.2704、方差 π2/2\pi^2/2)可得到无偏结果——本文即用此法。

3. 退化与重采样的两难。 ESS 太低不重采样 → 多样性崩溃;重采样太频繁 → 引入蒙特卡洛方差、丢掉低权重但未来可能重要的粒子。APF、自适应重采样(如「系统重采样 + ESS 阈值」)是标准解药。

4. SMC 似然是估计量,不是真值。 NN 不够时它会高估或低估边际似然;做参数估计时 NN 要大到似然收敛(见第七节)。

5. 粒子滤波给滤波/平滑,不给全路径后验。 想要整条路径的联合后验样本(像 MCMC 那样做全路径推断、算路径依赖的量),需要 粒子平滑(particle smoother,如前向滤波-反向平滑 FBS),普通滤波只给每一步的边缘后验。

九、小结:何时用粒子滤波,何时用 MCMC#

需求选粒子滤波选 MCMC
在线 / 实时估计隐状态✅ 序贯、来一个处理一个❌ 必须全样本重跑
长序列(高频、多年)O(NT)O(NT) 线性扩展❌ 全路径联合,慢
要边际似然做参数 MLE / 模型选择✅ SMC 似然免费给出❌ 需额外 bridge sampling
要整条路径的联合后验❌ 需 particle smoother✅ 天然给出
离线、小样本、要完整诊断✅ 更稳更标准
  • SV 的隐状态看不见,但粒子滤波用一团会飞的粒子把它在线追了出来:本文 N=2000N=2000 下滤波 RMSE 1.381.38,解释约 70% 的隐状态波动;
  • 顺手给出 SMC 边际似然,这是 MCMC 最难给、参数估计最需要的量;
  • APF 把平均 ESS 从 1483 提到 1760,退化更慢;
  • 但记住三件事:观测似然要用对数平方收益 + log(χ12)\log(\chi^2_1)(否则几乎不追踪)、RMSE 有观测噪声地板(多粒子救不了)、要全路径后验得上 particle smoother

附:本文所有图表与数值均来自上方可运行代码(SV 模拟 + 对数平方观测 + Gaussian-log(χ12)\log(\chi^2_1) 似然 + Bootstrap/APF 滤波),参数与结果一致,可直接复现。

粒子滤波估计随机波动率:当 MCMC 太慢时的序贯解法
https://blog.halo26812.eu.org/blog/particle-filter-sv
Author halo
Published at 2026年7月12日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨