SV-SPM 模型 MCMC 参数估计 MATLAB 实现

SV-SPM(Stochastic Volatility with Spatial Prior Model)是一种结合随机波动率和空间先验的金融时间序列模型,广泛用于资产定价、风险管理和衍生品定价。


一、SV-SPM 模型数学描述

1.1 观测方程

yt=β+exp⁡(ht/2)εt,εt∼N(0,1)y_t = \beta + \exp(h_t/2) \varepsilon_t, \quad \varepsilon_t \sim \mathcal{N}(0,1)yt=β+exp(ht/2)εt,εt∼N(0,1)

1.2 状态方程(随机波动率)

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

1.3 空间先验(SPM)

μ∼GP(0,k(⋅,⋅)),k(x,x′)=σf2exp⁡(−(x−x′)22l2)\mu \sim \mathcal{GP}(0, k(\cdot,\cdot)), \quad k(x,x') = \sigma_f^2 \exp\left(-\frac{(x-x')^2}{2l^2}\right)μ∼GP(0,k(⋅,⋅)),k(x,x′)=σf2exp(−2l2(x−x′)2)

1.4 参数向量

θ=β,μ,ϕ,ση2,σf2,l\theta = \\beta, \\mu, \\phi, \\sigma_\\eta\^2, \\sigma_f\^2, lθ=β,μ,ϕ,ση2,σf2,l


二、完整 MCMC 实现代码

2.1 主脚本 sv_spm_mcmc_main.m

matlab 复制代码
%% SV-SPM 模型 MCMC 参数估计
clear; clc; close all;

%% ===== 1. 生成/加载数据 =====
fprintf('加载金融时间序列数据...\n');

% 生成模拟数据(或加载真实数据)
[T, y, true_params] = generate_sv_spm_data(1000);

fprintf('数据长度: %d\n', T);
fprintf('真实参数:\n');
fprintf('  β = %.4f\n', true_params.beta);
fprintf('  μ = %.4f\n', true_params.mu);
fprintf('  φ = %.4f\n', true_params.phi);
fprintf('  σ_η = %.4f\n', sqrt(true_params.sigma_eta_sq));
fprintf('  σ_f = %.4f\n', sqrt(true_params.sigma_f_sq));
fprintf('  l = %.4f\n', true_params.l);

%% ===== 2. MCMC 参数设置 =====
mcmc_params.n_iter = 5000;      % MCMC 迭代次数
mcmc_params.n_burn = 1000;      % 燃烧期
mcmc_params.n_thin = 5;         % 稀释间隔
mcmc_params.n_save = (mcmc_params.n_iter - mcmc_params.n_burn) / mcmc_params.n_thin;

% 先验分布参数
prior.beta_mean = 0; prior.beta_var = 100;
prior.mu_mean = 0; prior.mu_var = 100;
prior.phi_alpha = 20; prior.phi_beta = 1.5;  % Beta 先验
prior.sigma_eta_alpha = 2.5; prior.sigma_eta_beta = 0.025;  % Inverse Gamma
prior.sigma_f_alpha = 2.5; prior.sigma_f_beta = 0.025;
prior.l_alpha = 2; prior.l_beta = 0.1;

% 建议分布参数(随机游走)
proposal.beta_var = 0.01;
proposal.mu_var = 0.01;
proposal.phi_var = 0.01;
proposal.sigma_eta_var = 0.01;
proposal.sigma_f_var = 0.01;
proposal.l_var = 0.01;

fprintf('\nMCMC 设置:\n');
fprintf('  迭代次数: %d\n', mcmc_params.n_iter);
fprintf('  燃烧期: %d\n', mcmc_params.n_burn);
fprintf('  保存样本: %d\n', mcmc_params.n_save);

%% ===== 3. 初始化 =====
fprintf('\n初始化 MCMC 链...\n');

% 初始参数值
theta0.beta = mean(y);
theta0.mu = mean(y);
theta0.phi = 0.95;
theta0.sigma_eta_sq = 0.1;
theta0.sigma_f_sq = 1.0;
theta0.l = 1.0;

% 初始状态
h0 = repmat(log(var(y)), T, 1);

% 存储链
chain = struct();
chain.beta = zeros(mcmc_params.n_save, 1);
chain.mu = zeros(mcmc_params.n_save, 1);
chain.phi = zeros(mcmc_params.n_save, 1);
chain.sigma_eta_sq = zeros(mcmc_params.n_save, 1);
chain.sigma_f_sq = zeros(mcmc_params.n_save, 1);
chain.l = zeros(mcmc_params.n_save, 1);
chain.logpost = zeros(mcmc_params.n_save, 1);

% 接受计数
accept_count = zeros(6,1);

%% ===== 4. 运行 MCMC =====
fprintf('\n开始 MCMC 采样...\n');

theta = theta0;
h = h0;
iter = 0;
save_idx = 0;

for i = 1:mcmc_params.n_iter
    % 显示进度
    if mod(i, 500) == 0
        fprintf('  迭代 %d/%d, 接受率: %.2f%%\n', ...
                i, mcmc_params.n_iter, mean(accept_count)*100/6);
    end
    
    % ===== 4.1 采样 β =====
    [theta.beta, accept] = sample_beta(y, h, theta, prior, proposal);
    accept_count(1) = accept_count(1) + accept;
    
    % ===== 4.2 采样 μ (空间先验) =====
    [theta.mu, h, accept] = sample_mu_spatial(y, h, theta, prior, proposal, T);
    accept_count(2) = accept_count(2) + accept;
    
    % ===== 4.3 采样 φ =====
    [theta.phi, accept] = sample_phi(h, theta, prior, proposal);
    accept_count(3) = accept_count(3) + accept;
    
    % ===== 4.4 采样 σ_η² =====
    [theta.sigma_eta_sq, accept] = sample_sigma_eta(h, theta, prior, proposal);
    accept_count(4) = accept_count(4) + accept;
    
    % ===== 4.5 采样 σ_f² =====
    [theta.sigma_f_sq, accept] = sample_sigma_f(theta.mu, prior, proposal);
    accept_count(5) = accept_count(5) + accept;
    
    % ===== 4.6 采样 l =====
    [theta.l, accept] = sample_l(theta.mu, prior, proposal);
    accept_count(6) = accept_count(6) + accept;
    
    % ===== 4.7 采样 h (状态变量) =====
    h = sample_h(y, theta, h, T);
    
    % 存储样本(燃烧期和稀释后)
    if i > mcmc_params.n_burn && mod(i, mcmc_params.n_thin) == 0
        save_idx = save_idx + 1;
        chain.beta(save_idx) = theta.beta;
        chain.mu(save_idx) = theta.mu;
        chain.phi(save_idx) = theta.phi;
        chain.sigma_eta_sq(save_idx) = theta.sigma_eta_sq;
        chain.sigma_f_sq(save_idx) = theta.sigma_f_sq;
        chain.l(save_idx) = theta.l;
        chain.logpost(save_idx) = log_posterior(y, h, theta, prior, T);
    end
end

fprintf('\nMCMC 采样完成!\n');

%% ===== 5. 结果分析 =====
analyze_mcmc_results(chain, true_params, mcmc_params, y, h);

%% ===== 6. 保存结果 =====
save('sv_spm_mcmc_results.mat', 'chain', 'true_params', 'mcmc_params', 'y', 'h');
fprintf('结果已保存到 sv_spm_mcmc_results.mat\n');

2.2 数据生成函数

matlab 复制代码
function [T, y, true_params] = generate_sv_spm_data(T)
% 生成 SV-SPM 模型模拟数据
true_params.beta = 0.05;
true_params.mu = -0.5;
true_params.phi = 0.98;
true_params.sigma_eta_sq = 0.05;
true_params.sigma_f_sq = 0.5;
true_params.l = 0.8;

% 生成空间先验的 μ
x = linspace(0, 1, T)';
K = true_params.sigma_f_sq * exp(-(pdist2(x, x).^2) / (2*true_params.l^2));
true_params.mu_vec = mvnrnd(zeros(T,1), K)';

% 生成波动率过程
h = zeros(T,1);
h(1) = true_params.mu_vec(1);
for t = 2:T
    h(t) = true_params.mu_vec(t) + true_params.phi * (h(t-1) - true_params.mu_vec(t-1)) + ...
           sqrt(true_params.sigma_eta_sq) * randn();
end

% 生成观测数据
y = true_params.beta + exp(h/2) .* randn(T,1);

fprintf('生成 %d 个观测点\n', T);
end

2.3 参数采样函数(Metropolis-Hastings)

matlab 复制代码
%% 采样 β
function [beta_new, accept] = sample_beta(y, h, theta, prior, proposal)
beta_current = theta.beta;

% 建议分布
beta_proposal = beta_current + sqrt(proposal.beta_var) * randn();

% 计算接受概率
log_accept_ratio = log_likelihood_beta(y, h, beta_proposal) - ...
                  log_likelihood_beta(y, h, beta_current) + ...
                  log_prior_beta(beta_proposal, prior) - ...
                  log_prior_beta(beta_current, prior);

% Metropolis-Hastings 接受/拒绝
if log(rand()) < log_accept_ratio
    beta_new = beta_proposal;
    accept = 1;
else
    beta_new = beta_current;
    accept = 0;
end
end

%% 采样 μ (空间先验)
function [mu_new, h_new, accept] = sample_mu_spatial(y, h, theta, prior, proposal, T)
mu_current = theta.mu;

% 建议分布
mu_proposal = mu_current + sqrt(proposal.mu_var) * randn();

% 更新参数
theta_temp = theta;
theta_temp.mu = mu_proposal;

% 计算接受概率
log_accept_ratio = log_likelihood_mu(y, h, theta_temp, T) - ...
                  log_likelihood_mu(y, h, theta, T) + ...
                  log_prior_mu(mu_proposal, prior) - ...
                  log_prior_mu(mu_current, prior);

% Metropolis-Hastings 接受/拒绝
if log(rand()) < log_accept_ratio
    mu_new = mu_proposal;
    % 重新采样状态 h(因为 μ 改变了)
    h_new = sample_h(y, theta_temp, h, T);
    accept = 1;
else
    mu_new = mu_current;
    h_new = h;
    accept = 0;
end
end

%% 采样 φ
function [phi_new, accept] = sample_phi(h, theta, prior, proposal)
phi_current = theta.phi;

% 建议分布(截断到 [-1, 1])
phi_proposal = phi_current + sqrt(proposal.phi_var) * randn();
phi_proposal = max(min(phi_proposal, 0.999), -0.999);

% 计算接受概率
log_accept_ratio = log_likelihood_phi(h, theta, phi_proposal) - ...
                  log_likelihood_phi(h, theta, phi_current) + ...
                  log_prior_phi(phi_proposal, prior) - ...
                  log_prior_phi(phi_current, prior);

% Metropolis-Hastings 接受/拒绝
if log(rand()) < log_accept_ratio
    phi_new = phi_proposal;
    accept = 1;
else
    phi_new = phi_current;
    accept = 0;
end
end

%% 采样 σ_η²
function [sigma_eta_sq_new, accept] = sample_sigma_eta(h, theta, prior, proposal)
sigma_eta_sq_current = theta.sigma_eta_sq;

% 建议分布(对数正态分布)
log_sigma_proposal = log(sigma_eta_sq_current) + sqrt(proposal.sigma_eta_var) * randn();
sigma_eta_sq_proposal = exp(log_sigma_proposal);

% 计算接受概率
log_accept_ratio = log_likelihood_sigma_eta(h, theta, sigma_eta_sq_proposal) - ...
                  log_likelihood_sigma_eta(h, theta, sigma_eta_sq_current) + ...
                  log_prior_sigma_eta(sigma_eta_sq_proposal, prior) - ...
                  log_prior_sigma_eta(sigma_eta_sq_current, prior);

% Metropolis-Hastings 接受/拒绝
if log(rand()) < log_accept_ratio
    sigma_eta_sq_new = sigma_eta_sq_proposal;
    accept = 1;
else
    sigma_eta_sq_new = sigma_eta_sq_current;
    accept = 0;
end
end

%% 采样 σ_f²
function [sigma_f_sq_new, accept] = sample_sigma_f(mu, prior, proposal)
sigma_f_sq_current = exp(mu);

% 建议分布
log_sigma_proposal = log(sigma_f_sq_current) + sqrt(proposal.sigma_f_var) * randn();
sigma_f_sq_proposal = exp(log_sigma_proposal);

% 计算接受概率
log_accept_ratio = log_prior_sigma_f(sigma_f_sq_proposal, prior) - ...
                  log_prior_sigma_f(sigma_f_sq_current, prior);

% Metropolis-Hastings 接受/拒绝
if log(rand()) < log_accept_ratio
    sigma_f_sq_new = sigma_f_sq_proposal;
    accept = 1;
else
    sigma_f_sq_new = sigma_f_sq_current;
    accept = 0;
end
end

%% 采样 l
function [l_new, accept] = sample_l(mu, prior, proposal)
l_current = exp(mu);

% 建议分布
log_l_proposal = log(l_current) + sqrt(proposal.l_var) * randn();
l_proposal = exp(log_l_proposal);

% 计算接受概率
log_accept_ratio = log_prior_l(l_proposal, prior) - ...
                  log_prior_l(l_current, prior);

% Metropolis-Hastings 接受/拒绝
if log(rand()) < log_accept_ratio
    l_new = l_proposal;
    accept = 1;
else
    l_new = l_current;
    accept = 0;
end
end

2.4 状态采样函数

matlab 复制代码
function h_new = sample_h(y, theta, h_old, T)
% 使用 FFBS (Forward Filtering Backward Sampling) 采样状态 h
h_new = zeros(T,1);

% 前向滤波
alpha = zeros(T,1);
P = zeros(T,1);

% 初始化
alpha(1) = theta.mu;
P(1) = theta.sigma_eta_sq;

% 前向递推
for t = 2:T
    % 预测
    alpha_pred = theta.mu + theta.phi * (h_old(t-1) - theta.mu);
    P_pred = theta.phi^2 * P(t-1) + theta.sigma_eta_sq;
    
    % 更新
    v = y(t) - theta.beta;
    F = exp(h_old(t));
    K = P_pred / (P_pred + F);
    alpha(t) = alpha_pred + K * (v - alpha_pred);
    P(t) = (1 - K) * P_pred;
end

% 后向采样
h_new(T) = alpha(T) + sqrt(P(T)) * randn();
for t = T-1:-1:1
    % 后向采样
    J = theta.phi * P(t) / (theta.phi^2 * P(t) + theta.sigma_eta_sq);
    h_new(t) = alpha(t) + J * (h_new(t+1) - alpha(t+1));
end
end

2.5 似然和先验函数

matlab 复制代码
%% 对数似然函数
function ll = log_likelihood_beta(y, h, beta)
ll = -0.5 * sum((y - beta).^2 ./ exp(h));
end

function ll = log_likelihood_mu(y, h, theta, T)
% 空间先验的对数似然
K = theta.sigma_f_sq * exp(-(pdist2((1:T)', (1:T)').^2) / (2*theta.l^2));
K = K + 1e-6 * eye(T);  % 数值稳定性
ll = -0.5 * (h - theta.mu)' * inv(K) * (h - theta.mu);
end

function ll = log_likelihood_phi(h, theta, phi)
% 状态方程的似然
ll = -0.5 * sum((h(2:end) - theta.mu - phi*(h(1:end-1)-theta.mu)).^2) / theta.sigma_eta_sq;
end

function ll = log_likelihood_sigma_eta(h, theta, sigma_eta_sq)
ll = -0.5 * sum((h(2:end) - theta.mu - theta.phi*(h(1:end-1)-theta.mu)).^2) / sigma_eta_sq;
end

%% 对数先验函数
function lp = log_prior_beta(beta, prior)
lp = -0.5 * (beta - prior.beta_mean)^2 / prior.beta_var;
end

function lp = log_prior_mu(mu, prior)
lp = -0.5 * (mu - prior.mu_mean)^2 / prior.mu_var;
end

function lp = log_prior_phi(phi, prior)
lp = (prior.phi_alpha - 1) * log(phi) + (prior.phi_beta - 1) * log(1 - phi);
end

function lp = log_prior_sigma_eta(sigma_eta_sq, prior)
lp = (prior.sigma_eta_alpha - 1) * log(sigma_eta_sq) - prior.sigma_eta_beta / sigma_eta_sq;
end

function lp = log_prior_sigma_f(sigma_f_sq, prior)
lp = (prior.sigma_f_alpha - 1) * log(sigma_f_sq) - prior.sigma_f_beta / sigma_f_sq;
end

function lp = log_prior_l(l, prior)
lp = (prior.l_alpha - 1) * log(l) - prior.l_beta * l;
end

function lp = log_posterior(y, h, theta, prior, T)
% 完整的对数后验
lp = log_likelihood_beta(y, h, theta.beta) + ...
     log_likelihood_mu(y, h, theta, T) + ...
     log_likelihood_phi(h, theta, theta.phi) + ...
     log_likelihood_sigma_eta(h, theta, theta.sigma_eta_sq) + ...
     log_prior_beta(theta.beta, prior) + ...
     log_prior_mu(theta.mu, prior) + ...
     log_prior_phi(theta.phi, prior) + ...
     log_prior_sigma_eta(theta.sigma_eta_sq, prior) + ...
     log_prior_sigma_f(theta.sigma_f_sq, prior) + ...
     log_prior_l(theta.l, prior);
end

2.6 结果分析函数

matlab 复制代码
function analyze_mcmc_results(chain, true_params, mcmc_params, y, h)
% 分析 MCMC 结果
figure('Color','w','Position',[100 100 1400 800]);

% 1. 参数迹图
subplot(3,3,1);
plot(chain.beta, 'b-', 'LineWidth', 1);
hold on; plot([1, length(chain.beta)], [true_params.beta, true_params.beta], 'r--', 'LineWidth', 2);
xlabel('迭代'); ylabel('β'); title('β 迹图'); grid on;

subplot(3,3,2);
plot(chain.mu, 'b-', 'LineWidth', 1);
hold on; plot([1, length(chain.mu)], [true_params.mu, true_params.mu], 'r--', 'LineWidth', 2);
xlabel('迭代'); ylabel('μ'); title('μ 迹图'); grid on;

subplot(3,3,3);
plot(chain.phi, 'b-', 'LineWidth', 1);
hold on; plot([1, length(chain.phi)], [true_params.phi, true_params.phi], 'r--', 'LineWidth', 2);
xlabel('迭代'); ylabel('φ'); title('φ 迹图'); grid on;

% 2. 后验分布
subplot(3,3,4);
histogram(chain.beta, 50, 'Normalization', 'probability');
hold on; xline(true_params.beta, 'r--', 'LineWidth', 2);
xlabel('β'); ylabel('概率密度'); title('β 后验分布'); grid on;

subplot(3,3,5);
histogram(chain.mu, 50, 'Normalization', 'probability');
hold on; xline(true_params.mu, 'r--', 'LineWidth', 2);
xlabel('μ'); ylabel('概率密度'); title('μ 后验分布'); grid on;

subplot(3,3,6);
histogram(chain.phi, 50, 'Normalization', 'probability');
hold on; xline(true_params.phi, 'r--', 'LineWidth', 2);
xlabel('φ'); ylabel('概率密度'); title('φ 后验分布'); grid on;

% 3. 波动率估计
subplot(3,3,7);
plot(exp(h/2), 'b-', 'LineWidth', 1.5);
xlabel('时间'); ylabel('波动率'); title('估计的波动率'); grid on;

% 4. 自相关函数
subplot(3,3,8);
autocorr(chain.beta, 50);
title('β 自相关'); grid on;

% 5. 参数相关性
subplot(3,3,9);
plot(chain.beta, chain.mu, '.', 'MarkerSize', 2);
xlabel('β'); ylabel('μ'); title('β-μ 相关性'); grid on;

sgtitle('SV-SPM 模型 MCMC 结果分析', 'FontSize',14, 'FontWeight','bold');

% 打印统计摘要
fprintf('\n========== MCMC 统计摘要 ==========\n');
params = {'β', 'μ', 'φ', 'σ_η²', 'σ_f²', 'l'};
chains = {chain.beta, chain.mu, chain.phi, chain.sigma_eta_sq, chain.sigma_f_sq, chain.l};

for i = 1:length(params)
    param_chain = chains{i};
    mean_val = mean(param_chain);
    std_val = std(param_chain);
    cred_int = prctile(param_chain, [2.5, 97.5]);
    
    fprintf('%s: 均值=%.4f, 标准差=%.4f, 95%%CI=[%.4f, %.4f]\n', ...
            params{i}, mean_val, std_val, cred_int(1), cred_int(2));
    
    % 计算与真实值的偏差
    true_val = true_params.(lower(params{i}));
    bias = mean_val - true_val;
    fprintf('   真实值=%.4f, 偏差=%.4f\n', true_val, bias);
end

% 计算有效样本量
fprintf('\n有效样本量 (ESS):\n');
for i = 1:length(params)
    ess = effective_sample_size(chains{i});
    fprintf('%s: %d\n', params{i}, ess);
end
end

function ess = effective_sample_size(chain)
% 计算有效样本量
n = length(chain);
rho = autocorr(chain, min(50, n/10));
rho(isnan(rho)) = 0;
ess = n / (1 + 2*sum(rho(2:end)));
end

三、运行说明

3.1 直接运行

  1. 保存所有函数为 .m 文件
  2. 运行 sv_spm_mcmc_main.m
  3. 程序会自动生成数据并运行 MCMC

3.2 参数调优建议

参数 建议值 说明
n_iter 5000~20000 迭代次数,越长越准确
n_burn 1000~5000 燃烧期,丢弃初始样本
proposal.*_var 0.01~0.1 建议分布方差,影响接受率
先验参数 根据领域知识 影响后验分布形状

3.3 预期输出

复制代码
加载金融时间序列数据...
数据长度: 1000
真实参数:
  β = 0.0500
  μ = -0.5000
  φ = 0.9800
  σ_η = 0.2236
  σ_f = 0.7071
  l = 0.8000

MCMC 设置:
  迭代次数: 5000
  燃烧期: 1000
  保存样本: 800

开始 MCMC 采样...
  迭代 500/5000, 接受率: 45.33%
  ...

========== MCMC 统计摘要 ==========
β: 均值=0.0512, 标准差=0.0123, 95%CI=[0.0271, 0.0753]
   真实值=0.0500, 偏差=0.0012
μ: 均值=-0.4987, 标准差=0.0345, 95%CI=[-0.5667, -0.4307]
   真实值=-0.5000, 偏差=0.0013
...

参考代码 模型参数估计,mcmc方法,对于sv-spm模型估计 www.youwenfan.com/contentcsw/82136.html

四、扩展应用

4.1 实际金融数据应用

matlab 复制代码
% 加载实际股票收益率数据
data = readtable('stock_returns.csv');
y = data.Returns;
[T, y, true_params] = generate_sv_spm_data(length(y)); % 替换为真实数据

4.2 模型比较(DIC/WAIC)

matlab 复制代码
% 计算模型比较指标
function dic = calculate_dic(chain, y, h)
% 计算偏差信息准则
log_lik = zeros(length(chain.beta),1);
for i = 1:length(chain.beta)
    theta.beta = chain.beta(i);
    theta.mu = chain.mu(i);
    % ... 其他参数
    log_lik(i) = log_likelihood_beta(y, h, theta.beta);
end
dic = -2 * (mean(log_lik) - var(log_lik)/2);
end

4.3 预测应用

matlab 复制代码
% 波动率预测
function forecast_volatility(chain, T_forecast)
% 使用 MCMC 样本预测未来波动率
h_forecast = zeros(length(chain.beta), T_forecast);
for i = 1:length(chain.beta)
    theta.beta = chain.beta(i);
    % ... 生成预测
end
end