Modelos de Cópula para Modelagem de Risco Conjunto em Portfólios de Cripto

Além da correlação linear - usando modelos de cópula para capturar a dependência de cauda e o risco conjunto em portfólios de criptomoedas para uma estimativa precisa de VaR e CVaR.

Modelos de cópula — distribuição de risco conjunto

A correlação é a primeira ferramenta que a maioria dos gestores de portfólio utiliza ao avaliar a diversificação. Mas nos mercados de criptomoedas, a correlação é perigosamente enganosa. Dois tokens podem apresentar uma correlação de Pearson de 0,3 durante mercados calmos e depois disparar para 0,95 durante uma queda. A correlação linear pressupõe distribuições elípticas — uma suposição que colapsa diante das caudas pesadas e das estruturas de dependência assimétricas endêmicas aos retornos de criptomoedas.

Os modelos de cópula resolvem isso separando o comportamento marginal (como cada ativo se comporta individualmente) da estrutura de dependência (como os ativos se movem em conjunto). Essa separação, enraizada no teorema de Sklar, nos dá uma estrutura flexível para modelar a distribuição conjunta completa dos retornos do portfólio — incluindo as caudas onde o risco realmente reside.

Por que a correlação linear falha para cripto

Considere um portfólio de BTC, ETH, SOL e AVAX. Durante o colapso da Terra/Luna em maio de 2022, as correlações entre esses ativos convergiram para 1,0, exatamente quando a diversificação era mais necessária. Um otimizador de média-variância que assumisse correlações estáveis teria subestimado drasticamente o risco do portfólio.

Os problemas centrais da correlação de Pearson para cripto:

  1. Distribuições não elípticas. Os retornos de criptomoedas exibem assimetria e curtose significativas. Os retornos diários do BTC regularmente mostram valores de curtose acima de 10 (distribuição normal: 3).
  2. Dependência assimétrica. Os ativos tendem a ser mais correlacionados durante quedas do que durante altas. Esse fenômeno de "quebra de correlação" é bem documentado nos mercados de ações e é ainda mais pronunciado em cripto.
  3. Dependência de cauda. A probabilidade de dois ativos sofrerem perdas extremas simultaneamente não é capturada pela correlação linear. Você pode ter dois ativos com correlação idêntica, mas dependência de cauda vastamente diferente.

O Teorema de Sklar: a base

O teorema de Sklar (1959) afirma que qualquer distribuição conjunta multivariada F(x1,x2,…,xd)F(x_1, x_2, \ldots, x_d) pode ser decomposta em:

F(x1,x2,…,xd)=C(F1(x1),F2(x2),…,Fd(xd))F(x_1, x_2, \ldots, x_d) = C\bigl(F_1(x_1), F_2(x_2), \ldots, F_d(x_d)\bigr)

onde FiF_i são as funções de distribuição marginal e C:[0,1]d→[0,1]C: [0,1]^d \to [0,1] é a cópula — uma função que codifica toda a estrutura de dependência entre as variáveis.

Reciprocamente, se as marginais forem contínuas, a cópula CC é única.

Essa decomposição é poderosa porque nos permite:

  • Modelar a distribuição marginal de cada ativo separadamente (usando GARCH, EVT, ou qualquer distribuição que se ajuste)
  • Modelar a estrutura de dependência de forma independente por meio da cópula
  • Combiná-las para obter a distribuição conjunta completa

A densidade da distribuição conjunta se fatora como:

f(x1,…,xd)=c(F1(x1),…,Fd(xd))⋅∏i=1dfi(xi)f(x_1, \ldots, x_d) = c\bigl(F_1(x_1), \ldots, F_d(x_d)\bigr) \cdot \prod_{i=1}^{d} f_i(x_i)

onde cc é a densidade da cópula e fif_i são as densidades marginais.

Famílias de cópulas e suas propriedades

Comparando a dependência de cauda entre famílias de cópulas

Cópula Gaussiana

A cópula Gaussiana é parametrizada por uma matriz de correlação Σ\Sigma:

CΣGauss(u1,…,ud)=ΦΣ(Φ−1(u1),…,Φ−1(ud))C_{\Sigma}^{\text{Gauss}}(u_1, \ldots, u_d) = \Phi_{\Sigma}\bigl(\Phi^{-1}(u_1), \ldots, \Phi^{-1}(u_d)\bigr)

onde ΦΣ\Phi_{\Sigma} é a CDF normal multivariada e Φ−1\Phi^{-1} é a função quantil normal univariada.

Dependência de cauda: λL=λU=0\lambda_L = \lambda_U = 0 (para ρ<1\rho < 1).

A cópula Gaussiana tem dependência de cauda zero — ela subestima sistematicamente a probabilidade de eventos extremos conjuntos. Isso foi um fator-chave na precificação incorreta de CDOs antes de 2008, e é igualmente perigoso para a modelagem de risco em cripto.

Cópula t de Student

A cópula t introduz dependência de cauda simétrica por meio de um parâmetro de graus de liberdade ν\nu:

Cν,Σt(u1,…,ud)=tν,Σ(tν−1(u1),…,tν−1(ud))C_{\nu, \Sigma}^{t}(u_1, \ldots, u_d) = t_{\nu, \Sigma}\bigl(t_{\nu}^{-1}(u_1), \ldots, t_{\nu}^{-1}(u_d)\bigr)

Dependência de cauda:

λL=λU=2⋅tν+1(−(ν+1)(1−ρ)1+ρ)\lambda_L = \lambda_U = 2 \cdot t_{\nu+1}\left(-\sqrt{\frac{(\nu+1)(1-\rho)}{1+\rho}}\right)

Para ν=4\nu = 4 e ρ=0,5\rho = 0,5, isso dá λ≈0,18\lambda \approx 0,18 — uma probabilidade de 18% de que ambos os ativos estejam simultaneamente em seu pior quantil. Um ν\nu menor (caudas mais pesadas) aumenta essa probabilidade. Os mercados de cripto, com seus retornos de cauda pesada, tipicamente exigem ν\nu na faixa de 3 a 8.

A cópula t é uma melhoria significativa em relação à Gaussiana, mas impõe dependência de cauda simétrica (λL=λU\lambda_L = \lambda_U). Na prática, os ativos de cripto frequentemente exibem dependência de cauda inferior (caem juntos) mais forte do que dependência de cauda superior (sobem juntos).

Cópula de Clayton

A cópula de Clayton captura a dependência de cauda inferior — exatamente o tipo de comportamento assimétrico de agrupamento em quedas que vemos em cripto:

CθClayton(u1,u2)=(u1−θ+u2−θ−1)−1/θ,θ>0C_{\theta}^{\text{Clayton}}(u_1, u_2) = \left(u_1^{-\theta} + u_2^{-\theta} - 1\right)^{-1/\theta}, \quad \theta > 0

Dependência de cauda: λL=2−1/θ\lambda_L = 2^{-1/\theta}, λU=0\lambda_U = 0.

À medida que θ\theta aumenta, a dependência de cauda inferior se fortalece. Para θ=2\theta = 2, λL≈0,71\lambda_L \approx 0,71 — uma probabilidade muito alta de perdas extremas conjuntas.

Cópula de Gumbel

A cópula de Gumbel é a imagem espelhada — ela captura a dependência de cauda superior:

CθGumbel(u1,u2)=exp⁡(−[(−ln⁡u1)θ+(−ln⁡u2)θ]1/θ),θ≥1C_{\theta}^{\text{Gumbel}}(u_1, u_2) = \exp\left(-\left[(-\ln u_1)^{\theta} + (-\ln u_2)^{\theta}\right]^{1/\theta}\right), \quad \theta \geq 1

Dependência de cauda: λL=0\lambda_L = 0, λU=2−21/θ\lambda_U = 2 - 2^{1/\theta}.

Cópula de Frank

A cópula de Frank tem dependência de cauda zero em ambas as caudas (λL=λU=0\lambda_L = \lambda_U = 0), tornando-a adequada para modelar a dependência no corpo da distribuição sem efeitos de cauda:

CθFrank(u1,u2)=−1θln⁡(1+(e−θu1−1)(e−θu2−1)e−θ−1)C_{\theta}^{\text{Frank}}(u_1, u_2) = -\frac{1}{\theta}\ln\left(1 + \frac{(e^{-\theta u_1}-1)(e^{-\theta u_2}-1)}{e^{-\theta}-1}\right)

Escolhendo a cópula certa para cripto

Para portfólios de cripto, a evidência empírica aponta para:

  • Clayton ou Gumbel rotacionada (survival Gumbel) para dependência de cauda inferior — capturando o contágio de quedas
  • Cópula t como uma escolha robusta e de uso geral quando a dependência de cauda simétrica é aceitável
  • Cópula de Joe para capturar forte dependência de cauda superior em fases de alta

Pesquisas de Bruhn e Jeleskovic (2024) descobriram que os modelos GARCH-cópula, particularmente aqueles que usam marginais t de Student com cópulas t, superaram consistentemente as abordagens de média-variância e CVaR histórico em condições de mercado de queda (2022), recuperação (2023) e estabilidade (2024) em cripto.

A maldição da dimensionalidade: entram as cópulas vine

Estrutura em árvore de cópula vine para um portfólio de cripto

As cópulas multivariadas padrão (Gaussiana, cópula t) escalam para altas dimensões, mas impõem suposições restritivas. As cópulas arquimedianas (Clayton, Gumbel, Frank) são naturalmente bivariadas — estendê-las para d>2d > 2 dimensões exige que todos os pares compartilhem o mesmo parâmetro de dependência, o que é irrealista.

As cópulas vine resolvem isso decompondo uma cópula dd-dimensional em uma cascata de cópulas bivariadas organizadas em uma estrutura em árvore. Cada par de variáveis (condicional às demais) recebe sua própria família e parâmetro de cópula bivariada.

A construção de pares de cópulas

Para uma densidade dd-dimensional, a fatoração da cópula vine é:

f(x1,…,xd)=∏i=1dfi(xi)⋅∏j=1d−1∏i=1d−jci,i+j∣i+1,…,i+j−1f(x_1, \ldots, x_d) = \prod_{i=1}^{d} f_i(x_i) \cdot \prod_{j=1}^{d-1}\prod_{i=1}^{d-j} c_{i,i+j|i+1,\ldots,i+j-1}

onde ci,j∣Sc_{i,j|S} é uma densidade de cópula bivariada para as variáveis ii e jj condicionadas ao conjunto SS.

Uma cópula vine dd-dimensional requer (d2)=d(d−1)/2\binom{d}{2} = d(d-1)/2 cópulas bivariadas. Para um portfólio de cripto de 10 ativos, isso são 45 cópulas de pares — cada uma possivelmente de uma família diferente.

Estruturas vine: C-vine, D-vine, R-vine

C-vine (vine canônica): Cada árvore tem um único nó raiz conectado a todos os outros nós. Melhor quando uma variável domina — por exemplo, BTC como o motor do mercado.

Tree 1:    BTC --- ETH
           BTC --- SOL
           BTC --- AVAX
           BTC --- DOT

Tree 2:    ETH|BTC --- SOL|BTC
           ETH|BTC --- AVAX|BTC
           ETH|BTC --- DOT|BTC

D-vine (vine desenhável): Uma estrutura de caminho sequencial. Melhor quando as variáveis têm uma ordenação natural (por exemplo, por capitalização de mercado ou setor).

R-vine (vine regular): A estrutura mais geral — qualquer sequência de árvore válida. As R-vines abrangem tanto C-vines quanto D-vines.

Pesquisas sobre portfólios de criptomoedas sugerem que estruturas D-vine frequentemente produzem previsões de VaR superiores em comparação com C-vine e R-vine para ativos de cripto, embora isso dependa da composição específica do portfólio.

Por que as cópulas vine importam para cripto

Um portfólio de 8 ativos de cripto modelado com uma única cópula de Clayton força todos os 28 pares a compartilhar o mesmo θ\theta. Mas BTC-ETH pode ter θClayton=3,5\theta_{\text{Clayton}} = 3,5 (forte dependência de queda), enquanto SOL-AVAX pode ter θ=1,2\theta = 1,2 (moderado). As cópulas vine permitem que cada par expresse sua própria estrutura de dependência:

  • BTC-ETH: cópula t (ν=4\nu=4, ρ=0,72\rho=0,72)
  • BTC-SOL: Clayton (θ=2,1\theta=2,1)
  • ETH-AVAX: Frank (θ=5,3\theta=5,3)
  • SOL-DOT | BTC: Gumbel (θ=1,8\theta=1,8)

Essa flexibilidade é crítica para uma estimativa precisa do risco do portfólio.

Modelando as marginais: GARCH-EVT

Antes de ajustar a cópula, precisamos transformar a série de retornos de cada ativo em variáveis uniformes [0,1][0,1] (a "transformada integral de probabilidade"). O pipeline padrão:

  1. Ajustar um modelo GARCH à série de retornos de cada ativo para capturar a volatilidade variável no tempo
  2. Extrair resíduos padronizados zt=(rt−μt)/σtz_t = (r_t - \mu_t) / \sigma_t
  3. Ajustar as caudas usando a Teoria de Valores Extremos (EVT) — especificamente, a Distribuição de Pareto Generalizada (GPD) para as caudas superior e inferior além de um limiar (tipicamente os percentis 5 e 95)
  4. Usar a CDF empírica para o corpo da distribuição
  5. Aplicar a transformada integral de probabilidade para obter observações pseudo-uniformes ui,t=F^i(zi,t)u_{i,t} = \hat{F}_i(z_{i,t})

Essa abordagem GARCH-EVT é frequentemente chamada de método "semi-paramétrico". Ela captura corretamente:

  • Agrupamento de volatilidade (GARCH)
  • Caudas pesadas (GPD do EVT)
  • A forma geral da distribuição (CDF empírica para o corpo)

Para ativos de cripto, um modelo EGARCH(1,1) ou GJR-GARCH(1,1) com inovações t de Student tende a funcionar bem, pois captura a resposta assimétrica de volatilidade (más notícias aumentam mais a volatilidade do que boas notícias).

VaR e CVaR de portfólio com cópulas

Valor em Risco (VaR)

O VaR do portfólio no nível de confiança α\alpha é:

VaRα=inf⁡{l:P(L≤l)≥α}\text{VaR}_\alpha = \inf\{ l : P(L \leq l) \geq \alpha \}

onde LL é a perda do portfólio. Com cópulas, estimamos o VaR via Monte Carlo:

  1. Simular NN amostras da cópula vine ajustada (no espaço uniforme)
  2. Transformar de volta para o espaço de retornos usando as CDFs marginais inversas
  3. Calcular os retornos do portfólio: rp=∑iwi⋅rir_p = \sum_i w_i \cdot r_i
  4. O VaR é o quantil α\alpha da distribuição de perda do portfólio simulada

Valor em Risco Condicional (CVaR / Expected Shortfall)

O CVaR é a perda esperada dado que a perda excede o VaR:

CVaRα=E[L∣L≥VaRα]=11−α∫α1VaRu du\text{CVaR}_\alpha = E[L \mid L \geq \text{VaR}_\alpha] = \frac{1}{1-\alpha}\int_{\alpha}^{1}\text{VaR}_u\, du

O CVaR é coerente (satisfaz a subaditividade), tornando-o superior ao VaR para otimização de portfólio. A estimativa por Monte Carlo é direta — calcular a média das perdas que excedem o VaR.

Por que o risco baseado em cópula supera o risco baseado em correlação

Considere dois portfólios com correlações par a par idênticas de 0,5:

  • Portfólio A: dependência de cópula Gaussiana (sem dependência de cauda)
  • Portfólio B: dependência de cópula de Clayton (θ=2\theta = 2, λL=0,71\lambda_L = 0,71)

No nível de confiança de 99%, o Portfólio B terá um VaR e CVaR significativamente mais altos porque a cópula de Clayton modela corretamente a tendência dos ativos de quebrarem juntos. A cópula Gaussiana subestima esse risco ao assumir que as comovimentações extremas são extremamente raras.

Em estudos empíricos sobre portfólios de cripto, a diferença no CVaR de 99% entre os modelos Gaussiano e de cópula vine pode exceder 30-40%, o que significa que os modelos baseados em correlação podem subestimar o risco de cauda em um terço ou mais.

Implementação: Python com pyvinecopulib

Aqui está um pipeline completo para ajustar uma cópula vine a retornos de cripto e estimar o VaR/CVaR do portfólio.

Etapa 1: Preparação de dados e ajuste marginal

import numpy as np
import pandas as pd
from arch import arch_model
from scipy import stats
import pyvinecopulib as pv

def fetch_crypto_returns(symbols, start="2023-01-01", end="2025-12-31"):
    """
    Fetch daily returns for a list of crypto symbols.
    Replace with your data source (ccxt, yfinance, etc.)
    """
    import yfinance as yf
    prices = yf.download(
        [f"{s}-USD" for s in symbols],
        start=start, end=end
    )["Close"]
    prices.columns = symbols
    returns = np.log(prices / prices.shift(1)).dropna()
    return returns

symbols = ["BTC", "ETH", "SOL", "AVAX", "DOT", "LINK", "MATIC", "ATOM"]
returns = fetch_crypto_returns(symbols)

def fit_garch_marginal(series, dist="t"):
    """
    Fit GJR-GARCH(1,1) with Student-t innovations.
    Returns standardized residuals and the fitted model.
    """
    model = arch_model(
        series * 100,  # scale for numerical stability
        vol="GARCH",
        p=1, o=1, q=1,  # GJR-GARCH
        dist=dist,
        mean="AR",
        lags=1
    )
    result = model.fit(disp="off")
    std_resid = result.std_resid.dropna()
    return std_resid, result

# Fit GARCH to each asset and collect standardized residuals
residuals = {}
garch_models = {}
for sym in symbols:
    std_resid, model = fit_garch_marginal(returns[sym])
    residuals[sym] = std_resid
    garch_models[sym] = model

residuals_df = pd.DataFrame(residuals).dropna()

Etapa 2: Transformada integral de probabilidade

def semi_parametric_pit(residuals, tail_threshold=0.05):
    """
    Semi-parametric probability integral transform:
    - GPD for tails beyond threshold
    - Empirical CDF for the body
    Returns pseudo-uniform observations in [0, 1].
    """
    n = len(residuals)
    u = np.zeros(n)
    sorted_resid = np.sort(residuals)

    lower_thresh = np.quantile(residuals, tail_threshold)
    upper_thresh = np.quantile(residuals, 1 - tail_threshold)

    for i, x in enumerate(residuals):
        if x <= lower_thresh:
            # Fit GPD to lower tail (negate for positive values)
            lower_exceedances = -(residuals[residuals <= lower_thresh] - lower_thresh)
            shape, _, scale = stats.genpareto.fit(lower_exceedances, floc=0)
            u[i] = tail_threshold * (
                1 - stats.genpareto.cdf(-(x - lower_thresh), shape, scale=scale)
            )
        elif x >= upper_thresh:
            # Fit GPD to upper tail
            upper_exceedances = residuals[residuals >= upper_thresh] - upper_thresh
            shape, _, scale = stats.genpareto.fit(upper_exceedances, floc=0)
            u[i] = 1 - tail_threshold * (
                1 - stats.genpareto.cdf(x - upper_thresh, shape, scale=scale)
            )
        else:
            # Empirical CDF for the body
            u[i] = np.mean(residuals <= x)

    # Clip to avoid exact 0 or 1
    u = np.clip(u, 1e-6, 1 - 1e-6)
    return u

# Transform all residuals to pseudo-uniform
U = np.column_stack([
    semi_parametric_pit(residuals_df[sym].values)
    for sym in symbols
])

Etapa 3: Ajustar a cópula vine

# Configure the vine copula fit
controls = pv.FitControlsVinecop(
    family_set=[
        pv.BicopFamily.student,
        pv.BicopFamily.clayton,
        pv.BicopFamily.gumbel,
        pv.BicopFamily.frank,
        pv.BicopFamily.joe,
        pv.BicopFamily.bb1,       # Clayton-Gumbel mixture
        pv.BicopFamily.bb7,       # Joe-Clayton mixture
        pv.BicopFamily.gaussian,
    ],
    selection_criterion="bic",    # BIC for model selection
    tree_criterion="tau",          # Kendall's tau for tree structure
    nonparametric_method="constant",
    trunc_lvl=5,                   # Truncate after 5 trees
)

# Fit the vine copula
vine = pv.Vinecop(U, controls=controls)

# Inspect the fitted structure
print(f"Log-likelihood: {vine.loglik(U):.2f}")
print(f"AIC: {vine.aic(U):.2f}")
print(f"BIC: {vine.bic(U):.2f}")

# Print pair copula families for the first tree
for i in range(vine.order.shape[0] - 1):
    pair = vine.get_pair_copula(0, i)
    print(f"Tree 1, Edge {i}: {pair.family} "
          f"(params: {pair.parameters})")

Etapa 4: VaR e CVaR por Monte Carlo

def estimate_var_cvar(vine, garch_models, symbols, weights,
                       n_sim=50_000, alpha=0.99, seed=42):
    """
    Estimate portfolio VaR and CVaR using Monte Carlo simulation
    from the fitted vine copula.
    """
    # Simulate from vine copula (uniform space)
    U_sim = vine.simulate(n=n_sim, seeds=[seed])

    # Transform back to return space via inverse marginals
    returns_sim = np.zeros((n_sim, len(symbols)))
    for j, sym in enumerate(symbols):
        model = garch_models[sym]
        # Get last conditional volatility and mean
        forecasts = model.forecast(horizon=1)
        mu = forecasts.mean.iloc[-1, 0] / 100  # unscale
        sigma = np.sqrt(forecasts.variance.iloc[-1, 0]) / 100

        # Inverse PIT: uniform -> standardized residual -> return
        # Using t-distribution inverse CDF with fitted dof
        nu = model.params.get("nu", 5)
        z_sim = stats.t.ppf(U_sim[:, j], df=nu)
        returns_sim[:, j] = mu + sigma * z_sim

    # Portfolio returns
    weights = np.array(weights)
    portfolio_returns = returns_sim @ weights

    # Losses (negative returns)
    losses = -portfolio_returns

    # VaR
    var = np.quantile(losses, alpha)

    # CVaR (Expected Shortfall)
    cvar = np.mean(losses[losses >= var])

    return var, cvar, portfolio_returns

# Equal-weighted portfolio
weights = [1.0 / len(symbols)] * len(symbols)

var_99, cvar_99, sim_returns = estimate_var_cvar(
    vine, garch_models, symbols, weights,
    n_sim=100_000, alpha=0.99
)

print(f"1-day 99% VaR:  {var_99*100:.2f}%")
print(f"1-day 99% CVaR: {cvar_99*100:.2f}%")

# Compare with Gaussian assumption
from scipy.stats import norm
mu_p = sim_returns.mean()
sigma_p = sim_returns.std()
var_gauss = -(mu_p + sigma_p * norm.ppf(0.01))
print(f"\nGaussian VaR:   {var_gauss*100:.2f}%")
print(f"Copula/Gaussian ratio: {var_99/var_gauss:.2f}x")

Etapa 5: Análise de dependência de cauda

def compute_tail_dependence(vine, symbols):
    """
    Extract lower and upper tail dependence coefficients
    from the first tree of the vine copula.
    """
    results = []
    order = vine.order
    n_edges = order.shape[0] - 1

    for i in range(n_edges):
        pair = vine.get_pair_copula(0, i)
        # Compute tail dependence from the bivariate copula
        # Using simulation-based estimation
        u_pair = pair.simulate(n=100_000, seeds=[42])
        q = 0.01  # 1st percentile

        # Lower tail dependence
        mask_lower = (u_pair[:, 0] <= q)
        lambda_L = np.mean(u_pair[mask_lower, 1] <= q) if mask_lower.sum() > 0 else 0

        # Upper tail dependence
        mask_upper = (u_pair[:, 0] >= 1 - q)
        lambda_U = np.mean(u_pair[mask_upper, 1] >= 1 - q) if mask_upper.sum() > 0 else 0

        i_idx = order[0]
        j_idx = order[i + 1]
        results.append({
            "pair": f"{symbols[i_idx]}-{symbols[j_idx]}",
            "family": str(pair.family),
            "lambda_L": round(lambda_L, 4),
            "lambda_U": round(lambda_U, 4),
        })

    return pd.DataFrame(results)

tail_dep = compute_tail_dependence(vine, symbols)
print(tail_dep.to_string(index=False))

Uma saída típica para um portfólio de cripto pode se parecer com isto:

PairFamilyλL\lambda_LλU\lambda_U
BTC-ETHstudent0.220.22
BTC-SOLclayton0.350.00
BTC-AVAXbb70.280.12
BTC-DOTstudent0.180.18
BTC-LINKclayton0.310.00
BTC-MATICfrank0.000.00
BTC-ATOMgumbel0.000.15

Observe como cada par pode ter uma estrutura de dependência completamente diferente. BTC-SOL mostra forte dependência de cauda inferior (Clayton) com dependência de cauda superior zero — eles caem juntos, mas não necessariamente sobem juntos. BTC-MATIC não mostra nenhuma dependência de cauda (Frank), sugerindo algum benefício de diversificação mesmo em situações extremas.

Backtesting do modelo de VaR com cópula

Um modelo de VaR só é útil se estiver bem calibrado. O backtest padrão calcula as violações de VaR — dias em que a perda real excedeu o VaR previsto — e testa se a taxa de violação corresponde à taxa esperada.

def backtest_var(returns, symbols, weights, window=500,
                 alpha=0.99, n_sim=20_000):
    """
    Rolling-window VaR backtest using vine copula.
    """
    violations = []
    var_series = []
    T = len(returns)

    for t in range(window, T):
        # Rolling window of returns
        window_returns = returns.iloc[t-window:t]

        # 1. Fit GARCH marginals
        U_window = np.zeros((window, len(symbols)))
        models_t = {}
        for j, sym in enumerate(symbols):
            std_resid, model = fit_garch_marginal(window_returns[sym])
            models_t[sym] = model
            # Simple empirical PIT for speed
            u = pv.to_pseudo_obs(std_resid.values.reshape(-1, 1))
            U_window[:len(u), j] = u.ravel()

        # 2. Fit vine copula
        U_clean = U_window[~np.any(U_window == 0, axis=1)]
        vine_t = pv.Vinecop(U_clean, controls=controls)

        # 3. Estimate VaR
        var_t, _, _ = estimate_var_cvar(
            vine_t, models_t, symbols, weights,
            n_sim=n_sim, alpha=alpha
        )
        var_series.append(var_t)

        # 4. Check violation
        actual_return = (returns.iloc[t][symbols].values
                         * np.array(weights)).sum()
        violations.append(-actual_return > var_t)

    violation_rate = np.mean(violations)
    expected_rate = 1 - alpha
    print(f"Expected violation rate: {expected_rate:.4f}")
    print(f"Actual violation rate:   {violation_rate:.4f}")
    print(f"Number of violations:    {sum(violations)} / {len(violations)}")

    return violations, var_series

Um modelo de VaR de 99% bem calibrado deve ter uma taxa de violação próxima de 1%. Se a taxa for significativamente mais alta, o modelo subestima o risco. Se significativamente mais baixa, é excessivamente conservador.

Considerações práticas

Custo computacional

O ajuste de cópulas vine é O(d2⋅n)O(d^2 \cdot n) por nível de árvore. Para um portfólio de 10 ativos com janelas móveis de 500 dias, um backtest completo com 50.000 simulações de Monte Carlo por etapa pode levar horas. Estratégias para gerenciar isso:

  • Vines truncadas: Definir trunc_lvl=3 ou trunc_lvl=4 — árvores mais altas capturam dependências condicionais mais fracas que contribuem menos para o risco
  • Contagem de simulação reduzida: 10.000-20.000 simulações frequentemente bastam para VaR de 99%
  • Computação paralela: Os ajustes GARCH para cada ativo são independentes e podem ser paralelizados
  • Cache de modelo: Reajustar a cópula semanalmente em vez de diariamente, atualizando apenas as previsões GARCH

Consciência de regime

Os mercados de cripto exibem regimes distintos (alta, baixa, lateral, eventos de alta volatilidade). Uma única cópula vine ajustada em toda a amostra pode não capturar a dependência dependente de regime. Considere:

  • Janelas móveis de 250-500 dias
  • Cópulas com troca de regime onde os parâmetros da cópula dependem de um estado oculto de Markov
  • Observações ponderadas exponencialmente que dão mais peso aos dados recentes

Armadilhas comuns

  1. Esquecer a PIT. Alimentar retornos brutos diretamente na cópula em vez de observações pseudo-uniformes produzirá resultados sem sentido. Sempre transforme para margens uniformes primeiro.
  2. Overfitting com muitas famílias. Incluir cada família bivariada possível no conjunto de seleção pode levar ao overfitting, especialmente com amostras curtas. Use BIC para seleção de modelo e considere restringir a 4-5 famílias.
  3. Ignorar a dependência serial. As cópulas modelam a dependência transversal em um único ponto no tempo. Se você pular a etapa GARCH e alimentar retornos autocorrelacionados na cópula, a dependência estimada será contaminada por efeitos seriais.
  4. Cópulas estáticas em mercados dinâmicos. Uma cópula ajustada em dados de mercado de alta de 2021 será mal calibrada para uma queda de 2022. Sempre use janelas móveis ou expansivas.

Conclusão

Os modelos de cópula — particularmente as cópulas vine — fornecem uma estrutura matematicamente rigorosa para modelar o risco conjunto de portfólios de cripto que vai muito além do que a correlação linear pode capturar. As principais vantagens:

  • Modelagem separada de marginais e dependência por meio do teorema de Sklar
  • Dependência de cauda flexível por meio da seleção adequada da família de cópulas (Clayton para contágio de quedas, Gumbel para comovimentação em altas, cópula t para caudas simétricas)
  • Escalabilidade em alta dimensão por meio da decomposição em cópula vine, onde cada par de ativos recebe sua própria cópula bivariada
  • Estimativa precisa de VaR/CVaR que leva em conta a dependência não linear e assimétrica — crítica para a gestão de risco em um mercado onde "tudo cai junto" é a norma, não a exceção

O pipeline GARCH-EVT-Cópula é agora a abordagem padrão em fundos de hedge quantitativos e mesas de risco focadas em cripto. Com bibliotecas como pyvinecopulib, a barreira de implementação é baixa o suficiente para que qualquer trader sistemático possa integrar a modelagem de risco baseada em cópula ao seu fluxo de trabalho de gestão de portfólio.

O código neste artigo fornece um ponto de partida funcional. Para uso em produção, você adicionaria validação cruzada adequada para a seleção da ordem GARCH, modelos marginais mais sofisticados (por exemplo, EGARCH com efeitos de alavancagem, ou medidas de volatilidade realizada usando dados intradiários), e testes de estresse sob parâmetros de cópula hipotéticos calibrados para episódios históricos de crise.


Referências

  • Sklar, A. (1959). Fonctions de repartition a n dimensions et leurs marges. Publications de l'Institut de Statistique de l'Universite de Paris, 8, 229-231.
  • Joe, H. (2014). Dependence Modeling with Copulas. Chapman and Hall/CRC.
  • Aas, K., Czado, C., Frigessi, A., & Bakken, H. (2009). Pair-copula constructions of multiple dependence. Insurance: Mathematics and Economics, 44(2), 182-198.
  • Jeleskovic, V. & Bruhn, L. (2024). Cryptocurrency portfolio optimization: Utilizing a GARCH-Copula model within the Markowitz framework. Journal of Corporate Accounting & Finance.
  • Nagler, T. & Vatter, T. (2023). pyvinecopulib: A Python library for vine copula models. GitHub.
  • Tiwari, A. K., et al. (2020). Modeling risk dependence and portfolio VaR forecast through vine copula for cryptocurrencies. PLOS ONE, 15(1), e0242102.