ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Copula函数入门:从联合分布到组合风险度量

Copula函数入门:从联合分布到组合风险度量 在风控圈子里泡久了你会发现一个特别让人头疼的问题单看每一只资产模型都拟合得挺好一旦要把它们放到一起看组合风险立刻就露怯。2008年金融危机前后大量机构用线性相关系数去度量资产之间的关联结果危机一来所有相关性同时飙升组合VaR被严重低估亏得底朝天。Copula函数之所以这些年越来越火核心就一个原因——它能把“每项资产自己的分布”和“资产之间的关联结构”彻底拆开各管各的。这篇文章我从头到尾讲清楚Copula是什么、怎么用、有哪些坑适合做量化风控、资产配置和数据建模的朋友参考。1. 为什么做组合风险必须先解决联合分布问题1.1 单资产看边际多资产看“手牵手”做风险管理的第一步永远是搞清楚收益分布。单只资产的收益分布再复杂也就是一维问题用历史模拟、参数法或者GARCH族模型都能处理。但组合风险不一样你要回答的不是“A资产明天跌多少”而是“A跌的时候B会不会跟着跌、跌多少”这本质上是个多维联合分布问题。联合分布为什么难因为真实金融数据根本不是教科书里那种干干净净的多元正态。收益率有偏度、有厚尾不同资产的尾部行为完全不一样有的资产平时和指数没什么关系一到大跌就“手牵手”一起崩。如果硬要用多元正态去描述这种关系结果就是两个极端要么把尾部相关性估得太低风险严重低估要么为了拟合尾部把整体方差拉大日常风控指标失真。Copula函数的思路其实特别朴素既然联合分布难搞那我把每个变量的边际分布单独建模再用一个“连接函数”把它们的关联结构拼起来。这个连接函数就是Copula。它不关心每项资产本身是什么分布只关心“你排第几、我排第几、咱俩的排名有没有联动”。1.2 线性相关系数的三个坑在引入Copula之前先说说我们以前最常用的工具——皮尔逊线性相关系数。这个指标在多元正态假设下是完美的但拿它来度量金融资产关联至少有三个坑。第一线性相关系数只能捕捉线性关系。资产收益之间如果存在非线性联动比如“平时各走各的极端行情一起暴跌”线性相关系数可能接近0给你一种“这俩没关系”的错觉。第二线性相关系数对边际分布极其敏感。你换一个样本区间、剔除几个极端值相关系数可能从0.5跳到0.2稳定性很差。第三线性相关系数无法区分上尾和下尾的依赖结构。两只资产的上涨联动和下跌联动本质上可能是完全不同的强度但线性相关系数只给一个平均意义上的数字把这个差异整个抹掉了。Copula家族的出现正是为了解决这些问题。它通过秩相关和尾部相关系数把“关联”这件事描述得更细腻不仅知道平时关系有多强还能分别度量“大涨时有多同步”和“大跌时有多同步”。2. Copula到底是什么从Sklar定理到常见家族2.1 Sklar定理的白话版本Copula的理论基石是Sklar定理这个定理用大白话讲就一句话任意一个多维联合分布函数都可以拆成一个Copula函数和若干个边际分布函数的组合。反过来也成立你随便挑几个边际分布再随便挑一个Copula函数把它们拼起来得到的必然是一个合法的联合分布。用公式写出来就是F(x₁, x₂, ..., xₙ) C(F₁(x₁), F₂(x₂), ..., Fₙ(xₙ))其中F是联合分布函数F₁到Fₙ是各变量的边际分布函数C就是Copula。注意一个关键细节F₁(x₁)本身是一个[0,1]区间上的均匀分布随机变量。所以Copula函数本质上描述的是“均匀分布随机变量之间的关联结构”。这个拆解有什么好处你可以完全自由地选择每项资产的边际分布——沪深300用t分布、债券指数用正态分布、某个商品用偏态分布完全没问题。然后你再单独决定它们之间的关联结构用什么Copula。边际分布和关联结构互不干扰这在传统多元分布建模里是想都不敢想的。2.2 常见Copula家族与尾部特征Copula家族非常多但实际做金融风控常用的就那么几个。我把它们分成两大类椭圆族和阿基米德族。椭圆族包括高斯Copula和t-Copula。高斯Copula就是多元正态关联结构的化身它只有一个相关矩阵参数使用简单、计算快但致命弱点是尾部渐进独立——极端行情下变量之间的相关性趋近于0。这恰恰和金融数据的经验相违背。t-Copula在高斯Copula基础上多了一个自由度参数自由度越小尾部越厚它能刻画极端行情的同步下跌或同步上涨是金融建模里最常用的备选。阿基米德族包括Gumbel、Clayton和Frank。Gumbel Copula擅长刻画上尾依赖也就是“牛市里一起涨”的联动Clayton Copula擅长刻画下尾依赖也就是“熊市里一起崩”的联动Frank Copula是对称的但尾部依赖比较弱适合关联程度居中、极端同步不明显的数据。实际操作中怎么选如果你的组合里股票和债券都有你会发现股灾时股票大跌、债券往往大涨这种“负相关的极端联动”用标准阿基米德Copula都不太好刻画需要用到旋转版本的Copula。而如果全是股票类资产下尾依赖明显Clayton或者t-Copula会更合适。2.3 从相关系数到秩相关与尾部相关系数用Copula建模时参数估计不依赖原始的线性相关系数而是依赖两种更稳健的指标Kendalls tau和Spearmans rho。它们都是秩相关——只关心数据的排序是否一致不关心具体数值大小。为什么要用秩相关因为Copula本身只处理均匀分布变换后的数据原始数据的绝对数值已经被抹掉了剩下的只有排名信息。Kendalls tau和Spearmans rho与Copula参数之间存在明确的数学对应关系比如Clayton Copula的参数θ与Kendalls tau满足τ θ / (θ 2)。这意味着你可以从数据里先算一个秩相关系数然后反推出Copula参数的初值大幅减少数值优化的难度。尾部相关系数则是Copula独有的度量工具。上尾相关系数λ_u刻画的是“一个变量出现极端大值的情况下另一个变量也出现极端大值的概率”下尾相关系数λ_l同理。高斯Copula的λ_u和λ_l都是0t-Copula的尾部相关系数大于0但不依赖上下尾的区别Clayton下尾相关为正、上尾为0Gumbel则正好反过来。记住这个特征表选型的时候对着看就行。3. 从零搭建Copula模型的完整流程3.1 五大步骤一步都不能省Copula建模的标准流程我总结成五步边际建模、概率积分变换、Copula参数估计、蒙特卡洛模拟、风险指标计算。每一步都有自己的坑跳过去后面一定出问题。第一步边际建模对每项资产的收益率序列分别建立时间序列模型最常用的是GARCH族模型加一个假设分布目的是把收益率序列里的自相关和异方差性剥干净留下一堆独立同分布的残差。第二步概率积分变换把残差代入假设分布的分布函数得到一组理论上服从[0,1]均匀分布的数据。这步是把边际分布的信息“抹掉”只保留关联结构的信息。第三步Copula参数估计对变换后的均匀分布数据拟合选定的Copula函数估计参数。第四步蒙特卡洛模拟从拟合好的Copula函数里抽样生成成千上万组模拟的均匀分布数据再逆变换回收益率空间。第五步计算风险指标对模拟出的组合收益排序取分位数得到VaR和CVaR。整个链路里最容易犯的错误是跳过第一步直接拿原始收益率去拟合Copula。原始收益率里有自相关和波动率聚集现象不满足独立同分布假设拟合出来的Copula参数是有偏的模拟结果自然不靠谱。3.2 边际模型的选择为什么我推荐GJR-GARCH边际模型直接决定了Copula建模的成败。很多教程喜欢直接用历史均值加样本方差去标准化收益率这在样本量足够大、数据平稳时勉强能用但金融收益率有明显的波动率聚集一个简单的两步法就足够。我的个人习惯是用GJR-GARCH(1,1)加Student-t分布。GJR-GARCH比标准GARCH多了一个非对称项可以捕捉“利空消息比利好消息带来的波动更大”这个金融数据里非常普遍的现象。之所以加Student-t分布假设是因为金融残差普遍厚尾用正态分布会低估极端情况。边际模型建完之后必须做诊断检验。重点看两样东西残差序列是否还有自相关标准化残差平方是否还有ARCH效应。常用的检验是Ljung-Box检验和ARCH-LM检验。如果检验不通过说明你的边际模型没有把数据里的动态结构提取干净需要增加GARCH项或者换更复杂的均值方程否则后面所有步骤都白做。3.3 概率积分变换把边际信息抹掉概率积分变换是Copula建模中最容易被人忽略、但实际影响极大的一个步骤。假设你选定了残差服从t分布那么对每个残差εᵢ计算uᵢ F_t(εᵢ)其中F_t是t分布的累积分布函数。理论上来讲如果边际模型正确uᵢ应该服从[0,1]上的均匀分布而且序列之间应该独立。实际操作中可以用直方图看一眼形状也可以用Kolmogorov-Smirnov检验判断均匀性。如果直方图出现明显的两头翘或者中间凹陷说明边际分布假设有问题需要回头调整。还有一个容易被忽视的细节如果残差自由度很小t分布的尾部很厚概率积分变换后数据会大量聚集在0和1附近。这时你后面拟合Copula特别是拟合Clayton这类对下尾敏感的Copula参数估计会受这几个极端值影响很大。解决办法是考虑用广义帕累托分布对尾部单独建模或者干脆换一个更稳健的Copula估计方法。3.4 Copula参数估计IFM还是CMLCopula参数估计最常用的方法是两阶段极大似然估计也叫IFM法。第一阶段估计各边际模型的参数第二阶段把边际参数固定代入Copula的似然函数只优化Copula自身参数。这个方法计算量小而且因为边际模型和Copula解耦调试起来非常方便。另一种方法是CML方法也叫典型极大似然。它干脆跳过边际模型的参数估计直接用经验累积分布函数对原始数据做变换然后最大化Copula的似然函数。CML的好处是不用担心边际模型设定错误坏处是经验CDF的离散性在小样本下会导致Copula参数估计偏差而且它无法给出边际模型的信息。我个人的建议是如果你对边际分布有把握用IFM如果你只是想快速看一下数据的关联结构不想花时间精调边际模型用CML做初步探索没问题但最终出风险数字的时候还是要回到IFM上。R里面的VineCopula包和copula包两种方法都支持切换成本很低。3.5 蒙特卡洛模拟从Copula到组合风险指标Copula参数拟合完成之后下一步就是蒙特卡洛模拟。模拟的核心是从一个已知的Copula函数中抽样得到一组均匀分布变量然后再通过逆变换把均匀变量映射回各资产的收益率空间。具体说来先根据估计出的Copula参数生成N组均匀分布随机数比如t-Copula的模拟需要先从多元t分布抽样再对每个分量做t分布的累积分布变换。然后把每组均匀数代入各资产残差分布的逆累积分布函数得到模拟残差。最后把模拟残差代回GARCH方程的均值项和波动率项递归生成模拟收益率。有了成千上万组模拟收益率组合风险指标就呼之欲出。把每一期的组合收益按权重加权得到组合收益序列排序后取5%分位数就是95%置信度下的VaR。CVaR更简单取最差的5%那部分收益的平均值。这组模拟还能顺带算压力情景下的条件风险比如“模拟结果中组合收益低于某个阈值的那些天各资产的平均收益分别是多少”这就是风险归因和压力测试的基础。4. 一次完整的R语言实操双资产组合的Copula建模4.1 数据和边际模型说了一堆理论不写代码等于白说。我用R语言跑一个完整的双资产组合案例。数据我用的是两个宽基指数的日收益率时间跨度五年一个代表股票资产一个代表债券资产。完整复现代码不复杂函数都封装在现成包里。# 加载必要的包 library(rugarch) # GARCH建模 library(copula) # Copula函数 library(VineCopula) # Copula拟合与选择 # 读取收益率数据这里假设ret1和ret2是两个收益序列 # data 是 data.frame包含 ret_stock 和 ret_bond # 第一步GJR-GARCH(1,1) Student-t 边际模型 spec_gjr - ugarchspec( variance.model list(model gjrGARCH, garchOrder c(1, 1)), distribution.model std ) fit_stock - ugarchfit(spec spec_gjr, data data$ret_stock) fit_bond - ugarchfit(spec spec_gjr, data data$ret_bond) # 提取标准化残差 resid_stock - residuals(fit_stock, standardize TRUE) resid_bond - residuals(fit_bond, standardize TRUE)这里有个细节提醒rugarch包默认输出的是标准化残差但你需要确认一下它的标准化是用条件标准差还是无条件标准差。正确的标准做法是用条件标准差这样残差才满足独立同分布假设。我一般会用sigma(fit_stock)手动除一遍确保万无一失。边际模型建完之后一定要做Ljung-Box检验。如果p值小于0.05说明残差还有自相关需要回头改均值方程。4.2 Copula拟合与选择边际模型建完之后就可以进入Copula环节了。把残差分别代入t分布的累积分布函数得到两个均匀分布序列然后输入VineCopula包做拟合。# 第二步概率积分变换 u_stock - pstd(resid_stock, mean 0, sd 1, nu coef(fit_stock)[shape]) u_bond - pstd(resid_bond, mean 0, sd 1, nu coef(fit_bond)[shape]) # 整合成矩阵 U - cbind(u_stock, u_bond) # 第三步用VineCopula自动选择最优Copula selected_copula - BiCopSelect(U[, 1], U[, 2], familyset c(1, 2, 3, 4, 5, 6)) summary(selected_copula) # 如果需要手动指定t-Copula并估计参数 t_cop - BiCopEst(U[, 1], U[, 2], family 2) # family2 表示 t-CopulaVineCopula的BiCopSelect会用AIC自动比较各个Copula的拟合优度省去手动比较的麻烦。在我的经验里股票和债券的组合经常选出t-Copula或者Frank Copula因为股债之间的关联本身较弱不需要太强的尾部依赖来刻画。有一点必须提醒自动选择不等于最优选择。如果你做的是压力测试关注的就是大跌时的尾部联动那即使AIC选出Frank Copula你也应该考虑强制用t-Copula或Clayton Copula因为AIC选的是整体拟合最优不是尾部拟合最优。4.3 蒙特卡洛模拟与VaR计算拟合完Copula下一步就是模拟和风险计算。这里我用copula包从拟合好的t-Copula里生成模拟数据然后逆变换回收益率空间。# 第四步蒙特卡洛模拟 set.seed(2024) n_sim - 10000 # 从t-Copula生成模拟的均匀分布数据 t_copula_obj - tCopula( param selected_copula$par, df selected_copula$par2, dim 2 ) sim_uniform - rCopula(n_sim, t_copula_obj) # 第五步逆变换回残差空间 sim_resid_stock - qstd(sim_uniform[, 1], mean 0, sd 1, nu coef(fit_stock)[shape]) sim_resid_bond - qstd(sim_uniform[, 2], mean 0, sd 1, nu coef(fit_bond)[shape]) # 残差代入GARCH方程生成模拟收益率 # 这里需要手动迭代计算条件方差简化起见直接用sample残差的标准差 sim_ret_stock - coef(fit_stock)[mu] sim_resid_stock * sigma(fit_stock)[nrow(data)] sim_ret_bond - coef(fit_bond)[mu] sim_resid_bond * sigma(fit_bond)[nrow(data)] # 计算组合收益假设50/50权重 sim_portfolio - 0.5 * sim_ret_stock 0.5 * sim_ret_bond # VaR 和 CVaR var_95 - quantile(sim_portfolio, probs 0.05) cvar_95 - mean(sim_portfolio[sim_portfolio var_95])这里简化了一件事我把最后一期条件标准差当作恒定值来用。严格的做法是把模拟残差逐期代入GARCH方差方程生成一整条模拟收益率路径再从中取最后一天的收益率作为未来一天的预测。真正的压力测试甚至要模拟未来10天、20天的路径然后计算累积收益。简化版的优点是好理解、代码短如果要上生产环境建议完整迭代GARCH方程代码量也不会多太多但结果会更可靠。4.4 高斯Copula与t-Copula的结果对比上面用的是t-Copula我再用高斯Copula跑一遍同样的流程对比两者的VaR结果能明显看出尾部依赖的差异。# 高斯Copula norm_cop - normalCopula(param BiCopEst(U[, 1], U[, 2], family 1)$par, dim 2) sim_uniform_norm - rCopula(n_sim, norm_cop) sim_resid_stock_norm - qstd(sim_uniform_norm[, 1], mean 0, sd 1, nu coef(fit_stock)[shape]) sim_resid_bond_norm - qstd(sim_uniform_norm[, 2], mean 0, sd 1, nu coef(fit_bond)[shape]) sim_portfolio_norm - 0.5 * (coef(fit_stock)[mu] sim_resid_stock_norm * sigma(fit_stock)[nrow(data)]) 0.5 * (coef(fit_bond)[mu] sim_resid_bond_norm * sigma(fit_bond)[nrow(data)]) var_95_norm - quantile(sim_portfolio_norm, probs 0.05)通常来讲t-Copula模拟出的组合收益尾部更厚95%VaR会比高斯Copula更极端。这正是我们想要的如果数据里存在尾部联动高斯Copula会低估极端风险t-Copula给出的风险数字更贴近现实。你在自己的数据上做对比时如果发现两者的VaR几乎一样那说明你的组合里两只资产的尾部联动确实很弱或者样本区间没有包含明显的极端行情。5. 实务中的五大常见问题与排查建议5.1 概率积分变换后仍然不服从均匀分布这是Copula建模中最常见的问题。你辛辛苦苦建完边际模型做完概率积分变换画直方图一看数据要么堆在中间要么堆在两头根本不是均匀分布。原因大概率是边际模型没建好——分布假设错了或者GARCH方程没有完全捕捉波动率聚集。排查思路很简单先检查标准化残差的QQ图看看尾部是否拟合良好再做Ljung-Box和ARCH-LM检验确认残差没有自相关和ARCH效应。如果这些都通过但PIT后的数据还是不服从均匀分布考虑换一个分布假设比如把t分布换成偏t分布或者用非参数方法做边际变换。5.2 Copula参数估计不收敛或结果异常用极大似然估计拟合Copula参数时优化算法不收敛是比较常见的问题。主要原因有两个一是样本量太小尾部数据太少参数识别困难二是初值设得太差优化算法陷入了局部最优。解决办法也直接先用Kendalls tau推断一个粗略的Copula参数初值再扔给优化器。比如Clayton Copula你可以先用cor(U, method kendall)算出tau然后用τ θ / (θ 2)反解出θ作为初值。这个方法在大多数情况下都能让优化器乖乖收敛。如果还是不收敛检查一下数据变换后的U矩阵是否存在大量完全相同的值比如收益率出现多个0导致CDF变换后并列这会影响参数估计。5.3 模拟出的组合结果与历史经验对不上有时候Copula拟合和模拟都顺利跑完了但你发现模拟出的极端损失比历史实际发生的还夸张或者反过来比历史温和得多。这通常是两个原因在交互作用边际模型的厚尾程度和Copula的尾部依赖强度。我遇到过最典型的情况是边际模型用了高斯分布Copula用了Clayton。最后模拟结果的下尾表现极其夸张因为Clayton的下尾依赖叠加高斯边际的薄尾导致模拟中出现大量“同时大跌但幅度特别离谱”的样本。解决方式是保持边际模型和Copula的尾部特征匹配——厚尾残差数据用t-Copula薄尾数据用高斯Copula别混搭着来。5.4 高维组合建模时的维度灾难做双资产Copula建模很容易但组合里有几十只股票时直接估计一个高维Copula的参数矩阵会非常困难。高维t-Copula的相关矩阵参数数量随维度平方增长样本量不够时估计结果极不稳定。这时候有两条路可以走。一条是Vine Copula它把高维联合分布拆成一组二维条件Copula的级联结构R里面的VineCopula包可以直接处理几十维的问题。另一条是先用PCA或者因子模型对资产做降维提取出几个主成分然后对主成分之间建Copula再把模拟结果映射回原始资产。后者的业务解释性更强在风控实践中更常用。5.5 样本外表现不佳回测失败Copula模型最怕的就是样本外失效。可能你在历史样本上拟合得很好AIC也漂亮但滚动回测时VaR超限次数频繁超过预期。这种情况绝大多数是数据的关联结构本身在变化也就是所谓的高相关状态切换——平时资产之间相关性低危机时相关性突然飙升。处理办法有几种一是用滚动窗口重新估计Copula参数保持模型参数与近期市场状态同步二是引入马尔可夫区制转换允许Copula参数在不同状态之间切换三是给尾部相关系数单独建一个动态模型。这三者的复杂度依次上升建议先从滚动窗口开始成本最低、效果最直观。6. Copula的边界什么场景适合什么场景别硬上6.1 高维场景的Vine Copula架构维度超过5个之后标准的多维t-Copula或者高斯Copula就显得力不从心。相关矩阵的参数数量太多且无法刻画变量之间差异化的尾部依赖结构——比如A和B之间下尾依赖强但A和C之间只有上尾依赖一个对称的t-Copula根本表达不了。Vine Copula的核心思想是用一种树状结构把高维Copula分解成若干个二维Copula。具体来说它把变量之间的依赖关系拆成树的第一层节点和边然后对边上变量的条件分布继续建模生成第二层以此类推。每一条边都是一个标准的二维Copula可以选择完全不同的家族这样整个模型就非常灵活。R里的VineCopula包支持R-vine、C-vine和D-vine三种结构。实践中最常用的是D-vine它假设变量之间有一个自然顺序适合收益率序列这种时间先后关系明确的场景。但Vine Copula也有自己的坑树结构和每条边的Copula家族都需要选择计算量大、过拟合风险高数据量不够的时候推荐使用简化Vine。6.2 时变Copula静态假设的补丁Copula建模默认关联结构在一段时间内是稳定的这个假设在长期样本里几乎不成立。市场相关性有非常明显的时变特征牛市中资产相关性普遍下降熊市中相关性普遍上升这就是业内说的“相关性崩溃”。做时变Copula最轻量化的办法是滚动窗口重估。设定一个长度适中的窗口比如250个交易日每5天滚动一次重新估计Copula参数。这个方案的优点是不需要对模型结构大改缺点是你得到的是一串时变参数要计算VaR时还得决定用最近哪一期的参数。更精细一点的做法是用DCC-GARCH的思路去改造Copula参数让相关矩阵本身服从一个GARCH过程。R里面rmgarch包有现成的函数支持但收敛速度比较慢数据量小的时候参数估计方差极大。我个人的建议是除非你有明确理由相信样本期内相关结构发生了剧烈变化否则先用滚动窗口就行简单模型往往比复杂模型更稳。6.3 别把Copula当万金油Copula函数确实强大但它不是万能的。首先Copula描述的是相关结构不是因果关系。两个资产同时下跌Copula只会告诉你它们倾向于一起跌不会告诉你谁导致谁。其次Copula对边际分布的处理虽然灵活但如果边际模型的预测能力很差Copula再精确也救不回来。最后Copula模型的外推能力有限——它擅长描述“已经发生过的关联模式”但对于从未出现过的极端情形任何Copula都无能为力。我的原则是Copula适合做风险度量和压力测试的情景生成不适合做因果推断更不适合当作精确预测工具。把Copula当作一把好用的螺丝刀而不是万能工具箱你才不会在关键时刻被它坑到。最后的经验分享Copula这个领域理论门槛看起来高实际上手并没有那么吓人。我建议新手别一上来就啃Sklar定理的完整证明也不用急着学Vine Copula先从二元t-Copula开始找两只资产跑通整个流程再逐步扩展维度。你会在实际数据上发现很多教材里不会写的事比如尾部依赖有多难估计、边际模型的选择对结果的影响甚至大过Copula家族的选择。做Copula建模这些年我最深的一个体会是数据清洗和边际建模占掉七成精力Copula本身的估计反而是最简单的一步。把这层关系想明白你的模型就已经超过大多数人了。
返回列表