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

GARCH asimetrico y de colas pesadas: EGARCH, GJR y Student-t

#volatility
#GARCH
#EGARCH
#risk
#VaR
#crypto
#algorithmic-trading

En la Parte 1 de esta serie construimos GARCH(1,1) desde cero: la intuicion del agrupamiento de volatilidad, la recursion de la varianza condicional, la estimacion por maxima verosimilitud, la prediccion y los diagnosticos estandar de residuos con la libreria arch. Si no la has leido, empieza por ahi: este articulo asume que ya sabes ajustar e interpretar un GARCH(1,1) simple y no volvera a derivar los fundamentos.

El GARCH(1,1) simple es una buena linea base y una mala respuesta final. Tiene dos defectos estructurales que son baratos de ignorar en un backtest y caros de ignorar con capital real. Primero, es simetrico: el modelo reacciona a un dia de +5%+5\% exactamente igual que a un dia de 5%-5\%, porque el shock entra en la recursion de la varianza unicamente a traves de su cuadrado, εt12\varepsilon_{t-1}^2. Elevar al cuadrado descarta el signo. Segundo, asume innovaciones gaussianas: incluso despues de que GARCH absorbe el agrupamiento de volatilidad, los residuos estandarizados de BTC y ETH tienen colas visiblemente pesadas, y una verosimilitud gaussiana infravalora sistematicamente la cola. Un VaR al 99% de GARCH(1,1)-Normal sera superado mucho mas del 1% de las veces.

Este articulo corrige ambos defectos. Anadimos asimetria con GJR-GARCH y EGARCH, y colas pesadas con innovaciones Student-tt y skew-tt de Hansen. Luego hacemos lo que de verdad paga las facturas: convertir la distribucion condicional ajustada en una prediccion de Value-at-Risk y Expected Shortfall a un paso, y hacer un backtest honesto de esa prediccion con los tests de Kupiec y Christoffersen. Un modelo de volatilidad que nunca sometes a un test de riesgo es una decoracion.

El efecto apalancamiento, y por que cripto es mas caotico

En renta variable la asimetria tiene un nombre y una historia. El efecto apalancamiento (Black, 1976): cuando la accion de una empresa cae, su ratio deuda-capital sube, el capital se vuelve mecanicamente mas arriesgado y la volatilidad aumenta. Las malas noticias elevan la volatilidad futura mas que las buenas noticias de igual magnitud. Empiricamente este es uno de los hechos estilizados mas robustos en la literatura de volatilidad de renta variable.

Cripto no tiene capital accionario ni apalancamiento de balance en el sentido corporativo, y sin embargo una asimetria de tipo efecto-apalancamiento aparece de todas formas la mayor parte del tiempo, impulsada por el desapalancamiento forzado en lugar de por la contabilidad. Cuando BTC cae con fuerza, los prestamos sobrecolateralizados se liquidan, los largos en futuros perpetuos se cierran a la fuerza, el funding se invierte y la cascada alimenta la volatilidad. Asi que el mecanismo es distinto pero el signo a menudo coincide con la renta variable: los movimientos a la baja disparan mas la volatilidad.

La salvedad importante: cripto es mas caotico, y deberias tratar la asimetria como una cuestion empirica y no como una ley. Los movimientos al alza violentos (short squeezes, un melt-up alimentado por apalancamiento, un salto por aprobacion de ETF) tambien pueden disparar la volatilidad realizada. Dependiendo del activo y de la ventana muestral, la asimetria estimada puede ser fuerte, debil u ocasionalmente del signo "equivocado". La disciplina que este articulo exige: ajusta el modelo asimetrico, observa si el parametro de asimetria es estadisticamente significativo y va en la direccion esperada, y conserva el parametro extra solo si se gana su lugar. No asumas que la historia de la renta variable se transfiere; ponla a prueba.

Probar la asimetria antes de modelarla

El resumen anterior dice "trata la asimetria como empirica", asi que antes de ajustar un modelo asimetrico, ejecuta un test formal barato de si la asimetria esta siquiera presente. Los tests de sesgo por signo de Engle-Ng (1993) hacen exactamente esto. Ajusta primero un GARCH(1,1) simetrico, toma sus residuos estandarizados al cuadrado zt2z_t^2 y regresalos sobre indicadores del signo y el tamano del shock anterior:

zt2=a0+a1St1+a2St1εt1+a3St1+εt1+utz_t^2 = a_0 + a_1 S_{t-1}^- + a_2 S_{t-1}^- \varepsilon_{t-1} + a_3 S_{t-1}^+ \varepsilon_{t-1} + u_t

donde St1=1{εt1<0}S_{t-1}^- = \mathbf{1}\{\varepsilon_{t-1} < 0\} y St1+=1St1S_{t-1}^+ = 1 - S_{t-1}^-. La logica: si el modelo simetrico ya ha capturado todo, el signo y el tamano del shock de ayer no deberian predecir el residuo al cuadrado de hoy, de modo que a1=a2=a3=0a_1 = a_2 = a_3 = 0. Los tests tt individuales son el sesgo por signo (a1a_1), el sesgo por tamano negativo (a2a_2) y el sesgo por tamano positivo (a3a_3); un test FF conjunto sobre los tres es el omnibus. Un a1a_1 o a2a_2 significativo indica que los shocks negativos estan sistematicamente mal valorados por el modelo simetrico: tu senal de que GJR o EGARCH ayudaran.

import statsmodels.api as sm

def sign_bias_test(symmetric_res):
    """Engle-Ng sign-bias tests on a fitted symmetric GARCH result."""
    z = symmetric_res.std_resid.dropna()
    z2 = (z ** 2).values[1:]
    eps_lag = z.values[:-1]                      # standardized shock proxy
    neg = (eps_lag < 0).astype(float)
    X = np.column_stack([
        np.ones_like(eps_lag),                   # intercept
        neg,                                     # sign bias
        neg * eps_lag,                           # negative size bias
        (1 - neg) * eps_lag,                     # positive size bias
    ])
    ols = sm.OLS(z2, X).fit()
    names = ["const", "sign_bias", "neg_size_bias", "pos_size_bias"]
    for nm, coef, t, p in zip(names, ols.params, ols.tvalues, ols.pvalues):
        print(f"{nm:16s} coef={coef:+.4f}  t={t:+.2f}  p={p:.3f}")
    print(f"Joint F p-value: {ols.f_pvalue:.4f}")
    return ols

sign_bias_test(models["GARCH-N"])

Si el test FF conjunto es no significativo, tienes licencia empirica para quedarte con el modelo simetrico y ahorrar dos parametros. Si es significativo (el resultado habitual para BTC/ETH), pasa a GJR/EGARCH con la conciencia tranquila, sabiendo que estas modelando una caracteristica real y no persiguiendo ruido. Esta es la disciplina empirica que exigia el planteamiento: no asumas la historia del apalancamiento de la renta variable, pruebala.

GJR-GARCH: asimetria mediante un termino de umbral

El modelo Glosten-Jagannathan-Runkle (1993), a veces llamado TGARCH o threshold GARCH, es la edicion mas pequena posible sobre GARCH(1,1) que permite que las malas noticias y las buenas noticias tengan efectos distintos. Recuerda la recursion simetrica de la varianza condicional de la Parte 1:

σt2=ω+αεt12+βσt12\sigma_t^2 = \omega + \alpha\,\varepsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2

GJR anade un termino de umbral: una dosis extra de varianza que se activa solo despues de un shock negativo.

σt2=ω+αεt12+γIt1εt12+βσt12\sigma_t^2 = \omega + \alpha\,\varepsilon_{t-1}^2 + \gamma\,I_{t-1}\,\varepsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2

donde It1I_{t-1} es el indicador

It1={1if εt1<00if εt10I_{t-1} = \begin{cases} 1 & \text{if } \varepsilon_{t-1} < 0 \\ 0 & \text{if } \varepsilon_{t-1} \geq 0 \end{cases}

Lee la recursion por casos. Tras un shock positivo (εt10\varepsilon_{t-1} \geq 0), el indicador es cero y el impacto del shock al cuadrado sobre la varianza del siguiente periodo es solo α\alpha. Tras un shock negativo, el indicador es uno y el impacto es α+γ\alpha + \gamma. El parametro γ\gamma es toda la historia de la asimetria en un unico numero:

  • γ>0\gamma > 0: los shocks negativos elevan la volatilidad mas que los shocks positivos de la misma magnitud. Este es el efecto apalancamiento, y es lo que esperas encontrar en BTC/ETH la mayor parte del tiempo.
  • γ=0\gamma = 0: el modelo colapsa de vuelta a GARCH(1,1) simetrico. Un test de razon de verosimilitud o un test tt sobre γ\gamma es, por tanto, una prueba directa de si la asimetria existe siquiera.
  • γ<0\gamma < 0: los shocks positivos elevan mas la volatilidad, el ocasional regimen de melt-up cripto. Raro, pero no lo descartes a priori.

Positividad y estacionariedad

Como σt2\sigma_t^2 sigue construyendose de forma aditiva, necesitamos que cada termino permanezca no negativo. Las condiciones suficientes de positividad son

ω>0,α0,α+γ0,β0.\omega > 0, \quad \alpha \geq 0, \quad \alpha + \gamma \geq 0, \quad \beta \geq 0.

Observa que se permite que γ\gamma sea negativo mientras α+γ0\alpha + \gamma \geq 0, de modo que el impacto tras una mala noticia nunca se vuelve negativo.

Para la estacionariedad en covarianza, asume que las innovaciones zt=εt/σtz_t = \varepsilon_t/\sigma_t estan estandarizadas con una distribucion simetrica en torno a cero, de forma que P(εt1<0)=1/2P(\varepsilon_{t-1} < 0) = 1/2 y el indicador contribuye γ/2\gamma/2 en promedio. La condicion de estacionariedad se convierte en

α+β+12γ<1.\alpha + \beta + \tfrac{1}{2}\gamma < 1.

La varianza incondicional (de largo plazo) es entonces

σˉ2=ω1αβ12γ.\bar{\sigma}^2 = \frac{\omega}{1 - \alpha - \beta - \tfrac{1}{2}\gamma}.

Este es el analogo GJR del resultado de la Parte 1 σˉ2=ω/(1αβ)\bar{\sigma}^2 = \omega/(1-\alpha-\beta), con el termino extra 12γ\tfrac{1}{2}\gamma que da cuenta de la contribucion media de la vida media del apalancamiento. Si tu distribucion de innovaciones es asimetrica (la skew-tt de Hansen, mas abajo), el 1/21/2 se reemplaza por la probabilidad real de que zt<0z_t < 0, pero 1/21/2 es la referencia estandar usada para la persistencia reportada.

EGARCH: modelar la log-varianza, sin restricciones de positividad

GJR te mantiene dentro de una camisa de fuerza de positividad de la varianza: cada combinacion de parametros debe comprobarse contra restricciones de desigualdad, lo cual es molesto durante la optimizacion y peor durante la reestimacion movil, cuando una ventana ocasionalmente se adentra en una region infactible. El Exponential GARCH de Nelson (1991) esquiva esto por completo modelando el logaritmo de la varianza condicional. Como logσt2\log \sigma_t^2 puede ser cualquier numero real, σt2=exp(logσt2)\sigma_t^2 = \exp(\log \sigma_t^2) es automaticamente positivo sin importar cuales sean los parametros. Ninguna restriccion que imponer.

Escribe la recursion en terminos de la innovacion estandarizada zt1=εt1/σt1z_{t-1} = \varepsilon_{t-1}/\sigma_{t-1}:

logσt2=ω+βlogσt12+α(zt1Ezt1)+γzt1\log \sigma_t^2 = \omega + \beta \log \sigma_{t-1}^2 + \alpha\bigl(|z_{t-1}| - \mathbb{E}|z_{t-1}|\bigr) + \gamma\, z_{t-1}

Dos terminos portan el shock, y separarlos es la idea entera:

  • El termino de magnitud α(zt1Ezt1)\alpha(|z_{t-1}| - \mathbb{E}|z_{t-1}|) responde al tamano del shock, eliminado el signo. Restar Ezt1\mathbb{E}|z_{t-1}| lo centra de modo que un shock de magnitud media no contribuye nada. Para una normal estandar, Ez=2/π0.7979\mathbb{E}|z| = \sqrt{2/\pi} \approx 0.7979; para una Student-tt estandarizada el valor absoluto esperado es menor y depende de ν\nu, pero arch lo maneja internamente.
  • El termino de signo γzt1\gamma\, z_{t-1} es la asimetria. Es lineal en la innovacion con signo, de modo que un zt1z_{t-1} negativo empuja logσt2\log\sigma_t^2 en la direccion opuesta a uno positivo.

La convencion de signo importa y hace tropezar a la gente. En esta parametrizacion el efecto apalancamiento (las malas noticias elevan la volatilidad) corresponde a γ<0\gamma < 0: un shock negativo zt1<0z_{t-1} < 0 hace entonces γzt1>0\gamma z_{t-1} > 0, aumentando la log-varianza. Este es el signo opuesto al γ>0\gamma > 0 de GJR. Consulta siempre la propia documentacion del modelo para la convencion en lugar de asumirla; arch reporta EGARCH con su propio signo, y lo comprobamos mas abajo contra una curva de impacto de noticias en vez de confiar en nuestra memoria.

Como todo es aditivo en logaritmos, la persistencia de un EGARCH(1,1) esta gobernada por el unico coeficiente autorregresivo β\beta sobre logσt12\log\sigma_{t-1}^2; la estacionariedad requiere solo β<1|\beta| < 1. Esa es una condicion mucho mas limpia que la desigualdad de GJR, y es una ventaja practica real cuando reajustas sobre ventanas moviles.

Una sutileza que vale la pena senalar: la respuesta de EGARCH a los shocks es exponencial en la innovacion (exponencias al final), mientras que GJR es cuadratica. Por tanto, EGARCH reacciona de forma mas violenta a shocks grandes, una ventaja en cripto, donde los eventos de cola son los que importan, pero tambien una razon por la que EGARCH puede producir ocasionalmente predicciones de varianza inverosimilmente grandes tras un dia atipico. Ninguno es universalmente mejor; eliges por el ajuste fuera de muestra y por los backtests de riesgo, que es el proposito de toda esta serie.

La curva de impacto de noticias

La forma mas limpia de ver la diferencia entre GARCH simetrico, GJR y EGARCH es la curva de impacto de noticias (Engle y Ng, 1993): manten σt1\sigma_{t-1} fijo en su nivel de largo plazo y representa la varianza condicional del siguiente periodo σt2\sigma_t^2 como funcion del ultimo shock εt1\varepsilon_{t-1}. Responde a "dado un shock de este tamano y signo, cuanto eleva el modelo la volatilidad de manana?"

  • GARCH simetrico produce una parabola simetrica centrada en cero. Un shock de 5%-5\% y uno de +5%+5\% caen a la misma altura. Este es precisamente el defecto que estamos corrigiendo.
  • GJR produce una parabola con un quiebre en cero, mas empinada a la izquierda (shocks negativos) que a la derecha cuando γ>0\gamma > 0. Las dos mitades tienen curvatura α+γ\alpha+\gamma y α\alpha respectivamente.
  • EGARCH produce una forma de V exponencial y asimetrica: los dos brazos tienen pendientes distintas por el termino γz\gamma z, y el conjunto se curva hacia arriba mas rapido que una parabola por la exponenciacion final.

Representamos las tres a partir de parametros ajustados mas adelante, en la seccion de implementacion; es el diagnostico mas util para comunicar lo que compra la asimetria.

Colas pesadas: innovaciones Student-t y skew-t

La asimetria corrige la respuesta del modelo al signo de los shocks. No hace nada respecto a la distribucion de los shocks en si. El GARCH simple asume ztN(0,1)z_t \sim \mathcal{N}(0,1), y esa suposicion es casi siempre erronea para cripto. Incluso despues de que GARCH elimina el agrupamiento de volatilidad, los residuos estandarizados zt=εt/σtz_t = \varepsilon_t / \sigma_t conservan exceso de curtosis: tienen colas pesadas. Una verosimilitud gaussiana, ajustando los hombros de la distribucion, infravalora la frecuencia con la que ocurre de verdad un dia estandarizado de 44, 55 o 66 sigmas.

La consecuencia para el riesgo es directa. Un VaR gaussiano al 99% usa el cuantil Φ1(0.01)2.326\Phi^{-1}(0.01) \approx -2.326, asi que predice VaR0.992.326σt\text{VaR}_{0.99} \approx 2.326\,\sigma_t. Si la verdadera distribucion estandarizada es Student-tt con, digamos, ν=5\nu = 5 grados de libertad, el verdadero cuantil del 1% esta cerca de 3.36-3.36: el VaR gaussiano es optimista en aproximadamente un 44%44\% a ese nivel de confianza. Lo superaras mucho mas del 1% de las veces y te sorprenderan sistematicamente dias "imposibles". Esto no es una rareza de cripto; Bollerslev (1987) introdujo el tt-GARCH precisamente porque los residuos de renta variable y de divisas mostraban las mismas colas pesadas. Cripto es solo una version mas extrema del mismo problema.

Student-t estandarizada

La densidad Student-tt tiene un parametro de grados de libertad ν>2\nu > 2 que controla el grosor de las colas: un ν\nu pequeno significa colas pesadas, y a medida que ν\nu \to \infty la tt converge a la gaussiana. El truco es que la distribucion tνt_\nu cruda tiene varianza ν/(ν2)1\nu/(\nu-2) \neq 1, asi que debemos estandarizarla a varianza unitaria antes de usarla como innovacion; de lo contrario el "σt\sigma_t" de la recursion GARCH no seria en realidad la desviacion tipica condicional.

La innovacion Student-tt estandarizada con varianza unitaria tiene densidad

f(z;ν)=Γ ⁣(ν+12)π(ν2)  Γ ⁣(ν2)(1+z2ν2)ν+12,ν>2.f(z;\nu) = \frac{\Gamma\!\left(\frac{\nu+1}{2}\right)}{\sqrt{\pi(\nu-2)}\;\Gamma\!\left(\frac{\nu}{2}\right)}\left(1 + \frac{z^2}{\nu-2}\right)^{-\frac{\nu+1}{2}}, \qquad \nu > 2.

Observa el (ν2)(\nu-2) interior: esa es la estandarizacion, reescalando de modo que Var(z)=1\mathrm{Var}(z)=1. La contribucion a la log-verosimilitud de una observacion, dada la varianza condicional GARCH σt2\sigma_t^2 y zt=εt/σtz_t = \varepsilon_t/\sigma_t, es

t=logΓ ⁣(ν+12)logΓ ⁣(ν2)12log(π(ν2))12logσt2ν+12log ⁣(1+zt2ν2).\ell_t = \log\Gamma\!\left(\tfrac{\nu+1}{2}\right) - \log\Gamma\!\left(\tfrac{\nu}{2}\right) - \tfrac{1}{2}\log\bigl(\pi(\nu-2)\bigr) - \tfrac{1}{2}\log\sigma_t^2 - \frac{\nu+1}{2}\log\!\left(1 + \frac{z_t^2}{\nu-2}\right).

El termino 12logσt2-\tfrac{1}{2}\log\sigma_t^2 es el jacobiano de la transformacion de εt\varepsilon_t a ztz_t, el mismo termino que viste en la verosimilitud del GARCH gaussiano en la Parte 1. Solo cambia la forma. Maximizar tt\sum_t \ell_t conjuntamente sobre los parametros GARCH y ν\nu es exactamente lo que hace arch cuando pasas dist='t'.

El ν\nu estimado es en si mismo informativo. Para retornos diarios de BTC/ETH tipicamente aterrizas en el rango ν36\nu \approx 3\text{–}6: colas pesadas, pero con varianza finita (que necesita ν>2\nu > 2) y normalmente curtosis finita (que necesita ν>4\nu > 4). Si tu ν\nu ajustado cae por debajo de 4, ten en cuenta que la curtosis muestral es tecnicamente infinita en el modelo y algunos estimadores se vuelven inestables; es una senal para mirar con atencion los valores atipicos y la calidad de los datos.

La skew-t de Hansen

La Student-tt tiene colas pesadas pero sigue siendo simetrica: las colas izquierda y derecha son igualmente pesadas. Los residuos de retorno de cripto a menudo son ademas asimetricos: la cola izquierda (los desplomes) es mas pesada que la derecha. La skew-tt de Hansen (1994) generaliza la tt estandarizada con un parametro de asimetria λ(1,1)\lambda \in (-1, 1) junto a ν\nu:

f(z;ν,λ)={bc(1+1ν2(bz+a1λ)2)ν+12z<a/bbc(1+1ν2(bz+a1+λ)2)ν+12za/bf(z;\nu,\lambda) = \begin{cases} b\,c\left(1 + \dfrac{1}{\nu-2}\left(\dfrac{bz+a}{1-\lambda}\right)^2\right)^{-\frac{\nu+1}{2}} & z < -a/b \\[2mm] b\,c\left(1 + \dfrac{1}{\nu-2}\left(\dfrac{bz+a}{1+\lambda}\right)^2\right)^{-\frac{\nu+1}{2}} & z \geq -a/b \end{cases}

donde las constantes a=4λcν2ν1a = 4\lambda c\,\frac{\nu-2}{\nu-1}, b2=1+3λ2a2b^2 = 1 + 3\lambda^2 - a^2 y c=Γ(ν+12)π(ν2)Γ(ν2)c = \frac{\Gamma(\frac{\nu+1}{2})}{\sqrt{\pi(\nu-2)}\,\Gamma(\frac{\nu}{2})} se eligen de modo que zz tenga media cero y varianza unitaria para todo (ν,λ)(\nu,\lambda) valido. La distribucion se divide en z=a/bz = -a/b, usando un escalado distinto en cada tramo para desplazar mas masa hacia una cola.

Interpretacion: λ<0\lambda < 0 da una distribucion asimetrica hacia la izquierda (mayor peso a la baja), que es el hallazgo habitual para cripto y lo que esperarias emparejar con un efecto apalancamiento. λ=0\lambda = 0 recupera la Student-tt simetrica, de modo que un test de λ=0\lambda = 0 te dice si el termino de asimetria esta comprando algo. En arch esto es dist='skewt', que estima tanto ν\nu como λ\lambda. La recompensa es un VaR cuyo cuantil de cola izquierda es honestamente mas pesado que su cuantil de cola derecha, exactamente lo que quieres cuando las perdidas que intentas sobrevivir son asimetricas. Esto conecta directamente con la asimetria entre perdida y ganancia en los resultados de las posiciones: una caida de x%x\% necesita mas de x%x\% para recuperarse, asi que modelar mal la cola izquierda es mas costoso que modelar mal la derecha.

Implementacion en Python

Ahora ajustamos todo esto con la libreria arch. La configuracion refleja la Parte 1: obtener retornos diarios, escalar por 100 para el condicionamiento numerico (los optimizadores GARCH se comportan mal cuando los retornos son O(0.01)O(0.01)) y ajustar con una media constante. Si quieres intradia o un modelo de media distinto, la maquinaria es identica.

Configuracion y datos

import numpy as np
import pandas as pd
from arch import arch_model
from arch.univariate import GARCH, EGARCH, ConstantMean, StudentsT, SkewStudent
from scipy import stats

def fetch_returns(symbol="BTC-USD", start="2019-01-01", end="2025-12-31"):
    """Daily log returns, in percent (scaled x100 for the optimizer)."""
    import yfinance as yf
    px = yf.download(symbol, start=start, end=end)["Close"].dropna()
    ret = 100.0 * np.log(px / px.shift(1)).dropna()
    ret.name = symbol
    return ret

r = fetch_returns("BTC-USD")
print(f"{len(r)} daily observations, "
      f"annualized vol ~ {r.std() * np.sqrt(365):.0f}%")

Cripto cotiza 24/7, asi que anualizamos con 365, no con 252: una fuente de confusion pequena pero recurrente cuando comparas un Sharpe o una volatilidad de cripto con las cifras de una mesa de renta variable.

Ajustar cuatro modelos

El patron en arch: vol='Garch' con p=1, q=1 es GARCH simetrico; anadir o=1 activa el termino de umbral GJR; vol='EGARCH' cambia al modelo de log-varianza. La distribucion de innovaciones se fija con dist: 'normal', 't', 'skewt'.

def fit(returns, vol="Garch", p=1, o=0, q=1, dist="normal"):
    am = arch_model(returns, mean="Constant",
                    vol=vol, p=p, o=o, q=q, dist=dist)
    res = am.fit(disp="off")
    return res

models = {
    "GARCH-N":    fit(r, vol="Garch",  o=0, dist="normal"),  # Part 1 baseline
    "GJR-t":      fit(r, vol="Garch",  o=1, dist="t"),        # asymmetry + fat tails
    "EGARCH-t":   fit(r, vol="EGARCH", o=1, dist="t"),        # log-variance asymmetry
    "GJR-skewt":  fit(r, vol="Garch",  o=1, dist="skewt"),    # + skew
}

for name, res in models.items():
    print(f"\n===== {name} =====")
    print(res.summary().tables[1])   # parameter table

Para vol='EGARCH', el argumento o controla el termino asimetrico (γz\gamma z) y p/q controlan los terminos de magnitud y de rezago; o=1, p=1, q=1 es el EGARCH(1,1) estandar. Un detalle: los nombres de los parametros de EGARCH en arch son las mismas letras, pero la convencion de signo sobre el termino de asimetria es la de Nelson, asi que una estimacion negativa es el efecto apalancamiento. Verificamos esto a partir de la curva de impacto de noticias en lugar de fiarnos de la memoria.

Leyendo el ajuste GJR

Una tabla de parametros GJR-tt se ve mas o menos asi (valores ilustrativos, no un experimento reportado; reajusta con tus propios datos):

                  coef    std err       t      P>|t|
omega           0.0480     0.017     2.82     0.005
alpha[1]        0.0620     0.018     3.44     0.001
gamma[1]        0.0910     0.028     3.25     0.001
beta[1]         0.8850     0.021    42.1      0.000
nu              4.30       0.55      7.82     0.000

Como leerla:

  • gamma[1] = 0.091 con un estadistico tt por encima de 3 es un efecto apalancamiento estadisticamente significativo. Tras un shock negativo el impacto del shock al cuadrado es α+γ=0.062+0.091=0.153\alpha + \gamma = 0.062 + 0.091 = 0.153; tras un shock positivo es solo α=0.062\alpha = 0.062. Las malas noticias mueven la volatilidad de este modelo aproximadamente 2.5×2.5\times mas que las buenas noticias del mismo tamano.
  • nu = 4.3 confirma colas pesadas, lejos de la gaussiana (ν\nu \to \infty), y lo bastante bajo como para que el cuarto momento apenas sea finito. Un VaR gaussiano sobre esta serie seria gravemente optimista.
  • La persistencia es α+β+12γ=0.062+0.885+0.04550.993\alpha + \beta + \tfrac{1}{2}\gamma = 0.062 + 0.885 + 0.0455 \approx 0.993: muy alta, como es habitual en cripto diario: los shocks decaen despacio y la volatilidad esta fuertemente agrupada.

La linea mas importante que hay que comprobar es la fila de γ\gamma. Si su pp-valor es grande, el termino asimetrico no se esta ganando su lugar en este activo y ventana, y deberias preferir el modelo simetrico mas simple. Esto es disciplina de seleccion de modelos, no decoracion; mas sobre ello abajo.

Comparar modelos por criterios de informacion

La log-verosimilitud siempre mejora cuando anades parametros, asi que no puedes seleccionar solo por log-verosimilitud. Usa AIC/BIC, que penalizan el numero de parametros (BIC de forma mas agresiva):

def compare(models: dict) -> pd.DataFrame:
    rows = []
    for name, res in models.items():
        rows.append({
            "model": name,
            "n_params": len(res.params),
            "loglik": res.loglikelihood,
            "AIC": res.aic,
            "BIC": res.bic,
        })
    df = pd.DataFrame(rows).set_index("model")
    return df.sort_values("BIC")

print(compare(models))

Reglas practicas de interpretacion: una mejora de BIC de mas de ~6 sobre la linea base es fuerte evidencia de que la estructura extra es real; una diferencia de 1-2 es ruido. Si GJR-t supera a GARCH-N por mas de 30 puntos de BIC pero GJR-skewt supera a GJR-t por solo 1, conserva la tt y descarta la asimetria; el parametro de asimetria no se esta pagando a si mismo en estos datos. No leas AIC/BIC como un sustituto de la validacion fuera de muestra; premian el ajuste dentro de muestra ajustado por complejidad, lo cual es necesario pero no suficiente. La verdadera prueba es el backtest de VaR y, en ultima instancia, la evaluacion walk-forward.

Representar la curva de impacto de noticias

Este es el grafico que da la recompensa: hace visible la asimetria y verifica la convencion de signo de EGARCH.

import matplotlib.pyplot as plt

def news_impact_curve(res, shock_grid):
    """
    Next-period conditional variance as a function of the last shock,
    holding sigma_{t-1} at the model's unconditional level.
    Works for symmetric GARCH, GJR (o=1), and EGARCH.
    """
    p = res.params
    vol_name = res.model.volatility.__class__.__name__
    sigma2_bar = np.mean(res.conditional_volatility ** 2)

    omega = p["omega"]
    alpha = p.get("alpha[1]", 0.0)
    gamma = p.get("gamma[1]", 0.0)
    beta  = p.get("beta[1]", 0.0)

    if vol_name == "EGARCH":
        sig_prev = np.sqrt(sigma2_bar)
        z = shock_grid / sig_prev
        E_abs = np.sqrt(2 / np.pi)   # approx; arch uses the fitted dist's E|z|
        log_s2 = (omega + beta * np.log(sigma2_bar)
                  + alpha * (np.abs(z) - E_abs) + gamma * z)
        return np.exp(log_s2)
    else:
        ind = (shock_grid < 0).astype(float)
        return omega + (alpha + gamma * ind) * shock_grid**2 + beta * sigma2_bar

shocks = np.linspace(-8, 8, 401)   # daily % shocks
plt.figure(figsize=(8, 5))
for name in ["GARCH-N", "GJR-t", "EGARCH-t"]:
    nic = news_impact_curve(models[name], shocks)
    plt.plot(shocks, nic, label=name, lw=2)
plt.axvline(0, color="gray", lw=0.8, ls="--")
plt.xlabel("Last shock  $\\varepsilon_{t-1}$  (daily %)")
plt.ylabel("Next-period conditional variance  $\\sigma_t^2$")
plt.title("News impact curve: symmetric vs GJR vs EGARCH (BTC)")
plt.legend()
plt.tight_layout()

Cuando ejecutas esto, la curva simetrica GARCH-N es una parabola limpia centrada en cero: un shock de 6%-6\% y uno de +6%+6\% dan varianza identica. GJR-t es una parabola con un quiebre en el origen, mas alta en el brazo izquierdo. EGARCH-t es la V exponencial, y si su brazo izquierdo queda por encima del derecho has confirmado el efecto apalancamiento y la convencion de signo de un solo vistazo. Si el brazo izquierdo de EGARCH queda por debajo del derecho, o bien γ\gamma se estimo positivo (un regimen de volatilidad al alza) o tienes el signo invertido; el grafico te dice cual sin conjeturas.

Una comparacion lado a lado de los cuatro modelos

Antes de pasar al riesgo, ayuda tener los cuatro modelos uno junto a otro. Cada fila es una decision de diseno, y las columnas muestran lo que esa decision cuesta y lo que compra.

Propiedad GARCH-N GJR-t EGARCH-t GJR-skewt
Asimetria (signo del shock) ninguna umbral γIε2\gamma I\varepsilon^2 con signo γz\gamma z umbral γIε2\gamma I\varepsilon^2
Forma de cola de la innovacion Gaussiana Student-tt Student-tt skew-tt
Asimetria en la innovacion no no no si (λ\lambda)
Restricciones de positividad si si (α+γ0\alpha+\gamma\ge0) ninguna (forma log) si
Condicion de estacionariedad α+β<1\alpha+\beta<1 α+β+12γ<1\alpha+\beta+\tfrac12\gamma<1 β<1\lvert\beta\rvert<1 α+β+P(z<0)γ<1\alpha+\beta+P(z<0)\gamma<1
Parametros extra vs linea base 0 γ,ν\gamma,\nu γ,ν\gamma,\nu γ,ν,λ\gamma,\nu,\lambda
Veredicto tipico en cripto falla el backtest de VaR fuerte, robusto fuerte, robusto marginal sobre GJR-t

El patron que hay que interiorizar: el salto de la columna 1 a la columna 2 (anadir asimetria y colas pesadas a la vez) es donde reside casi toda la mejora en la calibracion del riesgo. Los refinamientos posteriores (la forma funcional de EGARCH, el termino de asimetria) son reales pero de segundo orden, y en muchas series de cripto estan dentro del ruido. Gasta tu presupuesto de modelado en el primer salto y se esceptico con el resto.

Aplicacion al riesgo: VaR y Expected Shortfall

Ajustar un modelo de volatilidad mas sofisticado solo vale la pena si mejora una decision. La decision mas limpia de mejorar es la prediccion de riesgo de cola a un paso: cuan malo puede ser manana? Producimos un Value-at-Risk y una Expected Shortfall a un dia (tambien llamada Conditional VaR, que el pipeline de cartera HRP/CVaR usa como su objetivo) directamente a partir de la prediccion GARCH-tt/skew-tt ajustada.

De la distribucion condicional al VaR

La maquinaria GARCH da una prediccion a un paso de la media condicional μt+1\mu_{t+1} y de la desviacion tipica condicional σt+1\sigma_{t+1}. El retorno se modela como rt+1=μt+1+σt+1zt+1r_{t+1} = \mu_{t+1} + \sigma_{t+1} z_{t+1} con zt+1z_{t+1} extraido de la distribucion estandarizada ajustada (gaussiana, tt o skew-tt). Asi que el α\alpha-cuantil del retorno es simplemente una transformacion afin del α\alpha-cuantil de la distribucion estandarizada:

VaRα(t+1)=(μt+1+σt+1Fz1(1α))\text{VaR}_{\alpha}(t+1) = -\Bigl(\mu_{t+1} + \sigma_{t+1}\, F_z^{-1}(1-\alpha)\Bigr)

donde Fz1F_z^{-1} es el cuantil (CDF inversa) de la innovacion estandarizada y el signo menos inicial sigue la convencion de que el VaR es un numero de perdida positivo. Para un VaR al 99%, α=0.99\alpha = 0.99 e introduces Fz1(0.01)F_z^{-1}(0.01). Todo el beneficio de la tt/skew-tt aparece aqui: Fz1(0.01)F_z^{-1}(0.01) es mas negativo que el 2.326-2.326 gaussiano, asi que el VaR es honestamente mayor.

Expected Shortfall

El VaR te dice el umbral; no dice nada sobre cuan mala es la superacion cuando ocurre. La Expected Shortfall (la perdida media condicionada a superar el VaR) si lo hace, y es coherente (subaditiva), motivo por el cual es la medida de riesgo detras de la optimizacion CVaR y por el que Basilea se paso a ella. Para un modelo de localizacion-escala,

ESα(t+1)=(μt+1+σt+1E ⁣[zzFz1(1α)]).\text{ES}_{\alpha}(t+1) = -\Bigl(\mu_{t+1} + \sigma_{t+1}\, \mathbb{E}\!\left[z \mid z \leq F_z^{-1}(1-\alpha)\right]\Bigr).

El termino de esperanza condicional de cola E[zzq]\mathbb{E}[z \mid z \le q] tiene formas cerradas para las distribuciones estandar. Para la gaussiana, con q=Φ1(1α)q = \Phi^{-1}(1-\alpha),

E[zzq]=ϕ(q)1α,\mathbb{E}[z \mid z \le q] = -\frac{\phi(q)}{1-\alpha},

donde ϕ\phi es la pdf normal estandar. Para la Student-tt estandarizada con ν\nu grados de libertad y q=tν1(1α)q = t_\nu^{-1}(1-\alpha) (en la escala estandarizada), la esperanza de cola es

E[zzq]=ν+q2ν1gν(q)1α,\mathbb{E}[z \mid z \le q] = -\frac{\nu + q^2}{\nu - 1}\cdot\frac{g_\nu(q)}{1-\alpha},

donde gνg_\nu es la pdf de la tt estandarizada. La Expected Shortfall de la tt supera a la gaussiana en mas de lo que la supera el VaR, porque la cola de la tt no solo esta mas lejos, sino que es mas gruesa, de modo que la perdida media mas alla del umbral es desproporcionadamente grande. Esa brecha extra es el numero que un modelo gaussiano te oculta.

Calcular VaR y ES a partir de un modelo arch ajustado

Las distribuciones de arch exponen un metodo ppf (cuantil), asi que podemos obtener el cuantil estandarizado directamente y evitar volver a derivar nada. Para la ES integramos numericamente, lo cual es robusto y funciona de forma uniforme entre normal/t/skewt.

from scipy import integrate

def var_es_forecast(res, alpha=0.99):
    """
    One-step-ahead VaR and ES at level alpha, on the same (x100) scale
    as the returns fed to the model. Divide by 100 for fractional units.
    """
    fc = res.forecast(horizon=1, reindex=False)
    mu = fc.mean.iloc[-1, 0]
    sigma = np.sqrt(fc.variance.iloc[-1, 0])

    dist = res.model.distribution          # StudentsT / SkewStudent / Normal
    dp = [res.params[k] for k in res.params.index
          if k in ("nu", "eta", "lambda", "lam")]

    z_q = dist.ppf(1 - alpha, dp) if dp else dist.ppf(1 - alpha)
    z_q = float(np.atleast_1d(z_q)[0])

    def pdf(z):
        arr = np.atleast_1d(z).astype(float)
        lp = dist.loglikelihood(dp, arr, np.ones_like(arr), individual=True) \
             if dp else dist.loglikelihood([], arr, np.ones_like(arr),
                                           individual=True)
        return np.exp(lp)

    num, _ = integrate.quad(lambda z: z * pdf(z), -30, z_q, limit=200)
    es_z = num / (1 - alpha)               # E[z | z <= z_q]

    var = -(mu + sigma * z_q)
    es  = -(mu + sigma * es_z)
    return {"mu": mu, "sigma": sigma, "z_q": z_q,
            "VaR": var, "ES": es}

for name in ["GARCH-N", "GJR-t", "GJR-skewt"]:
    out = var_es_forecast(models[name], alpha=0.99)
    print(f"{name:11s}  sigma={out['sigma']:.2f}%  "
          f"z_q={out['z_q']:+.2f}  "
          f"VaR99={out['VaR']:.2f}%  ES99={out['ES']:.2f}%")

La columna z_q es toda la historia en un solo numero. El modelo gaussiano usa zq2.33z_q \approx -2.33; la tt con ν4.3\nu \approx 4.3 usa algo cercano a 3.3-3.3; la skew-tt empuja el cuantil izquierdo aun mas afuera mientras acerca el derecho. Mismo σt+1\sigma_{t+1}, VaR materialmente mayor. Si has estado ejecutando VaR gaussiano en cripto, esta es la brecha que has estado absorbiendo en silencio.

Un paso frente a multiples pasos: una salvedad

Todo lo anterior es una prediccion a un dia, y ahi es donde el VaR de GARCH es mas limpio. Dos cosas complican los horizontes mas largos y deberias conocerlas antes de extrapolar.

Primero, las predicciones de varianza revierten a la media. La varianza condicional a hh pasos de un GARCH estacionario converge hacia el nivel incondicional σˉ2\bar\sigma^2 a medida que hh crece, y la varianza acumulada a hh dias es la suma de las predicciones por paso: no es h×σt+12h\times\sigma_{t+1}^2 a menos que la volatilidad este en su media de largo plazo. El escalado ingenuo de "raiz cuadrada del tiempo" VaR(h)=hVaR(1)\text{VaR}(h) = \sqrt{h}\,\text{VaR}(1) ignora esta reversion a la media y es erroneo precisamente despues de un shock, cuando mas necesitas el numero. Usa la propia senda de varianza a multiples pasos del modelo.

Segundo, la distribucion de un retorno a varios dias no tiene la misma forma que la innovacion a un dia. Sumar varios shocks diarios distribuidos como tt (a traves de la recursion GARCH no lineal) no da una distribucion tt al horizonte de hh dias; no hay una forma cerrada limpia. Para el VaR a varios dias la ruta honesta es la simulacion: extrae sendas de innovaciones de la distribucion estandarizada ajustada, pasalas por la recursion GARCH para obtener sendas de retorno simuladas, agrega a retornos de hh dias y lee el cuantil empirico. Eso tambien maneja de forma natural el caso skew-tt, donde no existe ningun cuantil analitico a multiples horizontes en absoluto. Las formulas analiticas a un paso de este articulo son exactas; trata cualquier atajo a multiples pasos como una aproximacion que hay que validar.

Backtesting de VaR: Kupiec y Christoffersen

Una prediccion de VaR es una afirmacion probabilistica: "la perdida superara este umbral solo en el (1α)(1-\alpha) de los dias". La pones a prueba contando violaciones (dias en los que la perdida realizada supero el VaR predicho) sobre una evaluacion walk-forward y comprobando dos cosas. Primero, es correcta la tasa de violaciones? Segundo, son las violaciones independientes, o se agrupan (lo que significa que el modelo falla exactamente cuando importa, durante los picos de volatilidad)?

Sea It=1{losst>VaRt}I_t = \mathbf{1}\{\text{loss}_t > \text{VaR}_t\} la secuencia de violaciones, N=ItN = \sum I_t el numero de violaciones sobre TT dias, y π^=N/T\hat{\pi} = N/T la tasa observada. Tasa objetivo p=1αp = 1-\alpha.

El test de cobertura incondicional de Kupiec (1995) comprueba π^p\hat\pi \approx p mediante una razon de verosimilitud:

LRuc=2log ⁣[pN(1p)TNπ^N(1π^)TN]    χ12.LR_{uc} = -2\log\!\left[\frac{p^{N}(1-p)^{T-N}}{\hat\pi^{N}(1-\hat\pi)^{T-N}}\right] \;\sim\; \chi^2_1.

El test de independencia de Christoffersen (1998) comprueba que una violacion hoy no esta predicha por una violacion ayer. Sea nijn_{ij} el conteo de transiciones del estado ii al estado jj en la secuencia de violaciones, π01=n01/(n00+n01)\pi_{01} = n_{01}/(n_{00}+n_{01}), π11=n11/(n10+n11)\pi_{11} = n_{11}/(n_{10}+n_{11}) y π=(n01+n11)/T\pi = (n_{01}+n_{11})/T. Entonces

LRind=2log ⁣[(1π)n00+n10πn01+n11(1π01)n00π01n01(1π11)n10π11n11]    χ12.LR_{ind} = -2\log\!\left[\frac{(1-\pi)^{n_{00}+n_{10}}\,\pi^{\,n_{01}+n_{11}}}{(1-\pi_{01})^{n_{00}}\pi_{01}^{n_{01}}(1-\pi_{11})^{n_{10}}\pi_{11}^{n_{11}}}\right] \;\sim\; \chi^2_1.

Los dos se combinan en el test de cobertura condicional LRcc=LRuc+LRindχ22LR_{cc} = LR_{uc} + LR_{ind} \sim \chi^2_2, que comprueba simultaneamente la tasa correcta y la independencia. Un modelo puede pasar Kupiec (el numero correcto de violaciones) y sin embargo fallar Christoffersen (todas se apinaron en una unica semana de desplome); ese es el modo de fallo que mas quieres detectar, porque las violaciones agrupadas son las que hacen saltar una cuenta.

from scipy.stats import chi2

def var_backtest(losses, var, p):
    """
    losses, var: 1D arrays, same length; loss>0 is a loss, var>0 threshold.
    p = 1 - alpha (target violation rate, e.g. 0.01 for 99% VaR).
    Returns Kupiec, Christoffersen-independence, and conditional-coverage LR.
    """
    losses = np.asarray(losses)
    var = np.asarray(var)
    I = (losses > var).astype(int)          # violation indicators
    T = len(I)
    N = int(I.sum())
    pi_hat = N / T

    eps = 1e-12
    ll_null = N * np.log(p + eps) + (T - N) * np.log(1 - p + eps)
    ll_alt  = N * np.log(pi_hat + eps) + (T - N) * np.log(1 - pi_hat + eps)
    LR_uc = -2 * (ll_null - ll_alt)
    p_uc = 1 - chi2.cdf(LR_uc, df=1)

    n00 = np.sum((I[:-1] == 0) & (I[1:] == 0))
    n01 = np.sum((I[:-1] == 0) & (I[1:] == 1))
    n10 = np.sum((I[:-1] == 1) & (I[1:] == 0))
    n11 = np.sum((I[:-1] == 1) & (I[1:] == 1))
    pi01 = n01 / (n00 + n01) if (n00 + n01) else 0.0
    pi11 = n11 / (n10 + n11) if (n10 + n11) else 0.0
    pi   = (n01 + n11) / (n00 + n01 + n10 + n11)

    def _ll(pi01, pi11, pi_pooled, use_pooled):
        if use_pooled:
            a = (n00 + n10) * np.log(1 - pi_pooled + eps)
            b = (n01 + n11) * np.log(pi_pooled + eps)
            return a + b
        return (n00 * np.log(1 - pi01 + eps) + n01 * np.log(pi01 + eps)
                + n10 * np.log(1 - pi11 + eps) + n11 * np.log(pi11 + eps))

    LR_ind = -2 * (_ll(pi01, pi11, pi, True) - _ll(pi01, pi11, pi, False))
    p_ind = 1 - chi2.cdf(LR_ind, df=1)

    LR_cc = LR_uc + LR_ind
    p_cc = 1 - chi2.cdf(LR_cc, df=2)

    return {
        "T": T, "violations": N,
        "obs_rate": pi_hat, "target_rate": p,
        "LR_uc": LR_uc, "p_uc": p_uc,
        "LR_ind": LR_ind, "p_ind": p_ind,
        "LR_cc": LR_cc, "p_cc": p_cc,
    }

Para generar honestamente las entradas losses/var, reajustas (o al menos vuelves a predecir) sobre una ventana creciente o movil y registras el VaR a un paso para cada dia fuera de muestra, y luego lo comparas con la perdida realizada de ese dia. Nunca hagas backtest de VaR dentro de muestra: un modelo ajustado sobre el mismo desplome que se le pide predecir parecera mucho mejor de lo que es. Esta es la misma disciplina que la paridad backtest-live: la evaluacion solo debe usar informacion disponible en el momento de la decision.

def walk_forward_var(returns, alpha=0.99, start=750, refit_every=25,
                     vol="Garch", o=1, dist="t"):
    """
    Expanding-window one-step VaR. Refit every `refit_every` days
    (refitting daily is correct but slow; every ~25 days is a common
    compromise -- validate the shortcut on your data).
    """
    losses, vars_ = [], []
    res = None
    for t in range(start, len(returns)):
        if res is None or (t - start) % refit_every == 0:
            am = arch_model(returns.iloc[:t], mean="Constant",
                            vol=vol, p=1, o=o, q=1, dist=dist)
            res = am.fit(disp="off")
        fc = res.forecast(horizon=1, reindex=False)
        mu = fc.mean.iloc[-1, 0]
        sig = np.sqrt(fc.variance.iloc[-1, 0])
        dp = [res.params[k] for k in res.params.index
              if k in ("nu", "eta", "lambda", "lam")]
        z_q = float(np.atleast_1d(
            res.model.distribution.ppf(1 - alpha, dp) if dp
            else res.model.distribution.ppf(1 - alpha))[0])
        var_t = -(mu + sig * z_q)
        vars_.append(var_t)
        losses.append(-returns.iloc[t])     # realized loss for that day
    return np.array(losses), np.array(vars_)

losses, vars_ = walk_forward_var(r, alpha=0.99, dist="t")
print(var_backtest(losses, vars_, p=0.01))

La lectura: un VaR al 99% bien calibrado muestra una tasa observada cercana al 1%, un Kupiec no significativo (p_uc grande) y un Christoffersen no significativo (p_ind grande), sin agrupamiento. En la practica el resultado honesto en cripto es que GARCH-Normal falla Kupiec (demasiadas violaciones, p_uc diminuto) mientras que GJR-tt o EGARCH-tt pasa o se acerca. Ese contraste es todo el argumento de este articulo expresado como un contraste de hipotesis. Si incluso el modelo tt muestra violaciones agrupadas (p_ind pequeno), tu dinamica de volatilidad sigue estando mal especificada, a menudo una senal de que necesitas una memoria mas larga (component/FIGARCH) o una capa de regimen, lo cual conecta con la deteccion de regimenes con HMMs.

Ordenar modelos por perdida de cola, no solo por aprobado/suspenso

Kupiec y Christoffersen te dan un veredicto binario: el modelo se rechaza o no. Eso es necesario pero grueso: dos modelos pueden "pasar" ambos mientras uno es significativamente mas afilado. Para ordenar predicciones de VaR competidoras, puntualas con una funcion de perdida estrictamente consistente para el cuantil, la perdida pinball (de cuantil):

Lτ(r,q)=(τ1{r<q})(rq),τ=1α,L_\tau(r, q) = \bigl(\tau - \mathbf{1}\{r < q\}\bigr)\,(r - q), \qquad \tau = 1-\alpha,

donde qq es el cuantil de VaR (con signo) y rr el retorno realizado. Promediada sobre los dias fuera de muestra, una perdida pinball media menor significa un cuantil mejor calibrado y mas afilado; como la perdida es consistente para el τ\tau-cuantil, minimizarla no premia a un modelo por ser perezosamente ancho. Para comparar dos modelos formalmente, alimenta sus diferencias de perdida por dia a un test de Diebold-Mariano.

def pinball_loss(returns, var, alpha=0.99):
    tau = 1 - alpha
    q = -np.asarray(var)               # VaR is a positive loss; quantile is negative
    r = np.asarray(returns)
    hit = (r < q).astype(float)
    return np.mean((tau - hit) * (r - q))

Para la Expected Shortfall en concreto, ten en cuenta que la ES no es elicitable por si sola (no existe una funcion de perdida cuyo minimizador sea la ES sola), lo cual es una autentica arruga teorica: evaluas la ES conjuntamente con el VaR usando las reglas de puntuacion de Fissler-Ziggel, o recurres a la practica mas simple de comprobar que la magnitud media de superacion coincide con la ES predicha por el modelo. Una comprobacion de ES tosca pero util: entre los dias de violacion del VaR, compara la perdida realizada media con la ES media predicha en esos dias; deberian estar cerca.

El encuadre regulatorio es el enfoque del semaforo de Basilea: sobre 250 dias de negociacion, 0-4 violaciones de un VaR al 99% es "verde" (aceptable), 5-9 es "amarillo" (escrutinio), 10+ es "rojo" (el modelo se rechaza y suben los multiplicadores de capital). Es un primo mas grueso de Kupiec, pero es el lenguaje que hablan de verdad los comites de riesgo, y vale la pena reportarlo junto a los estadisticos LR.

Consideraciones practicas

Cuando los parametros extra no compensan

El valor por defecto honesto es el escepticismo hacia la complejidad. Cada parametro que anades es una perilla que el optimizador puede sobreajustar, y el GARCH asimetrico de colas pesadas tiene varias. Guia concreta:

  • Muestras iliquidas o cortas. Con unos pocos cientos de observaciones diarias, el error tipico de γ\gamma y λ\lambda sera grande, y "detectaras" asimetrias que son ruido muestral. En una altcoin nueva o poco liquida, un GARCH-tt simetrico suele ser el modelo mas complejo que los datos pueden soportar. Ajustar un EGARCH skew-tt a 200 dias es enganarte a ti mismo.
  • El termino de asimetria a menudo no cubre su coste. En la practica, pasar de Normal → tt es una mejora grande y fiable (las colas pesadas son reales y fuertes). Pasar de tt → skew-tt suele ser marginal: una ganancia de BIC de 1 o 2, a veces negativa. Anade asimetria solo cuando los datos lo pidan con claridad.
  • EGARCH frente a GJR suele ser un empate en datos diarios. Codifican la misma historia cualitativa con formas funcionales distintas. Elige por el backtest de VaR fuera de muestra, no por cual tiene la log-verosimilitud mas bonita dentro de muestra.
  • La frecuencia mas alta cambia la respuesta. En barras horarias o de minutos, la estacionalidad intradia y la microestructura dominan, y un GARCH de estilo diario simple esta mal especificado con independencia de la asimetria. Problema distinto, herramientas distintas.

Esta es la misma leccion que la evaluacion honesta sin ventaja robusta: un modelo mas complejo que no sobrevive a la prueba fuera de muestra es peor que el simple al que reemplazo, porque acarrea la ilusion de la precision. Reporta el resultado negativo ("la asimetria no ayudo en ETH") como un hallazgo real, y usa la optimizacion walk-forward como arbitro, no el AIC dentro de muestra.

Estas son las marginales sobre las que construyen todos los demas

Los modelos de aqui no son un punto final; son el bloque de construccion univariante de la maquinaria de riesgo conjunto. El articulo sobre modelos de copula para el riesgo conjunto de cripto usa exactamente EGARCH/GJR-tt como las marginales GARCH-EVT antes de ajustar una copula vine: ajustas un GARCH asimetrico de colas pesadas por activo, extraes los residuos estandarizados y solo entonces modelas la dependencia entre activos. Si tu marginal es un GARCH gaussiano simetrico, la copula hereda sus errores de cola por muy bueno que sea el modelo de dependencia. Marginales basura, VaR conjunto basura.

Para el problema de volatilidad multivariante (correlaciones variables en el tiempo en lugar de varianzas por activo) consulta la Parte 3, DCC-GARCH, que superpone un modelo de correlacion dinamica sobre estos ajustes univariantes. Y para convertir una prediccion de volatilidad en dimensionamiento de posiciones y un backtest de trading, la Parte 4 sobre volatility targeting usa las predicciones de σt+1\sigma_{t+1} de estos mismos modelos para escalar la exposicion de forma inversa al riesgo predicho.

Una alternativa libre de distribucion

Todo en la seccion de riesgo descansa sobre una suposicion parametrica: que los residuos estandarizados siguen una tt o skew-tt. Esa suposicion es contrastable y suele ser razonable, pero puede fallar. Si prefieres no comprometerte con ninguna forma de cola, la prediccion conforme da intervalos de prediccion libres de distribucion con garantias de cobertura en muestra finita: una filosofia genuinamente distinta que no hace ninguna afirmacion sobre la distribucion de las innovaciones. Los dos enfoques son complementarios: el GARCH-tt parametrico te da una densidad condicional completa (y por tanto ES, que los intervalos conformes no proporcionan directamente), mientras que el conforme te da una cobertura que se mantiene incluso cuando tu densidad es erronea. En produccion, usar ambos como comprobacion cruzada es un seguro barato.

Higiene numerica y de flujo de trabajo

  • Escala los retornos por 100. Los optimizadores GARCH convergen de forma mucho mas fiable sobre retornos en porcentaje que sobre retornos fraccionales crudos. Recuerda desescalar VaR/ES si reportas en unidades fraccionales.
  • Vigila la persistencia. Si α+β+12γ\alpha + \beta + \tfrac12\gamma estima por encima de ~0.999, el modelo esta casi integrado (tipo IGARCH); las predicciones revierten a la media extremadamente despacio y las predicciones de varianza a largo horizonte se vuelven poco fiables. No necesariamente erroneo, pero senalalo.
  • Fallos de convergencia en ventanas moviles. La forma logaritmica de EGARCH evita las restricciones de positividad pero aun puede no converger en una ventana patologica. Envuelve fit() en un try/except y recurre a los parametros de la ventana anterior en lugar de hacer caer un backtest en vivo.
  • Modelo de media. Usamos una media constante en todo momento. Para la mayoria del cripto diario la media condicional esta cerca de cero y queda sepultada por la volatilidad; no gastes complejidad de modelo intentando predecirla a menos que tengas una razon real.

Resumen

  • El GARCH(1,1) simple tiene dos defectos estructurales: es simetrico (reacciona a +x%+x\% y x%-x\% de forma identica porque los shocks entran como ε2\varepsilon^2) y asume innovaciones gaussianas (infravalorando las colas pesadas de cripto). Ambos cuestan dinero real a traves de un VaR optimista.
  • GJR-GARCH anade un termino de umbral γI(εt1<0)εt12\gamma\,I(\varepsilon_{t-1}<0)\,\varepsilon_{t-1}^2. Un γ>0\gamma > 0 significativo es el efecto apalancamiento: las malas noticias elevan mas la volatilidad. La positividad necesita α+γ0\alpha+\gamma\ge0; la persistencia es α+β+12γ\alpha+\beta+\tfrac12\gamma.
  • EGARCH modela logσt2\log\sigma_t^2, de modo que no hay restricciones de positividad y la estacionariedad es solo β<1|\beta|<1. La asimetria entra a traves de un termino con signo γzt1\gamma z_{t-1} (el apalancamiento es γ<0\gamma<0 en esta convencion) separado de un termino de magnitud zt1|z_{t-1}|.
  • La curva de impacto de noticias (varianza del siguiente periodo frente al ultimo shock) hace visible la asimetria y verifica la convencion de signo de EGARCH de un vistazo.
  • Las innovaciones Student-tt (dist='t') corrigen las colas mediante un grado de libertad ν\nu (tipicamente 3-6 para cripto); la skew-tt de Hansen (dist='skewt') anade una asimetria λ\lambda para una cola izquierda mas pesada. Pasar de Normal → tt es una ganancia grande y fiable; tt → skew-tt suele ser marginal.
  • El VaR y la ES se derivan de la distribucion condicional ajustada: VaRα=(μt+1+σt+1Fz1(1α))\text{VaR}_\alpha = -(\mu_{t+1} + \sigma_{t+1}F_z^{-1}(1-\alpha)), con el cuantil de colas pesadas haciendo el riesgo honestamente mayor que el gaussiano. La ES (coherente, \approx CVaR) captura la perdida media mas alla del VaR.
  • Haz backtest con Kupiec y Christoffersen. Kupiec comprueba la tasa de violaciones; Christoffersen comprueba que las violaciones no esten agrupadas. Un modelo puede pasar uno y fallar el otro: las violaciones agrupadas son el modo de fallo peligroso. Haz el backtest estrictamente fuera de muestra.
  • Disciplina sobre complejidad. Anade asimetria/skew solo cuando sobreviva al BIC y a un backtest de VaR fuera de muestra. En series cortas o iliquidas, el modelo mas simple suele ganar.

References:

  • Nelson, D. B. (1991). Conditional heteroskedasticity in asset returns: A new approach. Econometrica, 59(2), 347-370. DOI
  • Glosten, L. R., Jagannathan, R., & Runkle, D. E. (1993). On the relation between the expected value and the volatility of the nominal excess return on stocks. Journal of Finance, 48(5), 1779-1801. DOI
  • Bollerslev, T. (1987). A conditionally heteroskedastic time series model for speculative prices and rates of return. Review of Economics and Statistics, 69(3), 542-547. DOI
  • Hansen, B. E. (1994). Autoregressive conditional density estimation. International Economic Review, 35(3), 705-730. DOI
  • Engle, R. F., & Ng, V. K. (1993). Measuring and testing the impact of news on volatility. Journal of Finance, 48(5), 1749-1778. DOI
  • Christoffersen, P. F. (1998). Evaluating interval forecasts. International Economic Review, 39(4), 841-862. DOI
  • Kupiec, P. H. (1995). Techniques for verifying the accuracy of risk measurement models. Journal of Derivatives, 3(2), 73-84. DOI
  • Black, F. (1976). Studies of stock price volatility changes. Proceedings of the American Statistical Association, Business and Economic Statistics Section, 177-181.
  • McNeil, A. J., Frey, R., & Embrechts, P. (2015). Quantitative Risk Management: Concepts, Techniques and Tools (2nd ed.). Princeton University Press.
  • Sheppard, K. (2023). arch: Autoregressive conditional heteroskedasticity models in Python. GitHub.
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.