← Volver a los artículos
July 12, 2026
5 min de lectura

DCC-GARCH: Correlaciones Dinámicas para Pares y Riesgo de Cartera

#volatility
#GARCH
#DCC
#correlation
#portfolio
#pairs-trading
#crypto

Pregúntale a la mayoría de las mesas de trading de cripto por la correlación entre BTC y ETH y obtendrás un único número — 0.8, quizás 0.75 — calculado sobre alguna ventana que nadie recuerda haber elegido. Ese número es una mentira, o al menos una simplificación peligrosa. La correlación muestral es un promedio sobre un período durante el cual la verdadera estructura de dependencia se movía constantemente. En mercados tranquilos, BTC y ETH se separan lo suficiente como para hacer que un par market-neutral parezca atractivo. En una cascada de liquidaciones se acoplan entre sí y con todo lo demás, y la diversificación por la que pagaste se evapora en el momento exacto en que la necesitas.

Este no es un efecto sutil. Toma cualquier caída de 2022 — el colapso de LUNA en mayo, el desenlace de 3AC en junio, la implosión de FTX en noviembre — y verás la correlación promedio por pares entre los 20 tokens principales marchar del rango 0.4-0.6 hacia 0.9+ en cuestión de días. La correlación no es una constante que ocasionalmente se estima mal; es una serie temporal con su propia dinámica, su propia agrupación (clustering) y sus propios regímenes. Tratarla como un escalar es el equivalente multivariado de asumir volatilidad constante — un error que ya nos ocupamos de desmontar para un solo activo en la Parte 1 de esta serie.

Este artículo es la Parte 3 de una serie de cuatro partes sobre volatilidad. La Parte 1 construyó GARCH(1,1) univariado con la librería arch y mostró cómo la volatilidad se agrupa y revierte a la media. La Parte 2 añadió asimetría (GJR-GARCH, EGARCH) e innovaciones Student-t para capturar el efecto apalancamiento y las colas pesadas. Aquí pasamos a lo multivariado: modelamos toda la matriz de covarianza condicional HtH_t a medida que evoluciona, usando el modelo de Correlación Condicional Dinámica (DCC) de Engle. Eso nos da dos cosas que una correlación escalar nunca puede — un ratio de cobertura dinámico para pairs trading, y una varianza de cartera honesta y variable en el tiempo para la asignación basada en riesgo. La Parte 4 cierra la serie con un backtest de targeting de volatilidad que conecta los pronósticos univariados y multivariados en una regla de dimensionamiento de posiciones.

Asumimos que has leído las Partes 1 y 2, así que no volveremos a derivar el GARCH univariado. Si buscas el comportamiento conjunto de cola — la probabilidad de que dos activos rompan su cuantil del 1% juntos — eso es una cuestión de cópulas, y la cubrimos en Modelos de Cópula para Riesgo Conjunto. DCC y las cópulas son complementarios: la cópula te da una estructura de dependencia de cola estática pero flexible, mientras que DCC te da una serie temporal manejable de toda la matriz de correlación. Este artículo trata sobre lo segundo.

Por Qué la Correlación Estática Falla en Cripto

Antes de entrar en la maquinaria, seamos precisos sobre qué falla. Una única correlación muestral ρ^\hat{\rho} sobre una ventana [tw,t][t-w, t] estima

ρ^ij=s(ri,srˉi)(rj,srˉj)s(ri,srˉi)2s(rj,srˉj)2\hat{\rho}_{ij} = \frac{\sum_{s} (r_{i,s} - \bar{r}_i)(r_{j,s} - \bar{r}_j)}{\sqrt{\sum_s (r_{i,s}-\bar{r}_i)^2}\sqrt{\sum_s (r_{j,s}-\bar{r}_j)^2}}

Esto conlleva tres supuestos implícitos, todos falsos para cripto:

  1. Estacionariedad de la dependencia. La ventana tiene un único ρ\rho verdadero. En realidad la dependencia tiene regímenes — un régimen de mercado tranquilo cerca de 0.5 y un régimen de estrés cerca de 0.95 — y ρ^\hat{\rho} los mezcla en un punto medio sin sentido.
  2. Volatilidad marginal constante. La correlación de Pearson es una covarianza normalizada. Si σi,t\sigma_{i,t} y σj,t\sigma_{j,t} se están moviendo (lo están — esa es toda la premisa de las Partes 1 y 2), entonces incluso una covarianza constante produce una correlación variable en el tiempo, y viceversa. No puedes separar las dos sin un modelo de volatilidad debajo.
  3. Simetría a través de la dirección del mercado. La correlación sube más en caídas que en subidas. Este es el primo multivariado del efecto apalancamiento. Una ventana móvil no puede expresarlo sin volverse tan corta que sea puro ruido.

La solución de ventana móvil — recalcular ρ^\hat{\rho} sobre los últimos 30 o 60 días — cambia un problema por otro. Las ventanas cortas son reactivas pero ruidosas y llegan tarde a la ruptura real; las ventanas largas son estables pero obsoletas. Peor aún, una matriz de correlación móvil sobre dd activos no está garantizada de permanecer semidefinida positiva una vez que empiezas a recortarla o parchearla, lo cual rompe todos los optimizadores posteriores. Queremos un modelo que (a) esté impulsado por un proceso de volatilidad propio por activo, (b) produzca una matriz de correlación válida en cada paso por construcción, y (c) tenga parámetros que podamos estimar por máxima verosimilitud en lugar de elegir una longitud de ventana al azar. Ese modelo es DCC-GARCH.

El Problema Multivariado: La Matriz de Covarianza Condicional

Sea rtRdr_t \in \mathbb{R}^d el vector de retornos para dd activos en el instante tt, con media condicional μt\mu_t (a menudo solo una constante o un pequeño término AR) y residuo ϵt=rtμt\epsilon_t = r_t - \mu_t. Asumimos

ϵtFt1D(0,Ht)\epsilon_t \mid \mathcal{F}_{t-1} \sim \mathcal{D}(0, H_t)

donde HtH_t es la matriz de covarianza condicional d×dd \times d dado el conjunto de información Ft1\mathcal{F}_{t-1}, y D\mathcal{D} es alguna distribución condicional (gaussiana o, mejor para cripto, Student-t multivariada). Todo en el modelado de volatilidad multivariada es una respuesta diferente a una sola pregunta: ¿cómo parametrizas la dinámica de HtH_t para que se mantenga simétrica definida positiva en cada paso sin una explosión de parámetros?

Dos respuestas clásicas muestran por qué el problema es difícil.

VECH

El modelo VECH (Bollerslev, Engle, Wooldridge 1988) escribe la semivectorización de HtH_t como una función lineal de los residuos al cuadrado pasados y las covarianzas pasadas:

vech(Ht)=c+Avech(ϵt1ϵt1)+Bvech(Ht1)\mathrm{vech}(H_t) = c + A\,\mathrm{vech}(\epsilon_{t-1}\epsilon_{t-1}') + B\,\mathrm{vech}(H_{t-1})

donde vech()\mathrm{vech}(\cdot) apila el triángulo inferior de una matriz simétrica en un vector de longitud d(d+1)/2d(d+1)/2. Esto es máximamente general — cada varianza y covarianza depende de cada varianza y covarianza pasada — y máximamente inútil más allá de d=3d=3. Para dd activos, AA y BB son cada una d(d+1)2×d(d+1)2\frac{d(d+1)}{2} \times \frac{d(d+1)}{2}. Con d=5d=5 eso son dos matrices de 15×1515\times 15, aproximadamente 450 parámetros, más restricciones de definición positiva que son dolorosas incluso de expresar. La superficie de verosimilitud es un pantano.

BEKK

El modelo BEKK (Engle & Kroner 1995) garantiza la definición positiva por construcción usando una forma cuadrática:

Ht=CC+Aϵt1ϵt1A+BHt1BH_t = C'C + A'\,\epsilon_{t-1}\epsilon_{t-1}'\,A + B'\,H_{t-1}\,B

con CC triangular superior. Debido a que cada término es una forma cuadrática, Ht0H_t \succ 0 automáticamente siempre que CC0C'C \succ 0. BEKK es más parsimonioso que VECH pero aún escala como O(d2)O(d^2) parámetros — las matrices AA y BB son d×dd \times d cada una. Para d=10d=10 estás estimando del orden de 200+ parámetros conjuntamente por MLE, sobre datos diarios de cripto ruidosos, sin garantía de que el optimizador converja a algo con sentido. En la práctica, el BEKK completo se confina a d4d \le 4, e incluso entonces se usan las restricciones "diagonal" o "escalar" que descartan la mayor parte de la dinámica cruzada.

Esto es la maldición de la dimensionalidad para el GARCH multivariado: el número de parámetros crece cuadráticamente, pero la cantidad de información en los datos no. Te quedas sin grados de libertad mucho antes de quedarte sin activos que te interesen. Cualquier libro de cripto con 10-30 tokens está completamente fuera del alcance de VECH o BEKK.

La salida, debida a Engle, es dejar de intentar modelar HtH_t directamente y en su lugar factorizarla en piezas que ya sabemos estimar de forma económica.

El DCC de Engle (2002): La Descomposición en Dos Pasos

El modelo de Correlación Condicional Constante (CCC) de Bollerslev (1990) fue la primera factorización parsimoniosa. Escribe

Ht=DtRDtH_t = D_t\, R\, D_t

donde Dt=diag(σ1,t,,σd,t)D_t = \mathrm{diag}(\sigma_{1,t}, \ldots, \sigma_{d,t}) es la matriz diagonal de desviaciones estándar condicionales — un GARCH univariado por activo — y RR es una matriz de correlación constante. Esta es una gran simplificación: ajustas dd modelos GARCH univariados independientes, luego estimas una única matriz de correlación muestral de los residuos estandarizados. La definición positiva es automática siempre que RR sea una matriz de correlación válida y todos los σi,t>0\sigma_{i,t} > 0.

El problema de CCC está ahí mismo en el nombre — la correlación es constante, que es exactamente el supuesto que rechazamos al abrir este artículo. La Correlación Condicional Dinámica de Engle (2002) mantiene la hermosa factorización de CCC pero deja que la matriz de correlación respire:

Ht=DtRtDtH_t = D_t\, R_t\, D_t

Ahora RtR_t es variable en el tiempo. El genio está en que las volatilidades y las correlaciones se estiman en dos pasos separados, así que nunca enfrentamos la optimización conjunta completa de O(d2)O(d^2).

Paso 1: GARCH univariado por activo

Para cada activo ii, ajusta un modelo GARCH univariado exactamente como en las Partes 1 y 2 — GARCH(1,1), GJR-GARCH, o EGARCH con innovaciones Student-t, lo que mejor ajuste para esa serie. Esto da varianzas condicionales σi,t2\sigma_{i,t}^2 y por lo tanto Dt=diag(σ1,t,,σd,t)D_t = \mathrm{diag}(\sigma_{1,t}, \ldots, \sigma_{d,t}).

De los modelos ajustados extraemos los residuos estandarizados:

zi,t=ϵi,tσi,tz_{i,t} = \frac{\epsilon_{i,t}}{\sigma_{i,t}}

Por construcción cada zi,tz_{i,t} tiene (aproximadamente) varianza condicional unitaria. Apílalos en un vector zt=(z1,t,,zd,t)z_t = (z_{1,t}, \ldots, z_{d,t})'. Estos residuos estandarizados son la materia prima del paso de correlación — se les ha extraído su dinámica de volatilidad individual, así que cualquier co-movimiento que quede es dependencia pura, no un artefacto de volatilidad. (Esta es la misma lógica de estilo PIT que usa el artículo de cópulas antes de ajustar los márgenes; aquí nos detenemos en la estandarización en lugar de llegar hasta las uniformes.)

Paso 2: La recursión de correlación DCC

Modelamos un proceso auxiliar QtQ_t, una matriz simétrica definida positiva d×dd \times d, con una recursión tipo GARCH impulsada por los productos externos de los residuos estandarizados:

Qt=(1ab)Qˉ+azt1zt1+bQt1Q_t = (1 - a - b)\,\bar{Q} + a\, z_{t-1} z_{t-1}' + b\, Q_{t-1}

donde:

  • Qˉ=1Tt=1Tztzt\bar{Q} = \frac{1}{T}\sum_{t=1}^{T} z_t z_t' es la matriz de correlación incondicional de los residuos estandarizados (esto es correlation targeting — más sobre ello a continuación),
  • a0a \ge 0 gobierna cuán fuertemente el shock de hoy zt1zt1z_{t-1}z_{t-1}' tira de la correlación,
  • b0b \ge 0 gobierna la persistencia — cuánto de Qt1Q_{t-1} de ayer se traslada hacia adelante,
  • y la restricción de reversión a la media es a+b<1a + b < 1 (con a,b>0a, b > 0), directamente análoga a α+β<1\alpha + \beta < 1 en el GARCH univariado.

Nota que la estructura es idéntica a una recursión GARCH(1,1) escalar, pero sobre matrices: un ancla de largo plazo Qˉ\bar{Q}, un término de shock, y un término de persistencia. Debido a que es una combinación convexa de matrices semidefinidas positivas (Qˉ\bar{Q}, el producto externo de rango 1, y el Qt1Q_{t-1} anterior), QtQ_t permanece definida positiva siempre que Qˉ0\bar{Q} \succ 0 y los pesos sean no negativos. Esto es lo que nos compra matrices de covarianza válidas garantizadas de forma gratuita.

QtQ_t es casi una matriz de correlación pero no del todo — su diagonal no es exactamente 1. Así que la normalizamos:

Rt=(diag(Qt))1/2Qt(diag(Qt))1/2R_t = \left(\mathrm{diag}(Q_t)\right)^{-1/2}\, Q_t\, \left(\mathrm{diag}(Q_t)\right)^{-1/2}

Elemento a elemento, la correlación condicional entre los activos ii y jj es

ρij,t=qij,tqii,tqjj,t\rho_{ij,t} = \frac{q_{ij,t}}{\sqrt{q_{ii,t}\, q_{jj,t}}}

Este RtR_t es una matriz de correlación propia — diagonal unitaria, elementos fuera de la diagonal en [1,1][-1,1], definida positiva — en cada instante, por construcción. Reensamblamos la covarianza condicional completa:

Ht=DtRtDt,hij,t=ρij,tσi,tσj,tH_t = D_t\, R_t\, D_t, \qquad h_{ij,t} = \rho_{ij,t}\,\sigma_{i,t}\,\sigma_{j,t}

Esa última forma elemento a elemento es la que usarás constantemente: la covarianza condicional de dos activos es su correlación dinámica multiplicada por cada una de sus volatilidades dinámicas. Cada ingrediente en el lado derecho es variable en el tiempo y proviene de un modelo que puedes estimar.

Todo el modelo tiene solo dos parámetros de correlación, aa y bb, sin importar si d=2d = 2 o d=50d = 50. El lado de la volatilidad escala linealmente (un GARCH univariado por activo, cada uno con ~4-5 parámetros, todos ajustados de forma independiente y trivialmente paralelizable). Esto es por qué DCC escala donde BEKK y VECH no pueden: la maldición de la dimensionalidad se confina a Qˉ\bar{Q}, que se fija como objetivo (targeting, insertada como una estimación muestral) en lugar de optimizarse.

La restricción escalar y su costo

Los escalares a,ba, b significan que cada par de activos comparte la misma dinámica de correlación — la misma velocidad de ajuste y la misma persistencia. La correlación BTC-ETH y la correlación DOGE-SHIB se mueven al mismo ritmo aunque sus economías difieran. Este es el precio de la manejabilidad, y usualmente es un precio aceptable. Las generalizaciones (DCC Generalizado con matrices A,BA, B; el DCC asimétrico de Cappiello-Engle-Sheppard) lo relajan a costa de parámetros y estabilidad de estimación. Mencionamos aDCC más abajo.

La Cuasi-Log-Verosimilitud del DCC

Para estimar aa y bb necesitamos la verosimilitud. El resultado clave de Engle es que la log-verosimilitud gaussiana se separa en una parte de volatilidad y una parte de correlación, que es lo que justifica el estimador en dos pasos. Asumiendo ϵtFt1N(0,Ht)\epsilon_t \mid \mathcal{F}_{t-1} \sim \mathcal{N}(0, H_t), la contribución a la log-verosimilitud en el instante tt es

t=12(dlog(2π)+logHt+ϵtHt1ϵt)\ell_t = -\frac{1}{2}\left( d\log(2\pi) + \log|H_t| + \epsilon_t' H_t^{-1} \epsilon_t \right)

Sustituye Ht=DtRtDtH_t = D_t R_t D_t. Entonces Ht=Dt2Rt|H_t| = |D_t|^2 |R_t| y Ht1=Dt1Rt1Dt1H_t^{-1} = D_t^{-1} R_t^{-1} D_t^{-1}, y usando zt=Dt1ϵtz_t = D_t^{-1}\epsilon_t:

t=12(dlog(2π)+2logDt+logRt+ztRt1zt)\ell_t = -\frac{1}{2}\left( d\log(2\pi) + 2\log|D_t| + \log|R_t| + z_t' R_t^{-1} z_t \right)

Ahora la dividimos sumando y restando ztztz_t'z_t:

t=12(dlog(2π)+2logDt+ϵtDt2ϵt)parte de volatilidad   tV  12(logRt+ztRt1ztztzt)parte de correlacioˊ  tC\ell_t = \underbrace{-\frac{1}{2}\left( d\log(2\pi) + 2\log|D_t| + \epsilon_t' D_t^{-2}\epsilon_t \right)}_{\text{parte de volatilidad }\;\ell_t^{V}} \;\underbrace{-\frac{1}{2}\left( \log|R_t| + z_t' R_t^{-1} z_t - z_t' z_t \right)}_{\text{parte de correlación }\;\ell_t^{C}}

La parte de volatilidad tV\ell_t^V depende solo de los parámetros GARCH univariados (a través de DtD_t) — maximizarla es exactamente ajustar dd modelos GARCH univariados independientes, que hicimos en el Paso 1. La parte de correlación tC\ell_t^C depende de aa y bb (a través de RtR_t), dados los residuos estandarizados del Paso 1. Así que en el Paso 2 maximizamos solo

LC(a,b)=12t=1T(logRt+ztRt1zt)\mathcal{L}^C(a, b) = -\frac{1}{2}\sum_{t=1}^{T}\left( \log|R_t| + z_t' R_t^{-1} z_t \right)

(el término ztztz_t'z_t no depende de a,ba, b, así que lo descartamos). Esta es una optimización de dos parámetros sin importar cuántos activos haya — ese es el punto entero. Se llama cuasi-verosimilitud porque el estimador en dos pasos es consistente pero no completamente eficiente; los errores estándar necesitan una corrección (Engle & Sheppard 2001), pero para la generación de señales las estimaciones puntuales son lo que importa.

Para cripto, las innovaciones gaussianas subestiman el riesgo de cola. Sustituir por la verosimilitud Student-t multivariada es un cambio directo en t\ell_t (reemplazar el núcleo gaussiano con la densidad t-multivariada y añadir un parámetro de grados de libertad ν\nu). Mantenemos la cuasi-verosimilitud gaussiana en el estimador de abajo por claridad y señalamos dónde entra ν\nu — la estandarización de las Partes 1-2 ya usó innovaciones t en los márgenes, lo cual captura la mayor parte del beneficio de cola.

Implementación en Python

Un hecho contundente pero importante: la librería arch no hace GARCH multivariado ni DCC. arch es un motor univariado excelente (nos apoyamos en él para exactamente eso), pero no hay dcc_model en ella. Tus opciones prácticas son:

  1. Construir tu propio DCC sobre arch — ajustar modelos univariados con arch, extraer residuos estandarizados, implementar la recursión QQ y la cuasi-verosimilitud de correlación en NumPy/SciPy, y optimizar los dos escalares. Esto es lo que hacemos abajo. Son unas 60 líneas y completamente transparente.
  2. El paquete mgarch de PyPI — una implementación DCC-GARCH ligera en Python puro. Conveniente para un ajuste rápido, menos flexible si quieres márgenes GJR o innovaciones t cableadas con precisión.
  3. El rmgarch de R (Alexios Galanos) — la implementación de referencia. dccspec / dccfit soportan DCC, aDCC, GARCH-cópula, Student-t, y errores estándar propios. Si haces investigación seria de volatilidad multivariada, rmgarch (llamado desde Python vía rpy2 si es necesario) es el estándar de oro.

Construimos la opción 1 porque hace explícita cada pieza móvil y reutiliza las habilidades univariadas de las Partes 1-2.

Paso 1: Ajustar los márgenes GARCH univariados con arch

import numpy as np
import pandas as pd
from arch import arch_model
from scipy.optimize import minimize

def fetch_returns(symbols, start="2022-01-01", end="2025-12-31"):
    """
    Daily log returns for a list of crypto symbols.
    Replace with your data source (ccxt for 24/7 exchange data,
    yfinance for a quick sketch).
    """
    import yfinance as yf
    px = yf.download([f"{s}-USD" for s in symbols],
                     start=start, end=end)["Close"]
    px.columns = symbols
    rets = np.log(px / px.shift(1)).dropna()
    return rets

symbols = ["BTC", "ETH", "SOL", "BNB"]
returns = fetch_returns(symbols)

def fit_univariate(series, dist="t"):
    """
    Fit GJR-GARCH(1,1) with Student-t innovations (Part 2 model).
    Returns the fitted result and the *scaled* series it was fit on.
    We scale returns by 100 for the optimizer's numerical health,
    exactly as in Parts 1 and 2.
    """
    scaled = series * 100.0
    model = arch_model(scaled, mean="Constant",
                       vol="GARCH", p=1, o=1, q=1, dist=dist)
    res = model.fit(disp="off")
    return res

fits = {s: fit_univariate(returns[s]) for s in symbols}

Z = pd.DataFrame({s: fits[s].std_resid for s in symbols}).dropna()

Sigma = pd.DataFrame({s: fits[s].conditional_volatility
                      for s in symbols}).loc[Z.index]

print(Z.describe())

Una comprobación rápida de sanidad en los residuos estandarizados importa. Si alguna columna tiene una desviación estándar lejos de 1, o autocorrelación residual pesada en su cuadrado (Ljung-Box en zi,t2z_{i,t}^2), el margen univariado está mal especificado y el paso DCC heredará ese error. Corrige el margen primero — para eso estaba la Parte 2.

Paso 2: La recursión DCC y la cuasi-log-verosimilitud

def dcc_negloglik(params, Z):
    """
    Negative DCC quasi-log-likelihood (Gaussian) in (a, b).
    Z : (T, d) array of standardized residuals from Step 1.

    Implements:
        Q_t = (1-a-b) Qbar + a z_{t-1} z_{t-1}' + b Q_{t-1}
        R_t = diag(Q_t)^{-1/2} Q_t diag(Q_t)^{-1/2}
        LL  = -1/2 sum_t ( log|R_t| + z_t' R_t^{-1} z_t )
    with correlation targeting: Qbar = sample corr of Z.
    """
    a, b = params
    if a <= 0 or b <= 0 or a + b >= 1.0:
        return 1e10

    Z = np.asarray(Z)
    T, d = Z.shape
    Qbar = np.cov(Z, rowvar=False, bias=True)   # unconditional (targeted)
    dinv = np.diag(1.0 / np.sqrt(np.diag(Qbar)))
    Qbar = dinv @ Qbar @ dinv

    Q = Qbar.copy()          # initialize Q_1 at the target
    ll = 0.0
    for t in range(T):
        q_diag = np.sqrt(np.diag(Q))
        R = Q / np.outer(q_diag, q_diag)
        sign, logdet = np.linalg.slogdet(R)
        if sign <= 0:
            return 1e10
        z = Z[t]
        Rinv_z = np.linalg.solve(R, z)
        ll += -0.5 * (logdet + z @ Rinv_z)
        Q = (1 - a - b) * Qbar + a * np.outer(z, z) + b * Q
    return -ll

def fit_dcc(Z):
    """Estimate (a, b) by maximizing the DCC quasi-log-likelihood."""
    res = minimize(
        dcc_negloglik, x0=[0.03, 0.94], args=(np.asarray(Z),),
        method="L-BFGS-B",
        bounds=[(1e-6, 0.5), (1e-6, 0.999)],
    )
    a, b = res.x
    print(f"DCC estimates: a = {a:.4f}, b = {b:.4f}, a+b = {a+b:.4f}")
    print(f"log-likelihood: {-res.fun:.2f}")
    return a, b

a_hat, b_hat = fit_dcc(Z)

Ejecutar esto sobre un libro BTC/ETH/SOL/BNB durante varios años de datos diarios produce una salida con esta forma (los números de abajo son ilustrativos, no de un experimento fechado específico — ejecútalo sobre tus propios datos):

DCC estimates: a = 0.0287, b = 0.9401, a+b = 0.9688
log-likelihood: -3812.44

Cómo interpretarlo:

  • a=0.029a = 0.029 es pequeño — la matriz de correlación no se sacude con el shock de un solo día. Cada día empuja RtR_t hacia el producto externo zt1zt1z_{t-1}z_{t-1}' en solo ~3%.
  • b=0.940b = 0.940 es grande — las correlaciones son altamente persistentes. Una vez que el libro se acopla en un evento de estrés, permanece acoplado durante un tiempo, decayendo de vuelta hacia Qˉ\bar{Q} lentamente. Esto coincide con la experiencia vivida de las caídas en cripto: las correlaciones no se recuperan en el momento en que el precio se estabiliza.
  • a+b=0.969<1a + b = 0.969 < 1 confirma la reversión a la media. El proceso de correlación tiene un nivel de largo plazo estacionario (Qˉ\bar{Q}) al que retorna, con una vida media de aproximadamente log(0.5)/log(a+b)22\log(0.5)/\log(a+b) \approx 22 días. Si alguna vez estimas a+ba + b esencialmente igual a 1, el proceso de correlación es integrado — no tiene ancla de largo plazo, usualmente síntoma de una ruptura estructural dentro de tu muestra que el modelo está absorbiendo como persistencia infinita.

La persistencia casi unitaria y la carga de shock diminuta es la huella dactilar canónica del DCC en todas las clases de activos, y cripto no es la excepción. También es por qué una correlación móvil de 30 días es un sustituto tan pobre: una ventana móvil asume implícitamente valores de aa y bb que no coinciden en absoluto con esta estructura de decaimiento.

Algunas notas de implementación que ahorran tiempo real de depuración:

  • Inicialización. Comenzar en [0.03, 0.94] refleja la estimación típica de cripto: aa pequeño (las correlaciones reaccionan a shocks pero no violentamente), bb grande (las correlaciones son persistentes). Si tu optimizador deriva hacia a+b1a+b \to 1, el proceso de correlación es integrado — usualmente señal de una ruptura estructural en la muestra (un cambio de régimen que el modelo se esfuerza por ajustar como persistencia).
  • Convención de timing. Dentro del bucle evaluamos RtR_t contra ztz_t y luego actualizamos QQ con ztztz_t z_t' para el siguiente paso. Esto mantiene RtR_t como una función de la información hasta t1t-1 solamente — sin look-ahead. Equivocarse en este desfase de uno es el bug más común de DCC, e infla silenciosamente el ajuste dentro de la muestra.
  • Correlation targeting. Insertamos Qˉ\bar{Q} como la correlación muestral en lugar de estimarla. Esto es lo que hace que la optimización sea bidimensional. El costo es que Qˉ\bar{Q} usa la muestra completa, así que en un walk-forward estricto debes reestimarla solo sobre la ventana de entrenamiento (ver más abajo).

Paso 3: Reconstruir las trayectorias de correlación y covarianza

Una vez fijados a,ba, b, ejecuta la recursión una vez más, esta vez almacenando la trayectoria completa de RtR_t (y HtH_t) para que las estrategias posteriores puedan usarla.

def dcc_filter(Z, Sigma, a, b):
    """
    Run the DCC recursion with fixed (a,b) and return the full paths:
      R_path : (T, d, d) conditional correlation matrices
      H_path : (T, d, d) conditional covariance matrices (scaled units)
    Sigma : (T, d) conditional volatilities aligned with Z.
    """
    Z = np.asarray(Z); Sig = np.asarray(Sigma)
    T, d = Z.shape
    Qbar = np.cov(Z, rowvar=False, bias=True)
    dinv = np.diag(1.0 / np.sqrt(np.diag(Qbar)))
    Qbar = dinv @ Qbar @ dinv

    Q = Qbar.copy()
    R_path = np.empty((T, d, d))
    H_path = np.empty((T, d, d))
    for t in range(T):
        q_diag = np.sqrt(np.diag(Q))
        R = Q / np.outer(q_diag, q_diag)
        R_path[t] = R
        Dt = np.diag(Sig[t])          # D_t = diag(sigma_i,t)
        H_path[t] = Dt @ R @ Dt       # H_t = D_t R_t D_t
        z = Z[t]
        Q = (1 - a - b) * Qbar + a * np.outer(z, z) + b * Q
    return R_path, H_path

R_path, H_path = dcc_filter(Z, Sigma, a_hat, b_hat)

i, j = symbols.index("BTC"), symbols.index("ETH")
rho_btc_eth = pd.Series(R_path[:, i, j], index=Z.index, name="rho_BTC_ETH")
print(rho_btc_eth.describe())
print("min/max correlation:", rho_btc_eth.min().round(3),
      rho_btc_eth.max().round(3))

La serie rho_btc_eth es el resultado de todo el ejercicio: en lugar de un número, ahora tienes una correlación diaria que puedes graficar, umbralizar, o alimentar a una estrategia. En datos reales de cripto típicamente la verás variar de aproximadamente 0.5 en tramos tranquilos a más de 0.9 durante el estrés — exactamente el rango que una única correlación muestral promedia y borra.

Pronóstico un paso adelante

Para trading en vivo necesitas el Ht+1H_{t+1} del siguiente período a partir de la información disponible ahora. El lado de la volatilidad viene del pronóstico de un paso de cada modelo arch; el lado de la correlación es un giro más de la recursión:

def dcc_forecast_next(Z, a, b, fits, symbols):
    """One-step-ahead H_{t+1} using info through the last observation."""
    Z = np.asarray(Z); T, d = Z.shape
    Qbar = np.cov(Z, rowvar=False, bias=True)
    dinv = np.diag(1.0 / np.sqrt(np.diag(Qbar)))
    Qbar = dinv @ Qbar @ dinv

    Q = Qbar.copy()
    for t in range(T):
        z = Z[t]
        Q = (1 - a - b) * Qbar + a * np.outer(z, z) + b * Q
    q_diag = np.sqrt(np.diag(Q))
    R_next = Q / np.outer(q_diag, q_diag)

    sig_next = np.array([
        np.sqrt(fits[s].forecast(horizon=1, reindex=False)
                .variance.iloc[-1, 0])
        for s in symbols
    ])
    Dt = np.diag(sig_next)
    H_next = Dt @ R_next @ Dt
    return R_next, H_next, sig_next

R_next, H_next, sig_next = dcc_forecast_next(Z, a_hat, b_hat, fits, symbols)

Recuerda que todo está en unidades escaladas (×100) porque ajustamos arch sobre series * 100. Divide las volatilidades entre 100 (y las covarianzas entre 1002=10,000100^2 = 10{,}000) para volver a las unidades de retorno crudo antes de alimentar una estrategia. Mantener el escalado en orden es tedioso pero una fuente frecuente de bugs silenciosos.

Aplicación 1: Un Ratio de Cobertura Dinámico para Pairs Trading

El par market-neutral clásico — largo en un activo, corto en una cantidad ponderada por beta de otro — vive o muere por el ratio de cobertura β\beta. Estímalo por OLS estático sobre una ventana de entrenamiento y heredas exactamente el problema de correlación obsoleta del que trata todo este artículo: la cobertura que neutralizó la exposición al mercado el trimestre pasado está equivocada este trimestre.

DCC te da el ratio de cobertura como una serie temporal. La cobertura de varianza mínima de la exposición a ETH usando BTC es el coeficiente de regresión condicional

βt=Covt(rETH,rBTC)Vart(rBTC)=hETH,BTC,tσBTC,t2=ρETH,BTC,tσETH,tσBTC,t\beta_t = \frac{\mathrm{Cov}_t(r_{\text{ETH}}, r_{\text{BTC}})}{\mathrm{Var}_t(r_{\text{BTC}})} = \frac{h_{\text{ETH,BTC},t}}{\sigma_{\text{BTC},t}^2} = \rho_{\text{ETH,BTC},t}\,\frac{\sigma_{\text{ETH},t}}{\sigma_{\text{BTC},t}}

Cada término del lado derecho es una salida de DCC. El ratio de cobertura se mueve por dos razones distintas, y DCC las separa limpiamente: la correlación ρt\rho_t cambia (los activos se acoplan o desacoplan), y el ratio de volatilidad σETH,t/σBTC,t\sigma_{\text{ETH},t}/\sigma_{\text{BTC},t} cambia (un activo se vuelve relativamente más volátil). Una beta de OLS móvil mezcla ambos efectos con un retraso; DCC los atribuye.

def dynamic_hedge_ratio(R_path, Sigma, base="BTC", target="ETH",
                        symbols=symbols, index=Z.index):
    """
    beta_t to hedge `target` exposure with `base`:
        beta_t = rho_t * sigma_target,t / sigma_base,t
    (Scaling cancels in the ratio, so scaled units are fine here.)
    """
    i = symbols.index(target)
    j = symbols.index(base)
    rho = R_path[:, i, j]
    sig = np.asarray(Sigma)
    beta = rho * sig[:, i] / sig[:, j]
    return pd.Series(beta, index=index, name=f"beta_{target}_{base}")

beta_t = dynamic_hedge_ratio(R_path, Sigma)
print(beta_t.describe())

spread = (returns["ETH"] - beta_t.shift(1) * returns["BTC"]).dropna()

Alimenta spread a cualquier motor de pares que utilices. La cobertura dinámica no crea una ventaja por sí misma — hace que el spread que operas sea genuinamente market-neutral a través del tiempo, así que tu señal de reversión a la media no está contaminada por una exposición direccional a la deriva. Si construyes estrategias de pares, esto encaja directamente en los marcos de Arbitraje Estadístico y Pairs Trading en Cripto y el enfoque de distancia para pares, reemplazando su ratio de cobertura fijo. La propia serie de correlación es también una entrada más limpia para una señal de pares basada en correlación que cualquier ventana móvil — obtienes un ρt\rho_t suavizado y consistente con el modelo en lugar de una estimación ruidosa por ventanas.

Dos advertencias específicas para usar βt\beta_t en vivo. Primero, retrásalo (lag) — opera con βt1\beta_{t-1}, nunca con el βt\beta_t contemporáneo, o estarás mirando hacia adelante indebidamente. Segundo, un ratio de cobertura que da tumbos cada día genera rotación y comisiones; en el mercado 24/7 de cripto con costos de financiación en la pata corta, una cobertura sobrerreactiva puede sangrar más que la deriva que corrige. Suaviza βt\beta_t (una EWMA, o rebalancea la cobertura solo cuando se mueve más allá de una banda) y dimensiona todo con sensatez — el dimensionamiento de posiciones a partir de una señal ruidosa es su propia disciplina, cubierta en dimensionamiento con el criterio de Kelly.

Aplicación 2: Varianza de Cartera Variable en el Tiempo

Para una cartera con vector de pesos ww, la varianza condicional es

σp,t2=wHtw=ijwiwjρij,tσi,tσj,t\sigma_{p,t}^2 = w'\, H_t\, w = \sum_{i}\sum_{j} w_i w_j\, \rho_{ij,t}\,\sigma_{i,t}\,\sigma_{j,t}

Con una matriz de covarianza estática — el default de Markowitz — este número es una constante que calculaste una vez y finges que sigue siendo verdad. No lo es. El riesgo de la cartera respira con el mercado, y respira más fuerte precisamente cuando las correlaciones se disparan, porque en una caída tanto los términos σi,t\sigma_{i,t} como los términos ρij,t\rho_{ij,t} suben juntos y se multiplican. Una cartera que parecía tener 40% de volatilidad anualizada en mercados tranquilos puede estar corriendo al 80%+ en una semana de estrés, y una matriz de covarianza estática te dirá que nada cambió.

def portfolio_vol_path(H_path, weights, index=Z.index, unscale=1e4):
    """
    Conditional portfolio volatility sigma_{p,t} = sqrt(w' H_t w).
    H_path is in scaled (x100) units, so covariances carry a 100^2
    factor: divide by unscale=1e4 to return to raw-return variance.
    """
    w = np.asarray(weights)
    var_t = np.einsum("i,tij,j->t", w, H_path, w) / unscale
    return pd.Series(np.sqrt(var_t), index=index, name="port_vol")

w = np.array([0.4, 0.3, 0.2, 0.1])   # BTC, ETH, SOL, BNB
pv = portfolio_vol_path(H_path, w)
pv_annual = pv * np.sqrt(365)
print(pv_annual.describe())

Este σp,t\sigma_{p,t} variable en el tiempo es la entrada honesta que necesita la asignación basada en riesgo. La optimización media-varianza (Markowitz para cripto) con una covarianza muestral estática está optimizando contra una ficción; alimentarla con HtH_t (o su pronóstico de corto plazo) hace que la frontera eficiente misma sea variable en el tiempo y obliga al optimizador a reducir el riesgo hacia los regímenes de correlación creciente en lugar de después de ellos. Los enfoques de risk-parity y jerárquicos — el pipeline HRP + CVaR — son aún más sensibles a la entrada de covarianza, ya que toda la asignación es una función de la matriz de riesgo. Y si estás comparando asignadores cara a cara, como en algoritmos de optimización de cartera comparados, si consumen covarianza estática o dinámica suele ser un factor mayor del riesgo realizado que la elección del algoritmo.

La aplicación directa es el targeting de volatilidad de toda la cartera: elige una volatilidad anualizada objetivo σ\sigma^{*}, y escala la exposición bruta por σ/σp,t\sigma^{*} / \sigma_{p,t} cada período para que el riesgo realizado permanezca aproximadamente constante en lugar de dispararse en las crisis. Eso cierra el círculo con la Parte 4, que construye y hace backtest exactamente de esta regla.

Aplicación 3: Correlación como Señal de Régimen

Más allá de la cobertura y el dimensionamiento, la matriz de correlación lleva una señal macro. El escalar único más útil que puedes extraer es la correlación promedio por pares:

ρˉt=2d(d1)i<jρij,t\bar{\rho}_t = \frac{2}{d(d-1)}\sum_{i < j} \rho_{ij,t}

Cuando ρˉt\bar{\rho}_t sube en todo el libro, el mercado está entrando en un régimen de risk-off — las historias idiosincráticas dejan de importar y todo opera como una única beta macro. Esta es la huella cuantitativa de "las correlaciones van a 1 en una crisis". Tiende a liderar o coincidir con las caídas, lo que la convierte en un indicador de régimen utilizable en lugar de un post mórtem rezagado.

def avg_pairwise_corr(R_path, index=Z.index):
    T, d, _ = R_path.shape
    iu = np.triu_indices(d, k=1)          # upper-triangle off-diagonals
    avg = R_path[:, iu[0], iu[1]].mean(axis=1)
    return pd.Series(avg, index=index, name="avg_corr")

avg_corr = avg_pairwise_corr(R_path)

roll_q = avg_corr.rolling(365, min_periods=90).quantile(0.80)
risk_off = (avg_corr > roll_q)

Puedes usar risk_off como un regulador independiente (recortar la exposición bruta, ampliar los stops, retirar estrategias de reversión a la media que se ven arrolladas cuando todo tiende en la misma dirección) o como una característica en un modelo de régimen más formal. Encaja naturalmente con el enfoque de cadenas de Markov ocultas en detección de régimen con HMM: la correlación DCC promedio es una de las variables de observación más informativas que puedes entregarle a un HMM, porque es prospectiva sobre el estrés sistémico de una manera que los retornos rezagados no lo son. La advertencia honesta: la correlación creciente te dice que la diversificación está fallando, no hacia dónde va el mercado. Es una señal de riesgo, no una señal de alfa, y debe dimensionarse como tal — ver la asimetría de pérdidas y ganancias para entender por qué tratar un régimen de riesgo como una apuesta direccional termina mal.

Consideraciones Prácticas

Estabilidad de la estimación y número de activos

DCC escala mucho mejor que BEKK, pero "escalar" no es "gratis". La matriz de correlation targeting Qˉ\bar{Q} es una correlación muestral d×dd \times d, y las matrices de correlación muestral se vuelven mal condicionadas a medida que dd se acerca al número de observaciones. Con 4 activos y 1000 días estás bien. Con 60 activos y 400 días, Qˉ\bar{Q} es casi singular, su inversa en la verosimilitud explota, y RtR_t puede desviarse hacia lo no definido positivo por ruido numérico. Mitigaciones, aproximadamente en orden de frecuencia de necesidad:

  • Encoger (shrink) Qˉ\bar{Q} hacia un objetivo estructurado (Ledoit-Wolf, o hacia la identidad / una matriz de correlación constante) antes de ejecutar la recursión. Este es el arreglo de mayor apalancamiento para libros grandes.
  • Agrupar activos en un puñado de sectores (majors, L1s, DeFi, memes), modelar dentro y entre ellos a nivel sectorial, o ejecutar DCC sobre factores de componentes principales en lugar de activos crudos.
  • Preferir más datos sobre más activos. DCC tiene un apetito insaciable por un historial largo, limpio y contemporáneo — que es exactamente lo que no tienen los tokens jóvenes.

Realistamente, mantén el DCC directo a unas pocas docenas de activos como máximo. Para un universo grande, DCC sobre retornos de factores más residuos idiosincráticos es la solución estándar.

El correlation targeting es un atajo con un costo

Fijar como objetivo Qˉ\bar{Q} hace que la estimación sea manejable pero hornea la correlación incondicional de toda la muestra en cada RtR_t. En un backtest estricto esto es una fuga de look-ahead: tu matriz de correlación del día tt "conoce" la correlación promedio de toda la muestra, incluyendo el futuro. Para una evaluación honesta debes reestimar Qˉ\bar{Q} solo sobre la ventana de entrenamiento y mantenerla fija fuera de muestra, o desplazarla hacia adelante. Esta es la misma disciplina que impone todo el marco de optimización walk-forward, y es fácil de violar accidentalmente con un conveniente np.cov(Z) sobre el array completo — como hace nuestro código de enseñanza arriba. Corrígelo antes de confiar en un solo número de P&L.

Cadencia de reajuste y disciplina de look-ahead

No necesitas reoptimizar a,ba, b todos los días — son parámetros estables. Una cadencia de producción sensata:

  • Reestimar a,ba, b y los parámetros GARCH univariados semanal o mensualmente.
  • Ejecutar el filtro (actualizar QtQ_t, σi,t\sigma_{i,t}) cada período con los parámetros congelados para obtener RtR_t y HtH_t frescos. Filtrar es barato; ajustar no lo es.
  • Siempre pronosticar, nunca suavizar. Usa RtR_t construido con información hasta t1t-1 para operar en tt. La estructura de dos pasadas (ajustar sobre una ventana, luego filtrar hacia adelante) es lo que te mantiene honesto.

La brecha entre un backtest de DCC y el rendimiento en vivo es casi siempre una fuga de look-ahead — Qˉ\bar{Q} de muestra completa, βt\beta_t contemporáneo, o reajuste sobre datos que incluyen la operación que estás evaluando. La disciplina de hacer coincidir el backtest con las condiciones en vivo es su propio tema en paridad backtest-vivo, y DCC es un modelo que castiga la falta de rigor aquí más que la mayoría. Si, después de una evaluación walk-forward limpia, la correlación dinámica no añade nada sobre una simple estimación móvil para tu estrategia, eso es un resultado negativo real y publicable — la mentalidad en resultados negativos honestos aplica directamente.

DCC Asimétrico (aDCC)

Así como el efecto apalancamiento univariado (Parte 2) significa que las malas noticias elevan la volatilidad más que las buenas, las correlaciones suben más después de shocks negativos conjuntos que después de shocks positivos conjuntos. Cappiello, Engle & Sheppard (2006) capturan esto con DCC asimétrico, añadiendo un término impulsado por el producto externo de los residuos estandarizados de parte negativa zt=min(zt,0)z_t^- = \min(z_t, 0):

Qt=(QˉaQˉbQˉgNˉ)+azt1zt1+gzt1zt1+bQt1Q_t = (\bar{Q} - a\bar{Q} - b\bar{Q} - g\bar{N}) + a\, z_{t-1} z_{t-1}' + g\, z_{t-1}^- z_{t-1}^{-\prime} + b\, Q_{t-1}

donde Nˉ=1Ttztzt\bar{N} = \frac{1}{T}\sum_t z_t^- z_t^{-\prime} y g0g \ge 0 mide el impulso extra de correlación por movimientos conjuntos a la baja. Para cripto, donde la correlación de crash domina el riesgo, el término de asimetría es usualmente significativo y vale la pena el parámetro extra. rmgarch ajusta aDCC de fábrica (model="aDCC"); añadir el término ztz_t^- a nuestro estimador NumPy es un ejercicio directo.

Comparación: DCC Frente a las Alternativas

¿Dónde se sitúa DCC entre las formas de obtener una matriz de covarianza para un libro de cripto? El resumen honesto:

Enfoque Parámetros Escala hasta ¿ρ\rho variable en el tiempo? ¿Definición positiva garantizada? ¿Dependencia de cola?
Covarianza muestral / móvil 0 (longitud de ventana) cualquier dd groseramente (rezagada, ruidosa) no (necesita parcheo) no
EWMA (RiskMetrics) 1 (λ\lambda) cualquier dd sí (decaimiento único) no
CCC-GARCH dd márgenes + Qˉ\bar{Q} docenas no (RR constante) no
DCC-GARCH dd márgenes + 2 docenas no
aDCC-GARCH dd márgenes + 3 docenas sí, asimétrico parcial
BEKK O(d2)O(d^2) 4\le 4 sí (rico) no
VECH O(d4)O(d^4) 3\le 3 sí (el más rico) doloroso no
GARCH-cópula dd márgenes + cópula docenas (vines) cópula estática

Algunas lecturas de esta tabla:

  • EWMA es la línea base económica que todos deberían superar antes de afirmar que DCC ayuda. Es un caso especial de un parámetro en espíritu — un único decaimiento exponencial aplicado directamente a la covarianza — y para muchos libros es sorprendentemente difícil de mejorar fuera de muestra. Si DCC no supera a EWMA en un walk-forward limpio, usa EWMA.
  • CCC vs DCC es todo el punto de este artículo: misma factorización, pero CCC congela RR y DCC deja que se mueva. Los dos parámetros extra (a,ba, b) son toda la diferencia, y en cripto se ganan su lugar.
  • BEKK/VECH compran dinámicas más ricas — cada covarianza puede responder a cada shock pasado — pero el costo en parámetros los confina a libros diminutos. Para más de 4 activos no son una opción real.
  • GARCH-cópula es la única fila con un "sí" bajo dependencia de cola. Esa es la complementariedad de nuevo: DCC modela el centro dinámico de la distribución conjunta, las cópulas modelan sus colas estáticas. Si tu pregunta de riesgo es "qué pasa cuando todo se rompe a la vez", recurre al pipeline de cópulas; si es "cuál es mi ratio de cobertura / varianza de cartera ahora mismo", recurre a DCC.

El default práctico para una mesa sistemática de cripto: DCC (o aDCC) para ratios de cobertura y covarianza dinámica en el cuerpo, una capa de cópula para riesgo de cola y CVaR, y EWMA como la línea base de sanity-check que te mantiene honesto sobre si la maquinaria extra se está pagando a sí misma.

Limitaciones

  • Dinámica escalar. Un solo aa y un solo bb para todos los pares es una restricción fuerte. BTC-ETH y dos altcoins oscuras comparten la misma velocidad de ajuste. El DCC Generalizado relaja esto pero reintroduce la explosión de parámetros que DCC fue diseñado para evitar.
  • Pérdida de eficiencia por dos pasos. El estimador de cuasi-verosimilitud es consistente pero no completamente eficiente, y los errores estándar ingenuos son incorrectos. Usa la corrección de Engle-Sheppard si te importa la inferencia; para la generación de señales las estimaciones puntuales bastan.
  • Colas gaussianas por defecto. La cuasi-verosimilitud gaussiana simple subestima el riesgo de cola conjunto. Las innovaciones Student-t ayudan; para la dependencia de cola genuina (la probabilidad de movimientos extremos simultáneos), DCC es la herramienta equivocada y un modelo de cópula es el correcto. DCC te da el cuerpo dinámico de la correlación; las cópulas te dan la cola estática. Las mesas serias usan ambos.
  • La correlación no es causalidad, ni dirección. El aumento de ρˉt\bar{\rho}_t advierte que la diversificación está fallando; no dice nada sobre la dirección del mercado. No sobrecargues una señal de riesgo con expectativas direccionales.
  • Hambre de datos. Todo lo anterior asume historiales largos, limpios y sincronizados. Los tokens más nuevos e interesantes de cripto violan las tres condiciones.

Resumen

  • La correlación estática es una mentira en cripto. Las correlaciones se agrupan, persisten, y se disparan hacia 1 en las caídas — exactamente cuando se supone que la diversificación debería ayudar. Un único ρ^\hat{\rho} muestral promedia un proceso de cambio de régimen en un punto medio sin sentido.
  • El GARCH multivariado completo (VECH, BEKK) no escala. El número de parámetros crece como O(d2)O(d^2); ambos se confinan a un puñado de activos en la práctica.
  • DCC (Engle 2002) factoriza el problema: Ht=DtRtDtH_t = D_t R_t D_t, con DtD_t de ajustes GARCH univariados independientes (reutilizando las Partes 1-2) y RtR_t de una recursión de dos parámetros. Escala a docenas de activos porque solo se optimizan a,ba, b.
  • La recursión Qt=(1ab)Qˉ+azt1zt1+bQt1Q_t = (1-a-b)\bar{Q} + a\,z_{t-1}z_{t-1}' + b\,Q_{t-1}, normalizada a RtR_t, produce una matriz de correlación definida positiva válida en cada paso, con a,b>0a,b>0, a+b<1a+b<1.
  • arch no hace DCC. Ajusta los márgenes con arch, luego implementa el estimador NumPy/SciPy de ~60 líneas aquí, o usa mgarch (Python) o rmgarch (R, la referencia).
  • Tres beneficios concretos: un ratio de cobertura dinámico βt=ρtσETH,t/σBTC,t\beta_t = \rho_t\,\sigma_{\text{ETH},t}/\sigma_{\text{BTC},t} para pairs trading; una varianza de cartera honesta y variable en el tiempo wHtww'H_t w para la asignación basada en riesgo; y la correlación promedio por pares como señal de régimen risk-off.
  • La disciplina lo es todo. El correlation targeting filtra el promedio de toda la muestra, así que reestima Qˉ\bar{Q} solo con datos de entrenamiento; retrasa (lag) cada ratio de cobertura; filtra hacia adelante, nunca suavices. La evaluación walk-forward no es negociable.
  • aDCC añade un término de asimetría a la baja y usualmente vale la pena en cripto, donde domina la correlación de crash.
  • La Parte 4 usa estos pronósticos para construir y hacer backtest de una estrategia con targeting de volatilidad.

Referencias:

  • Engle, R. (2002). Dynamic Conditional Correlation: A Simple Class of Multivariate Generalized Autoregressive Conditional Heteroskedasticity Models. Journal of Business & Economic Statistics, 20(3), 339-350. DOI
  • Engle, R. & Sheppard, K. (2001). Theoretical and Empirical Properties of Dynamic Conditional Correlation Multivariate GARCH. NBER Working Paper 8554. DOI
  • Bollerslev, T. (1990). Modelling the Coherence in Short-Run Nominal Exchange Rates: A Multivariate Generalized ARCH Model. Review of Economics and Statistics, 72(3), 498-505. DOI
  • Cappiello, L., Engle, R., & Sheppard, K. (2006). Asymmetric Dynamics in the Correlations of Global Equity and Bond Returns. Journal of Financial Econometrics, 4(4), 537-572. DOI
  • Engle, R. & Kroner, K. (1995). Multivariate Simultaneous Generalized ARCH. Econometric Theory, 11(1), 122-150. DOI
  • Bollerslev, T., Engle, R., & Wooldridge, J. (1988). A Capital Asset Pricing Model with Time-Varying Covariances. Journal of Political Economy, 96(1), 116-131. DOI
  • Galanos, A. (2022). rmgarch: Multivariate GARCH Models. R package. CRAN.
blog.disclaimer

MarketMaker.cc Team

Investigación Cuantitativa y Estrategia

Discutir en Telegram
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.