halo 的技术博客

返回

一句话结论:相关系数衡量的是「平均」的同涨同跌,而 Copula 衡量的是「联合分布」的同涨同跌——尤其是崩盘时一起跳水的尾部联动。 本文用 Python 实跑发现:在 ρ=0.7 下,高斯 Copula 的上尾相依系数恒为 0(危机里它假装两资产互不相关),而自由度 ν=4 的 t-Copula 上尾相依系数高达 0.39——这正是 2008 年式「什么都一起跌」的数学根源。若你的风险模型还只用相关系数,那你在最该警惕的时刻是瞎的。

一、Sklar 定理:把「边际」和「相依」拆开#

多元分布有个麻烦:两个资产各自的收益分布(边际)好建模,但它们「怎么一起动」的联合结构极难直接写。Sklar 定理给我们一把手术刀:

F(x1,x2)=C(F1(x1),F2(x2))F(x_1, x_2) = C\big(F_1(x_1),\, F_2(x_2)\big)

其中 F1,F2F_1, F_2 是各自的边际 CDF,CC 就是 Copula——它只负责描述「相依结构」,且定义域被压进单位超立方体 [0,1]2[0,1]^2。关键红利:边际怎么扭曲、怎么重尾,都不影响 Copula 本身。你可以给两只股票各配一个 Student-t 边际(重尾),再用一个 Copula 把它们的联动装进去,三者解耦。

这就是为什么相关系数会失灵:皮尔逊相关只描述了「线性」联合结构的某一个截面,一旦进入极端尾部,真实的联动方式(由 Copula 决定)可能和它完全两样。

二、伪观测:把任意收益变成 [0,1] 上的均匀量#

要拟合 Copula,第一步是把原始收益「洗」成 [0,1] 上的伪观测(概率积分变换)。最稳健的做法是用经验 CDF(即排序秩),不假设边际的具体形式:

import numpy as np
from scipy import stats

def pseudo_observations(X):
    """X: shape (n, d) 的原始收益矩阵;返回 [0,1] 上的伪观测 U。"""
    # 双重 argsort = 秩(从 1 开始),再除以 n+1 避免取到 0/1 边界
    ranks = X.argsort(axis=0).argsort(axis=0) + 1
    return ranks / (X.shape[0] + 1)

# 例:两支股指日收益
U = pseudo_observations(returns)   # shape (n, 2),每列都在 (0,1)
python

经验 CDF 的好处是不会被边际的重尾带偏——无论原始收益多胖尾,伪观测都服帖地躺在 [0,1] 里,剩下的相依结构就干净地暴露出来。

三、高斯 Copula:用相关矩阵装相依#

高斯 Copula 的做法很直白:生成相关的标准正态,再经标准正态 CDF 压回 [0,1]:

def sample_gaussian_copula(rho, n, seed=0):
    rng = np.random.default_rng(seed)
    Z = rng.standard_normal((n, 2))
    cov = np.array([[1.0, rho], [rho, 1.0]])
    L = np.linalg.cholesky(cov)          # 注入相关结构
    X = Z @ L.T
    return stats.norm.cdf(X)             # 概率积分变换 -> [0,1]
python

相关系数 ρ 直接从伪观测的秩相关推出(秩相关对单调变换不变):

def spearman_to_pearson(rho_s):
    return 2.0 * np.sin(np.pi * rho_s / 6.0)

rho_s = stats.spearmanr(U[:, 0], U[:, 1]).correlation
rho = spearman_to_pearson(rho_s)         # 用于高斯 Copula 的相关参数
python

高斯 Copula(左)与 t-Copula(右)的散点对比:前者四角均匀,后者在左下/右上明显抱团

左边是高斯 Copula:椭圆分布,四个角干干净净——它假装极端行情里两资产互不相干。右边是 t-Copula(ν=4):下尾(左下,双双暴跌)和上尾(右上,双双暴涨)明显抱团。这一抱团,就是危机里「什么都一起跌」的样子。

四、t-Copula:多一个自由度 ν,尾部就活了#

高斯 Copula 的致命缺陷是尾部渐近独立——无论中间 ρ 多大,极端同时发生的概率趋于 0。t-Copula 通过自由度 ν 修正了这一点:ν 越小,尾部越「黏」。

def sample_t_copula(rho, nu, n, seed=1):
    rng = np.random.default_rng(seed)
    Z = rng.standard_normal((n, 2))
    cov = np.array([[1.0, rho], [rho, 1.0]])
    L = np.linalg.cholesky(cov)
    X = Z @ L.T
    S = rng.chisquare(nu, n) / nu             # 自由度 ν 的卡方 / ν
    T = X / np.sqrt(S)[:, None]               # 多元 t
    return stats.t.cdf(T, nu)                 # 分量-wise Student-t CDF -> [0,1]
python

ν 怎么估?给定 ρ(由秩相关推得),对 ν 做剖面极大似然即可:

from scipy.stats import multivariate_t
from scipy import optimize

def fit_t_copula_nu(U, rho, nu0=5.0):
    cov = np.array([[1.0, rho], [rho, 1.0]])
    def neg_loglik(nu):
        if nu <= 2:
            return 1e10
        z = stats.t.ppf(U, nu)                # 伪观测反变换成 t 分位
        rv = multivariate_t(loc=np.zeros(2), shape=cov, df=nu)
        return -np.sum(rv.logpdf(z))
    res = optimize.minimize(neg_loglik, nu0, bounds=[(2.01, 100.0)])
    return res.x[0]
python

ν 一旦估出来偏小(比如 < 10),就等于数据亲自告诉你:「我的尾部比高斯假设黏得多,别用相关系数糊弄自己。」

五、尾部相依系数:高斯=0,t>0——这才是关键差别#

上尾相依系数定义为:当资产 A 已经极端上涨时,资产 B 也极端上涨的概率极限,

λU=limu1P(U2>uU1>u)\lambda_U = \lim_{u\to 1^-} P\big(U_2 > u \,\big|\, U_1 > u\big)

经验估计只需数一数「两个都 > u」的占比除以「U₁ > u」的占比:

def emp_tail_dependence(U, u_grid):
    lam = []
    for u in u_grid:
        cond = U[:, 0] > u
        denom = cond.sum()
        lam.append(((U[:, 1] > u) & cond).sum() / denom if denom else np.nan)
    return np.array(lam)

# t-Copula 的解析上尾相依(闭式):
def t_copula_tail_dependence(rho, nu):
    z = -np.sqrt((nu + 1) * (1 - rho) / (1 + rho))
    return 2.0 * stats.t.cdf(z, nu + 1)
python

尾部相依系数曲线:阈值 u 越高,t-Copula 下两资产同涨同跌的概率显著为正,高斯 Copula 却贴着 0

在我们的设定(ρ=0.7)下,解析结果冷冰冰:高斯 Copula 的 λ=0.000,t-Copula(ν=4)的 λ=0.391。也就是说,在 1% 的极端行情里,高斯模型认为「A 暴跌时 B 也暴跌」的概率趋近于 0;而 t-Copula 说这个概率是 39%。如果你的 VaR / 组合风险模型用的是高斯假设,它在尾部给出的是系统性低估——而这恰恰是尾部最危险。

Copula 密度热力图:t-Copula 把质量压向四个角(红),高斯 Copula 把质量压向中腹(蓝)

密度图把差异画得更直观:高斯 Copula 的质量集中在中间肚子,四个角空空;t-Copula(右图与最右「对数密度差」)把显著更多的质量塞进了角落。中间那点差异无所谓,角落那坨才是风险预算真正该盯的地方。

六、回到真实收益:合成双股指的联合尾部#

用 t-Copula 生成相依结构,再逆变换回两只重尾股指的日收益分布,能看到什么?

rng = np.random.default_rng(42)
U_real = sample_t_copula(0.6, 3, 4000)
ra = stats.t.ppf(U_real[:, 0], 6) * 0.012   # 股指 A:t(6) 边际
rb = stats.t.ppf(U_real[:, 1], 5) * 0.015   # 股指 B:t(5) 边际
joint_crash = ((ra < -0.03) & (rb < -0.03)).mean()
python

合成双股指日收益:左下角(同跌)明显比高斯假设更密——这就是 Copula 要抓的尾部联动

结果:同时暴跌(两收益都 < −3%)的样本占比约 4.0%,而同样 ρ 下的高斯假设只有约 0.4%——差了整整一个数量级。这解释了为什么分散投资在平稳市有效、在危机里失效:相关性没变,变的是 Copula 把尾部黏在了一起。

七、三个必须直说的真实陷阱#

  1. 模型选择本身就是风险(Copula 风险)。 选了高斯 Copula,你就强行假设了尾部独立;选了 t-Copula,你又假设了对称的上下尾相依。真实市场的下尾往往比上尾更黏(杠杆+流动性枯竭只发生在下跌)。更诚实的做法是用非对称的 Joe/Clayton Copula,或用经验 Copula(直接对伪观测做核密度估计)完全不假设函数族。

  2. 自由度 ν 极端敏感。 ν 从 4 变到 20,尾部相依系数会从 0.39 掉到接近 0.1。而 ν 的 MLE 估计在高维、短样本下方差极大。务必做敏感性分析:把 ν 上下浮动一档,看你的组合 VaR 变多少。

  3. 边际+Copula 的拼接在前沿会漏。 经验 CDF 在分布中间很稳,但在最极端的 1% 尾部,样本太少,伪观测本身就不准;而 Copula 的尾部行为恰恰取决于那 1%。补救手段是用极值理论(EVT)单独建模边际的尾部,再用 Copula 接中间。

最后一句收口:相关系数回答的是「平时它们像不像」,Copula 回答的是「崩盘时它们会不会一起死」。前者用 ρ 一句话带过,后者需要用尾部相依系数、用自由度 ν、用密度角落那坨质量来认真建模——尤其在给你的组合算风险预算时,别让高斯假设在你最脆弱的时刻替你瞎乐观。

本文所有图表均由 Python(NumPy + SciPy + Matplotlib)基于解析 Copula 采样与极大似然估计生成,数据为合成但结构贴近真实股指,仅用于方法演示,不构成任何投资或交易建议。

Copula 依赖建模:捕捉相关性之外的尾部联动风险
https://blog.halo26812.eu.org/blog/copula-dependence-modeling
Author halo
Published at 2026年7月11日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨