← Volver a los artículos
March 30, 2026
5 min de lectura

Modelos de cópula para la modelización conjunta del riesgo en carteras cripto

Modelos de cópula para la modelización conjunta del riesgo en carteras cripto
#risk
#copula
#portfolio
#tail-dependence
#VaR
📊
Part 4 of 5 · Collection
Portfolio Construction & Risk

Modelos de cópula — distribución conjunta del riesgo

La correlación es la primera herramienta a la que recurren la mayoría de los gestores de carteras al evaluar la diversificación. Pero en los mercados cripto, la correlación resulta peligrosamente engañosa. Dos tokens pueden mostrar una correlación de Pearson de 0,3 en mercados tranquilos y luego dispararse a 0,95 durante un desplome. La correlación lineal asume distribuciones elípticas, una hipótesis que se derrumba ante las colas pesadas y las estructuras de dependencia asimétrica endémicas de los rendimientos de las criptomonedas.

Los modelos de cópula resuelven esto separando el comportamiento marginal (cómo se comporta cada activo individualmente) de la estructura de dependencia (cómo se mueven los activos conjuntamente). Esta separación, basada en el teorema de Sklar, nos proporciona un marco flexible para modelar la distribución conjunta completa de los rendimientos de la cartera, incluidas las colas, donde realmente reside el riesgo.

Por qué la correlación lineal falla en cripto

Consideremos una cartera de BTC, ETH, SOL y AVAX. Durante el colapso de Terra/Luna en mayo de 2022, las correlaciones entre estos activos convergieron hacia 1,0, precisamente cuando más se necesitaba la diversificación. Un optimizador de media-varianza que asumiera correlaciones estables habría subestimado drásticamente el riesgo de la cartera.

Los problemas centrales de la correlación de Pearson para cripto:

  1. Distribuciones no elípticas. Los rendimientos cripto muestran una asimetría y curtosis significativas. Los rendimientos diarios de BTC muestran regularmente valores de curtosis superiores a 10 (distribución normal: 3).
  2. Dependencia asimétrica. Los activos tienden a estar más correlacionados durante las caídas que durante los rallies. Este fenómeno de "ruptura de correlación" está bien documentado en los mercados de acciones y es aún más pronunciado en cripto.
  3. Dependencia de cola. La probabilidad de que dos activos experimenten simultáneamente pérdidas extremas no queda capturada por la correlación lineal. Se pueden tener dos activos con correlación idéntica pero dependencia de cola muy diferente.

El teorema de Sklar: el fundamento

El teorema de Sklar (1959) establece que cualquier distribución conjunta multivariante F(x1,x2,,xd)F(x_1, x_2, \ldots, x_d) puede descomponerse en:

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)

donde FiF_i son las funciones de distribución marginales y C:[0,1]d[0,1]C: [0,1]^d \to [0,1] es la cópula, una función que codifica toda la estructura de dependencia entre las variables.

A la inversa, si las marginales son continuas, la cópula CC es única.

Esta descomposición es poderosa porque nos permite:

  • Modelar la distribución marginal de cada activo por separado (usando GARCH, EVT, o cualquier distribución que se ajuste)
  • Modelar la estructura de dependencia de forma independiente mediante la cópula
  • Combinarlas para obtener la distribución conjunta completa

La densidad de la distribución conjunta se factoriza 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)

donde cc es la densidad de la cópula y fif_i son las densidades marginales.

Familias de cópulas y sus propiedades

Comparación de la dependencia de cola entre familias de cópulas

Cópula gaussiana

La cópula gaussiana está parametrizada por una matriz de correlación Σ\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)

donde ΦΣ\Phi_{\Sigma} es la CDF normal multivariante y Φ1\Phi^{-1} es la función cuantil normal univariante.

Dependencia de cola: λL=λU=0\lambda_L = \lambda_U = 0 (para ρ<1\rho < 1).

La cópula gaussiana tiene una dependencia de cola nula — subestima sistemáticamente la probabilidad de eventos extremos conjuntos. Esto fue un factor clave en la mala valoración de los CDO antes de 2008, y es igual de peligroso para la modelización de riesgo cripto.

Cópula t de Student

La cópula t introduce dependencia de cola simétrica mediante un parámetro de grados de libertad ν\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)

Dependencia de cola:

λL=λU=2tν+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 y ρ=0,5\rho = 0,5, esto da λ0,18\lambda \approx 0,18 — una probabilidad del 18% de que ambos activos estén simultáneamente en su peor cuantil. Un ν\nu menor (colas más pesadas) aumenta esta probabilidad. Los mercados cripto, con sus rendimientos de colas pesadas, típicamente requieren un ν\nu en el rango de 3 a 8.

La cópula t es una mejora significativa respecto a la gaussiana, pero impone dependencia de cola simétrica (λL=λU\lambda_L = \lambda_U). En la práctica, los activos cripto suelen mostrar una dependencia de cola inferior más fuerte (caen juntos) que de cola superior (suben juntos).

Cópula de Clayton

La cópula de Clayton captura la dependencia de cola inferior, exactamente el tipo de comportamiento asimétrico de agrupación en caídas que vemos en 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

Dependencia de cola: λL=21/θ\lambda_L = 2^{-1/\theta}, λU=0\lambda_U = 0.

A medida que aumenta θ\theta, la dependencia de cola inferior se intensifica. Para θ=2\theta = 2, λL0,71\lambda_L \approx 0,71 — una probabilidad muy alta de pérdidas extremas conjuntas.

Cópula de Gumbel

La cópula de Gumbel es la imagen especular: captura la dependencia de cola superior:

CθGumbel(u1,u2)=exp([(lnu1)θ+(lnu2)θ]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

Dependencia de cola: λL=0\lambda_L = 0, λU=221/θ\lambda_U = 2 - 2^{1/\theta}.

Cópula de Frank

La cópula de Frank tiene dependencia de cola nula en ambas colas (λL=λU=0\lambda_L = \lambda_U = 0), lo que la hace adecuada para modelar la dependencia en el cuerpo de la distribución sin efectos de cola:

CθFrank(u1,u2)=1θln(1+(eθu11)(eθu21)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)

Elegir la cópula adecuada para cripto

Para carteras cripto, la evidencia empírica apunta a:

  • Clayton o Gumbel rotada (survival Gumbel) para la dependencia de cola inferior, capturando el contagio en las caídas
  • Cópula t como elección general robusta cuando es aceptable la dependencia de cola simétrica
  • Cópula de Joe para capturar una fuerte dependencia de cola superior en las fases de rally

Un estudio de Bruhn y Jeleskovic (2024) encontró que los modelos GARCH-cópula, particularmente aquellos que usan marginales t de Student con cópulas t, superaron consistentemente a los enfoques de media-varianza y CVaR histórico en condiciones de mercado bajista (2022), de recuperación (2023) y de estabilidad (2024) en cripto.

La maldición de la dimensionalidad: entran las cópulas vine

Estructura de árbol de cópula vine para una cartera cripto

Las cópulas multivariantes estándar (gaussiana, t) escalan a altas dimensiones pero imponen supuestos restrictivos. Las cópulas arquimedianas (Clayton, Gumbel, Frank) son naturalmente bivariadas — extenderlas a dimensiones d>2d > 2 requiere que todos los pares compartan el mismo parámetro de dependencia, lo cual es poco realista.

Las cópulas vine resuelven esto descomponiendo una cópula de dimensión dd en una cascada de cópulas bivariadas organizadas en una estructura de árbol. Cada par de variables (condicionado a las demás) obtiene su propia familia y parámetro de cópula bivariada.

La construcción de pares-cópula

Para una densidad de dimensión dd, la factorización de la cópula vine es:

f(x1,,xd)=i=1dfi(xi)j=1d1i=1djci,i+ji+1,,i+j1f(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}

donde ci,jSc_{i,j|S} es una densidad de cópula bivariada para las variables ii y jj condicionadas al conjunto SS.

Una cópula vine de dimensión dd requiere (d2)=d(d1)/2\binom{d}{2} = d(d-1)/2 cópulas bivariadas. Para una cartera cripto de 10 activos, eso son 45 cópulas de pares, cada una potencialmente de una familia diferente.

Estructuras vine: C-vine, D-vine, R-vine

C-vine (vine canónico): Cada árbol tiene un único nodo raíz conectado a todos los demás nodos. Es óptimo cuando una variable domina, por ejemplo, BTC como impulsor del 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 dibujable): Una estructura de ruta secuencial. Es óptima cuando las variables tienen un orden natural (por ejemplo, por capitalización de mercado o sector).

R-vine (vine regular): La estructura más general — cualquier secuencia de árboles válida. Los R-vines engloban tanto a los C-vines como a los D-vines.

La investigación sobre carteras de criptomonedas sugiere que las estructuras D-vine a menudo producen mejores pronósticos de VaR en comparación con C-vine y R-vine para activos cripto, aunque esto depende de la composición específica de la cartera.

Por qué las cópulas vine importan en cripto

Una cartera de 8 activos cripto modelada con una única cópula de Clayton obliga a los 28 pares a compartir el mismo θ\theta. Pero BTC-ETH podría tener θClayton=3,5\theta_{\text{Clayton}} = 3,5 (fuerte dependencia en caídas), mientras que SOL-AVAX podría tener θ=1,2\theta = 1,2 (moderada). Las cópulas vine permiten que cada par exprese su propia estructura de dependencia:

  • 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)

Esta flexibilidad es fundamental para una estimación precisa del riesgo de la cartera.

Modelización de las marginales: GARCH-EVT

Antes de ajustar la cópula, necesitamos transformar la serie de rendimientos de cada activo en variables uniformes [0,1][0,1] (la "transformada integral de probabilidad"). El pipeline estándar:

  1. Ajustar un modelo GARCH a la serie de rendimientos de cada activo para capturar la volatilidad variable en el tiempo
  2. Extraer los residuos estandarizados zt=(rtμt)/σtz_t = (r_t - \mu_t) / \sigma_t
  3. Ajustar las colas usando la Teoría de Valores Extremos (EVT), específicamente la Distribución de Pareto Generalizada (GPD) para las colas superior e inferior más allá de un umbral (típicamente los percentiles 5 y 95)
  4. Usar la CDF empírica para el cuerpo de la distribución
  5. Aplicar la transformada integral de probabilidad para obtener observaciones pseudo-uniformes ui,t=F^i(zi,t)u_{i,t} = \hat{F}_i(z_{i,t})

Este enfoque GARCH-EVT suele denominarse método "semiparamétrico". Captura correctamente:

  • El agrupamiento de la volatilidad (GARCH)
  • Las colas pesadas (GPD de EVT)
  • La forma general de la distribución (CDF empírica para el cuerpo)

Para activos cripto, un modelo EGARCH(1,1) o GJR-GARCH(1,1) con innovaciones t de Student suele funcionar bien, ya que captura la respuesta de volatilidad asimétrica (las malas noticias aumentan la volatilidad más que las buenas).

VaR y CVaR de cartera con cópulas

Value-at-Risk (VaR)

El VaR de la cartera al nivel de confianza α\alpha es:

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

donde LL es la pérdida de la cartera. Con cópulas, estimamos el VaR mediante Monte Carlo:

  1. Simular NN muestras de la cópula vine ajustada (en el espacio uniforme)
  2. Transformar de vuelta al espacio de rendimientos usando las CDF marginales inversas
  3. Calcular los rendimientos de la cartera: rp=iwirir_p = \sum_i w_i \cdot r_i
  4. El VaR es el cuantil α\alpha de la distribución de pérdidas de cartera simulada

Conditional Value-at-Risk (CVaR / Expected Shortfall)

El CVaR es la pérdida esperada dado que la pérdida excede el VaR:

CVaRα=E[LLVaRα]=11αα1VaRudu\text{CVaR}_\alpha = E[L \mid L \geq \text{VaR}_\alpha] = \frac{1}{1-\alpha}\int_{\alpha}^{1}\text{VaR}_u\, du

El CVaR es coherente (satisface la subaditividad), lo que lo hace superior al VaR para la optimización de carteras. La estimación por Monte Carlo es directa: se promedian las pérdidas que exceden el VaR.

Por qué el riesgo basado en cópulas supera al riesgo basado en correlación

Consideremos dos carteras con correlaciones por pares idénticas de 0,5:

  • Cartera A: dependencia de cópula gaussiana (sin dependencia de cola)
  • Cartera B: dependencia de cópula de Clayton (θ=2\theta = 2, λL=0,71\lambda_L = 0,71)

Al nivel de confianza del 99%, la Cartera B tendrá un VaR y un CVaR significativamente más altos porque la cópula de Clayton modela correctamente la tendencia de los activos a caer juntos. La cópula gaussiana subestima este riesgo al asumir que los co-movimientos extremos son extremadamente raros.

En estudios empíricos sobre carteras cripto, la diferencia en el CVaR al 99% entre los modelos gaussiano y de cópula vine puede superar el 30-40%, lo que significa que los modelos basados en correlación pueden subestimar el riesgo de cola en un tercio o más.

Implementación: Python con pyvinecopulib

A continuación se presenta un pipeline completo para ajustar una cópula vine a rendimientos cripto y estimar el VaR/CVaR de la cartera.

Paso 1: Preparación de datos y ajuste de las marginales

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

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()

Paso 2: Transformada integral de probabilidad

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:
            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:
            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:
            u[i] = np.mean(residuals <= x)

    u = np.clip(u, 1e-6, 1 - 1e-6)
    return u

U = np.column_stack([
    semi_parametric_pit(residuals_df[sym].values)
    for sym in symbols
])

Paso 3: Ajustar la cópula vine

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
)

vine = pv.Vinecop(U, controls=controls)

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

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})")

Paso 4: VaR y 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.
    """
    U_sim = vine.simulate(n=n_sim, seeds=[seed])

    returns_sim = np.zeros((n_sim, len(symbols)))
    for j, sym in enumerate(symbols):
        model = garch_models[sym]
        forecasts = model.forecast(horizon=1)
        mu = forecasts.mean.iloc[-1, 0] / 100  # unscale
        sigma = np.sqrt(forecasts.variance.iloc[-1, 0]) / 100

        nu = model.params.get("nu", 5)
        z_sim = stats.t.ppf(U_sim[:, j], df=nu)
        returns_sim[:, j] = mu + sigma * z_sim

    weights = np.array(weights)
    portfolio_returns = returns_sim @ weights

    losses = -portfolio_returns

    var = np.quantile(losses, alpha)

    cvar = np.mean(losses[losses >= var])

    return var, cvar, portfolio_returns

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}%")

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")

Paso 5: Análisis de dependencia de cola

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)
        u_pair = pair.simulate(n=100_000, seeds=[42])
        q = 0.01  # 1st percentile

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

        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))

Un resultado típico para una cartera cripto podría verse así:

Pair Family λL\lambda_L λU\lambda_U
BTC-ETH student 0.22 0.22
BTC-SOL clayton 0.35 0.00
BTC-AVAX bb7 0.28 0.12
BTC-DOT student 0.18 0.18
BTC-LINK clayton 0.31 0.00
BTC-MATIC frank 0.00 0.00
BTC-ATOM gumbel 0.00 0.15

Nótese cómo cada par puede tener una estructura de dependencia completamente diferente. BTC-SOL muestra una fuerte dependencia de cola inferior (Clayton) con dependencia de cola superior nula: caen juntos, pero no necesariamente suben juntos. BTC-MATIC no muestra ninguna dependencia de cola (Frank), lo que sugiere cierto beneficio de diversificación incluso en los extremos.

Backtesting del modelo VaR con cópulas

Un modelo de VaR solo es útil si está bien calibrado. El backtest estándar calcula las violaciones del VaR — días en los que la pérdida real superó el VaR predicho — y prueba si la tasa de violaciones coincide con la tasa 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):
        window_returns = returns.iloc[t-window:t]

        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
            u = pv.to_pseudo_obs(std_resid.values.reshape(-1, 1))
            U_window[:len(u), j] = u.ravel()

        U_clean = U_window[~np.any(U_window == 0, axis=1)]
        vine_t = pv.Vinecop(U_clean, controls=controls)

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

        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

Un modelo de VaR al 99% bien calibrado debería tener una tasa de violaciones cercana al 1%. Si la tasa es significativamente más alta, el modelo subestima el riesgo. Si es significativamente más baja, es demasiado conservador.

Consideraciones prácticas

Costo computacional

El ajuste de la cópula vine es O(d2n)O(d^2 \cdot n) por nivel de árbol. Para una cartera de 10 activos con ventanas móviles de 500 días, un backtest completo con 50.000 simulaciones de Monte Carlo por paso puede tardar horas. Estrategias para gestionar esto:

  • Vines truncados: fijar trunc_lvl=3 o trunc_lvl=4 — los árboles superiores capturan dependencias condicionales más débiles que contribuyen menos al riesgo
  • Recuento reducido de simulaciones: 10.000-20.000 simulaciones suelen bastar para el VaR al 99%
  • Cómputo paralelo: los ajustes GARCH de cada activo son independientes y se pueden paralelizar
  • Caché de modelos: reajustar la cópula semanalmente en lugar de diariamente, actualizando solo los pronósticos GARCH

Conciencia de régimen

Los mercados cripto muestran regímenes distintos (alcista, bajista, lateral, eventos de alta volatilidad). Una única cópula vine ajustada sobre toda la muestra puede no capturar la dependencia dependiente del régimen. Considerar:

  • Ventanas móviles de 250-500 días
  • Cópulas con cambio de régimen, donde los parámetros de la cópula dependen de un estado de Markov oculto
  • Observaciones ponderadas exponencialmente, que dan más peso a los datos recientes

Errores comunes

  1. Olvidar el PIT. Introducir rendimientos brutos directamente en la cópula en lugar de observaciones pseudo-uniformes producirá resultados sin sentido. Siempre transformar primero a márgenes uniformes.
  2. Sobreajuste con demasiadas familias. Incluir todas las familias bivariadas posibles en el conjunto de selección puede provocar sobreajuste, especialmente con muestras cortas. Usar BIC para la selección del modelo y considerar restringirse a 4-5 familias.
  3. Ignorar la dependencia serial. Las cópulas modelan la dependencia transversal en un único punto temporal. Si se omite el paso GARCH y se introducen rendimientos autocorrelacionados en la cópula, la dependencia estimada quedará contaminada por efectos seriales.
  4. Cópulas estáticas en mercados dinámicos. Una cópula ajustada con datos del mercado alcista de 2021 estará mal calibrada para un desplome en 2022. Usar siempre ventanas móviles o expansivas.

Conclusión

Los modelos de cópula, en particular las cópulas vine, proporcionan un marco matemáticamente riguroso para modelar el riesgo conjunto de las carteras cripto que va mucho más allá de lo que puede capturar la correlación lineal. Las ventajas clave:

  • Modelización separada de marginales y dependencia mediante el teorema de Sklar
  • Dependencia de cola flexible mediante la selección apropiada de la familia de cópula (Clayton para el contagio en caídas, Gumbel para el co-movimiento en rallies, cópula t para colas simétricas)
  • Escalabilidad en alta dimensión mediante la descomposición en cópula vine, donde cada par de activos obtiene su propia cópula bivariada
  • Estimación precisa del VaR/CVaR que tiene en cuenta la dependencia no lineal y asimétrica — crucial para la gestión de riesgos en un mercado donde "todo se desploma junto" es la norma, no la excepción

El pipeline GARCH-EVT-Cópula es ahora el enfoque estándar en los fondos de cobertura cuantitativos y las mesas de riesgo enfocadas en cripto. Con bibliotecas como pyvinecopulib, la barrera de implementación es lo suficientemente baja como para que cualquier trader sistemático pueda integrar la modelización de riesgo basada en cópulas en su flujo de gestión de carteras.

El código de este artículo ofrece un punto de partida funcional. Para uso en producción, se añadiría una validación cruzada adecuada para la selección del orden GARCH, modelos marginales más sofisticados (por ejemplo, EGARCH con efectos de apalancamiento, o medidas de volatilidad realizada usando datos intradía), y pruebas de estrés bajo parámetros de cópula hipotéticos calibrados con episodios de crisis históricos.


Referencias

  • 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.
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Mantente a la vanguardia

Suscríbete a nuestro boletín para recibir información exclusiva sobre trading con IA, análisis de mercado y actualizaciones de la plataforma.

Respetamos tu privacidad. Puedes darte de baja en cualquier momento.