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

Targeting de volatilidad y trading con pronósticos GARCH

#volatility
#GARCH
#volatility-targeting
#backtesting
#risk
#crypto
#algorithmic-trading

Las primeras tres partes de esta serie te enseñaron a pronosticar la volatilidad. Construimos un GARCH(1,1) univariado en la Parte 1, agregamos apalancamiento y colas gruesas con GJR e innovaciones Student-t en la Parte 2, y modelamos toda una matriz de covarianza a lo largo del tiempo con DCC-GARCH en la Parte 3. Al final de cada una, imprimimos un número: la volatilidad esperada de mañana. Y luego, si somos honestos, nos detuvimos, como si producir el pronóstico fuera el objetivo.

No lo es. Un pronóstico de volatilidad no es un P&L. Nadie ha cobrado jamás por un buen QLIKE. Un pronóstico solo vale algo en el momento exacto en que cambia una decisión que de otro modo habrías tomado diferente: cuánto comprar, cuándo recortar, cuánto capital asignar. Si tu pronóstico no mueve una posición, su precisión estadística es un hobby privado.

Esta última parte trata de cerrar ese ciclo. Tomamos los pronósticos de las Partes 1-3 y los ponemos a trabajar en la decisión más limpia posible: targeting de volatilidad, dimensionar una posición de modo que la volatilidad realizada del portafolio alcance un objetivo constante. Luego hacemos lo que a este blog le importa más que cualquier modelo individual: evaluamos honestamente. Comparamos GARCH contra referencias simples pero fuertes (volatilidad realizada rodante, EWMA), usamos funciones de pérdida robustas al hecho de que nunca podemos observar la volatilidad verdadera, corremos un backtest walk-forward con costos y sin look-ahead, y decimos claramente qué compra y qué no compra el targeting de volatilidad. Spoiler: mejora los retornos ajustados por riesgo y controla los drawdowns con mucha más fiabilidad de la que fabrica alfa.

Por qué el targeting de volatilidad es la prueba correcta

Hay formas más sofisticadas de usar un pronóstico de volatilidad (pricing de opciones, límites de VaR, hedging dinámico), pero el targeting de volatilidad es la que aísla el valor del pronóstico con la menor contaminación de otras apuestas. La idea es una sola ecuación.

Mantener un activo riesgoso con una dirección implicada por la señal (por ahora, simplemente "long"). En lugar de una posición fija, escalamos la exposición inversamente a la volatilidad pronosticada:

wt=σtargetσ^t(luego topado)w_t = \frac{\sigma_{\text{target}}}{\hat{\sigma}_{t}} \quad \text{(luego topado)}

donde σ^t\hat{\sigma}_t es tu pronóstico de la volatilidad del siguiente periodo (hecho usando solo datos hasta tt), y σtarget\sigma_{\text{target}} es la volatilidad anualizada a la que quieres que corra la estrategia, digamos 15% o 20%. Cuando el modelo pronostica un mercado tranquilo, apalancas hacia (o más allá de) 1.0; cuando pronostica una tormenta, te reduces. La volatilidad realizada de la posición escalada es, a primer orden,

Vol(wtrt+1)=wtσt+1=σtargetσ^tσt+1σtarget\text{Vol}(w_t r_{t+1}) = w_t \, \sigma_{t+1} = \frac{\sigma_{\text{target}}}{\hat\sigma_t}\, \sigma_{t+1} \approx \sigma_{\text{target}}

siempre que σ^tσt+1\hat\sigma_t \approx \sigma_{t+1}. Así que toda la calidad del ejercicio descansa en una sola cosa: qué tan cerca está tu pronóstico σ^t\hat\sigma_t de la volatilidad real del siguiente periodo σt+1\sigma_{t+1}. Un mejor pronóstico produce un perfil de volatilidad realizada más plano y, como veremos, un mejor Sharpe. Por eso esta es la prueba correcta. El pronóstico no es decoración; es el denominador.

Por qué esto eleva el Sharpe y controla los drawdowns

Dos hechos empíricos hacen el trabajo aquí.

La volatilidad es mucho más pronosticable que los retornos. La dirección del retorno de BTC de mañana es cercana a lanzar una moneda a frecuencia diaria; la magnitud no lo es. La volatilidad se agrupa (los movimientos grandes siguen a movimientos grandes), que es la razón entera por la que GARCH existe (la Parte 1 derivó la estructura AR(1)-en-varianza que codifica esto). Un R2R^2 de 0.4-0.6 para la varianza a un día vista es rutinario; el mismo número para los retornos sería una señal de nivel Renaissance. El targeting de volatilidad explota la cantidad pronosticable y se mantiene agnóstico sobre la que no lo es.

Los ratios de Sharpe no son constantes en el tiempo; caen cuando la volatilidad se dispara. Los regímenes de alta volatilidad en cripto (cascadas de desapalancamiento, fallas de exchanges, los días en que todo salta 30%) tienden a tener peor retorno por unidad de riesgo, no mejor. Al recortar mecánicamente la exposición exactamente cuando la volatilidad pronosticada es alta, subponderas los periodos que más contribuyen a los drawdowns y menos al retorno compuesto. Moreira y Muir (2017) mostraron para acciones que los portafolios gestionados por volatilidad (exactamente este escalado 1/σ21/\sigma^2) elevan los ratios de Sharpe y producen alfas positivos frente al factor no gestionado. El mecanismo no es magia; es negarse a mantener una posición en dólares constante en ventanas prediciblemente turbulentas.

El beneficio en drawdown es aún más directo. El drawdown máximo está dominado por la cola de la distribución de retornos de la posición. Dado que wtrt+1w_t r_{t+1} tiene volatilidad fijada cerca de σtarget\sigma_{\text{target}}, la cola izquierda gruesa que una estrategia de nocional fijo sufre durante una explosión de volatilidad se comprime: ya eras pequeño al entrar. El targeting de volatilidad no predice crashes, pero está sistemáticamente subexpuesto cuando el mercado está agitado, y la agitación es cuando ocurren los crashes.

Relación con Kelly y el sizing fraccional

El targeting de volatilidad es primo hermano del criterio de Kelly. Para un solo activo con retorno excedente esperado μ\mu y varianza σ2\sigma^2, la fracción óptima de crecimiento (Kelly completo) es

f=μσ2.f^\star = \frac{\mu}{\sigma^2}.

Si asumes que el ratio de Sharpe μ/σ\mu/\sigma es aproximadamente constante (una suposición fuerte, pero la implícita en "el mercado paga un precio estable por el riesgo"), entonces μ=Sσ\mu = S\sigma y f=S/σf^\star = S/\sigma, que es exactamente el targeting de volatilidad con σtarget=S(fraccioˊn Kelly)\sigma_{\text{target}} = S \cdot (\text{fracción Kelly}). En otras palabras, el targeting de volatilidad es sizing Kelly bajo el supuesto de que el retorno esperado escala con la volatilidad. Trabajamos Kelly completo y fraccional, y por qué nadie cuerdo opera Kelly completo, en criterio de Kelly y sizing de estrategias. La lección práctica de ahí se traslada: usa una fracción del tamaño teórico, porque el error de estimación en el denominador (tu pronóstico de vol) y especialmente en el numerador (retorno esperado) hace que el sizing completo sea peligrosamente agresivo.

Vale la pena tener en mente dos conexiones más. Primero, el targeting de vol dimensiona sobre la dispersión simétrica de los retornos, pero los payoffs de cripto no son simétricos: el costo de un día de -20% no es el espejo de un día de +20% una vez que entran en juego el apalancamiento y la liquidación. Tratamos esa asimetría directamente en asimetría pérdida-ganancia, y un pronóstico GJR/EGARCH (Parte 2) ya incorpora parte de ella en σ^t\hat\sigma_t al reaccionar más a los shocks negativos. Segundo, un pronóstico de vol es una estimación puntual; una visión de riesgo más completa le adjunta un intervalo. Predicción conforme para trading muestra cómo convertir las salidas del modelo en intervalos libres de distribución contra los cuales dimensionar, lo que combina naturalmente con el tema de evaluación honesta de este artículo.

Los competidores: lo que GARCH tiene que superar

Aquí está la disciplina que separa una evaluación real de una demo. Antes de coronar a GARCH, debes darle oponentes que sean baratos, obvios y sorprendentemente difíciles de vencer. Si tu elaborado modelo GJR-t no puede superar a un EWMA de cinco líneas, has aprendido algo valioso y te ahorraste mucha complejidad de producción.

Comparamos cuatro pronosticadores de la volatilidad del siguiente periodo.

(a) Volatilidad realizada rodante (desviación estándar móvil)

El pronóstico más ingenuo: la volatilidad de mañana es igual a la desviación estándar muestral de los últimos nn retornos diarios.

σ^tRV=1n1i=1n(rti+1rˉ)2\hat\sigma_{t}^{\text{RV}} = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}\bigl(r_{t-i+1}-\bar r\bigr)^2}

Tiene un hiperparámetro (la ventana nn, típicamente 20-60 días) y ningún modelo. Su falla es que cada observación en la ventana recibe el mismo peso y luego cae abruptamente por un precipicio cuando sale, el efecto de "fantasma" o "eco", donde un solo día de crash infla el pronóstico durante exactamente nn días y luego desaparece de la noche a la mañana, haya o no el mercado realmente calmado.

(b) EWMA / RiskMetrics (λ=0.94\lambda = 0.94)

El promedio móvil ponderado exponencialmente corrige el problema del eco al dar un peso geométricamente decreciente a los retornos al cuadrado más antiguos:

σ^t2=λσ^t12+(1λ)rt2\hat\sigma_{t}^{2} = \lambda\,\hat\sigma_{t-1}^{2} + (1-\lambda)\,r_{t}^{2}

Este es el estimador RiskMetrics (J.P. Morgan, 1996). Con el canónico λ=0.94\lambda = 0.94 para datos diarios, la memoria efectiva es de aproximadamente 1/(1λ)171/(1-\lambda) \approx 17 días, pero la decadencia es suave, sin precipicio. Nota lo que EWMA realmente es: es un GARCH(1,1) integrado con ω=0\omega = 0 y α+β=1\alpha + \beta = 1, es decir, GARCH sin reversión a la media y sin varianza de largo plazo. Tiene cero parámetros libres si aceptas λ=0.94\lambda = 0.94, y es la única referencia más dura de todo este artículo. Una gran fracción de los papers "GARCH vence a X" fallan silenciosamente en superar a EWMA fuera de muestra.

import numpy as np
import pandas as pd

def ewma_vol(returns: pd.Series, lam: float = 0.94, sigma0: float | None = None) -> pd.Series:
    """RiskMetrics EWMA conditional volatility (returns in decimal, e.g. 0.03).

    sigma2_t = lam * sigma2_{t-1} + (1 - lam) * r_t^2
    Returns a series aligned to `returns` where value at t uses info up to t.
    """
    r2 = np.square(returns.values)
    var = np.empty_like(r2)
    var[0] = sigma0**2 if sigma0 is not None else r2[0]
    for t in range(1, len(r2)):
        var[t] = lam * var[t - 1] + (1.0 - lam) * r2[t - 1]  # note: r_{t-1}, no look-ahead
    return pd.Series(np.sqrt(var), index=returns.index, name="ewma_vol")

La sutileza que hace tropezar a la gente: el pronóstico para el periodo tt (usable para dimensionar una posición mantenida durante tt) debe construirse a partir de retornos observados antes de tt. En la recursión de arriba, var[t] usa r2[t-1], así que la serie es un genuino pronóstico un paso adelante. Acertar este índice es la diferencia entre un backtest y una fantasía, más sobre esto en la sección de walk-forward.

(c) GARCH(1,1) y GJR-t (Partes 1-2)

Nuestros protagonistas. GARCH(1,1) estándar:

σt2=ω+αϵt12+βσt12,ω>0, α,β0, α+β<1\sigma_t^2 = \omega + \alpha\, \epsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2, \qquad \omega>0,\ \alpha,\beta\ge0,\ \alpha+\beta<1

con varianza de largo plazo σˉ2=ω/(1αβ)\bar\sigma^2 = \omega/(1-\alpha-\beta) y el pronóstico a un paso saliendo directamente de la recursión (Parte 1). La extensión GJR-GARCH agrega un término de apalancamiento para que los shocks negativos eleven la varianza más que los positivos:

σt2=ω+(α+γ1[ϵt1<0])ϵt12+βσt12\sigma_t^2 = \omega + (\alpha + \gamma\, \mathbb{1}[\epsilon_{t-1}<0])\,\epsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2

y emparejado con innovaciones Student-t para manejar las colas gruesas de cripto, este es el GJR-t de la Parte 2. La razón por la que GARCH puede vencer a EWMA es la reversión a la media: después de un shock, GARCH tira el pronóstico de vuelta hacia σˉ2\bar\sigma^2 a una tasa gobernada por α+β\alpha+\beta, mientras que EWMA (al ser integrado) nunca revierte. Cuando la volatilidad se dispara y luego se normaliza, el caso común, el pronóstico de GARCH decae de vuelta más rápido y con mayor precisión. Cuando la volatilidad es genuinamente persistente, los dos son casi indistinguibles.

(d) HAR-RV sobre varianza realizada (si tienes datos intradía)

Si tienes barras intradía (y en mercados cripto 24/7 casi siempre las tienes), puedes construir un proxy de volatilidad mucho menos ruidoso que los retornos diarios al cuadrado: la varianza realizada, la suma de los retornos intradía al cuadrado durante el día.

RVt=i=1Mrt,i2(p. ej. M=288 barras de cinco minutos)RV_t = \sum_{i=1}^{M} r_{t,i}^2 \qquad (\text{p. ej. } M = 288 \text{ barras de cinco minutos})

El modelo Heterogeneous Autoregressive de Corsi (2009) pronostica la varianza realizada de mañana a partir de promedios diarios, semanales y mensuales de la RVRV pasada, una forma tosca pero notablemente efectiva de capturar la persistencia de memoria larga con tres regresores:

RVt+1=β0+βdRVt+βwRVt(5)+βmRVt(22)+ut+1RV_{t+1} = \beta_0 + \beta_d\, RV_t + \beta_w\, \overline{RV}_t^{(5)} + \beta_m\, \overline{RV}_t^{(22)} + u_{t+1}

donde RVt(5)\overline{RV}_t^{(5)} y RVt(22)\overline{RV}_t^{(22)} son los promedios móviles de 5 días y 22 días de la RVRV diaria. Es una regresión OLS simple, explota el proxy intradía de mayor calidad, y con frecuencia es el mejor pronosticador de vol diaria de los cuatro, a menudo venciendo a GARCH precisamente porque RVRV es un objetivo más limpio que r2r^2.

import numpy as np
import pandas as pd

def realized_variance(intraday_returns: pd.Series, day_index) -> pd.Series:
    """Daily realized variance = sum of squared intraday (log) returns per day.
    `intraday_returns` indexed by timestamp; `day_index` maps to calendar day.
    """
    return intraday_returns.pow(2).groupby(day_index).sum()

def har_features(rv: pd.Series) -> pd.DataFrame:
    """Build HAR regressors from a daily realized-variance series."""
    df = pd.DataFrame({"rv": rv})
    df["rv_d"] = df["rv"].shift(1)                          # yesterday
    df["rv_w"] = df["rv"].shift(1).rolling(5).mean()        # trailing week
    df["rv_m"] = df["rv"].shift(1).rolling(22).mean()       # trailing month
    df["target"] = df["rv"]                                 # predict today's RV
    return df.dropna()

def fit_har(rv: pd.Series):
    """Fit HAR-RV by OLS. Returns (coef_dict, predict_fn)."""
    df = har_features(rv)
    X = np.column_stack([np.ones(len(df)), df["rv_d"], df["rv_w"], df["rv_m"]])
    y = df["target"].values
    beta, *_ = np.linalg.lstsq(X, y, rcond=None)
    names = ["const", "rv_d", "rv_w", "rv_m"]

    def predict(rv_d, rv_w, rv_m):
        return float(beta @ np.array([1.0, rv_d, rv_w, rv_m]))

    return dict(zip(names, beta)), predict

Una nota sobre log-HAR: dado que RVRV tiene sesgo a la derecha y es estrictamente positiva, muchos practicantes regresan logRVt+1\log RV_{t+1} sobre características HAR en log, lo que mejora el ajuste y garantiza pronósticos positivos. Cuando exponencias de vuelta deberías agregar una corrección de Jensen de media varianza, RV^=exp(y^+12σ^u2)\widehat{RV} = \exp(\hat{y} + \tfrac{1}{2}\hat\sigma_u^2), o subpronosticarás sistemáticamente.

Con cuatro pronosticadores en mano (RV, EWMA, GARCH/GJR-t, HAR), la pregunta se convierte en: ¿cómo decidimos cuál es mejor, cuando nunca podemos ver aquello que todos están tratando de predecir?

Evaluar un pronóstico de volatilidad honestamente

Esta es la enseñanza central del artículo, así que desacelera aquí.

Quieres comparar pronósticos σ^t2\hat\sigma_t^2 contra la varianza condicional verdadera σt2\sigma_t^2. Pero σt2\sigma_t^2 es una cantidad latente: es un parámetro del proceso generador de datos, nunca observado directamente. Todo lo que obtienes es un retorno realizado por día. Así que toda evaluación de volatilidad es en realidad una comparación de tu pronóstico contra un proxy ruidoso de la verdad. Los dos proxies estándar:

  • Retornos al cuadrado rt2r_t^2. No sesgado para σt2\sigma_t^2 bajo un modelo de media cero (E[rt2Ft1]=σt2\mathbb{E}[r_t^2 \mid \mathcal{F}_{t-1}] = \sigma_t^2), pero extremadamente ruidoso: un solo retorno diario es una estimación de una observación de una desviación estándar. El proxy rt2r_t^2 puede ser 0 (un día plano) incluso cuando la vol verdadera es alta, o enorme en un día de cola con suerte.
  • Varianza realizada RVtRV_t a partir de datos intradía. Mucho menos ruidosa (el muestreo intradía promedia el ruido idiosincrático de un solo retorno), que es exactamente por qué HAR-RV funciona y por qué deberías usar RVRV como tu proxy si tienes datos intradía disponibles.

La sutileza que atrapa a casi todos: como el proxy es ruidoso, la elección de la función de pérdida no es inocente. Rankear dos pronósticos con la pérdida incorrecta y el proxy ruidoso puede invertir el ranking, diciéndote que el peor pronóstico es mejor. Patton (2011) trabajó con precisión qué funciones de pérdida son "robustas" en el sentido de que rankear pronósticos por la pérdida esperada sobre el proxy ruidoso da el mismo ranking que obtendrías sobre la varianza verdadera (inobservable). Solo una familia específica califica. Dos miembros importan en la práctica.

MSE vs QLIKE

El error cuadrático medio sobre la varianza:

LMSE(σ2,h)=(σ2h)2L_{\text{MSE}}(\sigma^2, h) = (\sigma^2 - h)^2

donde h=σ^2h = \hat\sigma^2 es el pronóstico y σ2\sigma^2 es proxied por r2r^2 o RVRV. MSE es robusto en el sentido de Patton (su ranking es consistente con el proxy), pero es simétrico y dependiente de la escala: penaliza un sobre-pronóstico y un sub-pronóstico del mismo tamaño absoluto por igual, y pondera los errores durante periodos de alta volatilidad enormemente más que los errores durante periodos tranquilos. Un modelo que acierta el 95% de días tranquilos pero explota en los pronósticos de varianza en los tres días de crisis se verá terrible bajo MSE, aunque su comportamiento en crisis sea lo que realmente quieres.

La pérdida QLIKE (cuasi-verosimilitud) es el caballo de batalla:

LQLIKE(σ2,h)=σ2hlogσ2h1L_{\text{QLIKE}}(\sigma^2, h) = \frac{\sigma^2}{h} - \log\frac{\sigma^2}{h} - 1

Es la pérdida implicada por una verosimilitud gaussiana sobre la varianza, también es robusta en el sentido de Patton, y tiene dos propiedades que la hacen la elección preferida para volatilidad. Primero, es asimétrica en la dirección correcta: penaliza el sub-pronóstico de varianza más que el sobre-pronóstico. Para un gestor de riesgo o un vol-targeter, esa es la asimetría correcta: sub-pronosticar la vol significa que asumiste demasiado tamaño justo antes de que importara, que es el error costoso. Segundo, es (aproximadamente) invariante a la escala: porque depende del ratio σ2/h\sigma^2/h, un error de pronóstico del 10% cuesta aproximadamente lo mismo ocurra en un día tranquilo o en un día de crisis, así que la evaluación no es secuestrada por un puñado de observaciones de alta varianza como ocurre con MSE. Esa robustez a la heterocedasticidad del proxy es exactamente lo que quieres cuando todo el punto es que la volatilidad varía enormemente.

Nota que LQLIKE0L_{\text{QLIKE}} \ge 0, con igualdad si y solo si h=σ2h = \sigma^2. Menor es mejor, igual que MSE.

import numpy as np

def qlike(proxy: np.ndarray, forecast_var: np.ndarray) -> np.ndarray:
    """Per-observation QLIKE loss. `proxy` is r^2 or RV (a variance proxy),
    `forecast_var` is h = sigma_hat^2. Both strictly positive, same units.
    """
    ratio = proxy / forecast_var
    return ratio - np.log(ratio) - 1.0

def mse_var(proxy: np.ndarray, forecast_var: np.ndarray) -> np.ndarray:
    """Per-observation MSE on the variance scale."""
    return np.square(proxy - forecast_var)

Dos advertencias operativas. Mantén el proxy y el pronóstico en unidades idénticas (ambos varianzas diarias, o ambos anualizadas) o el ratio no tiene sentido. Y nunca dejes que forecast_var llegue a cero, recórtalo a un piso pequeño, porque log0\log 0 envenenará todo el promedio.

Regresión de Mincer-Zarnowitz

Un solo número de pérdida te dice cuál pronóstico es mejor; no te dice cómo un pronóstico está equivocado. La regresión de Mincer-Zarnowitz (1969) sí lo hace. Regresa el proxy sobre el pronóstico:

rt2  =  a+bσ^t2+ut.r_t^2 \;=\; a + b\,\hat\sigma_t^2 + u_t.

Bajo un pronóstico óptimo e insesgado, a=0a = 0 y b=1b = 1: en promedio la varianza realizada iguala al pronóstico. Las desviaciones diagnostican la patología:

  • b<1b < 1 con a>0a > 0: la firma clásica de un pronóstico que es demasiado volátil, sobre-reacciona, prediciendo extremos que no se materializan del todo. Muy común en modelos impulsados por retornos al cuadrado crudos.
  • b>1b > 1: el pronóstico sub-reacciona, escalando muy poco con la varianza verdadera.
  • R2R^2 de regresión bajo: incluso si a,ba,b se ven bien en promedio, el pronóstico rastrea la varianza pobremente día a día. Como el proxy es tan ruidoso, no te alarmes de que el R2R^2 de MZ contra r2r^2 frecuentemente sea solo 0.05-0.20; contra RVRV será mucho mayor. El R2R^2 contra r2r^2 está acotado bien por debajo de 1 sin importar qué tan bueno sea el pronóstico, puramente por el ruido del proxy.

Una prueba FF conjunta de H0:(a,b)=(0,1)H_0: (a,b) = (0,1) da una verificación formal de calibración. En la práctica, usa MZ como diagnóstico para entender un pronóstico, y QLIKE para rankear pronósticos.

Diebold-Mariano: ¿es real la diferencia?

Supón que el QLIKE promedio de GARCH es 0.183 y el de EWMA es 0.191. GARCH "gana". Pero ¿es 0.008 una ventaja real o ruido de muestreo? La prueba de Diebold-Mariano (1995) responde exactamente esto. Define el diferencial de pérdida por periodo

dt=L(proxyt,htA)L(proxyt,htB)d_t = L(\text{proxy}_t, h^A_t) - L(\text{proxy}_t, h^B_t)

para dos pronósticos AA y BB (aquí LL = QLIKE). La nula es precisión predictiva igual, H0:E[dt]=0H_0: \mathbb{E}[d_t] = 0. El estadístico es el diferencial medio estandarizado por su error estándar de largo plazo (HAC), porque dtd_t está serialmente correlacionado:

DM=dˉLRV^(dt)/T  d  N(0,1)\text{DM} = \frac{\bar d}{\sqrt{\widehat{\mathrm{LRV}}(d_t)/T}} \;\xrightarrow{d}\; \mathcal{N}(0,1)

donde LRV^\widehat{\mathrm{LRV}} es una estimación de varianza de largo plazo tipo Newey-West. Un estadístico DM más allá de ±1.96\pm 1.96 rechaza precisión igual al 5%. Crucialmente, DM es una prueba sobre pronósticos, no sobre modelos anidados, y maneja la dependencia serial en la serie de pérdida que una prueba tt ingenua sobre dtd_t ignoraría.

import numpy as np
from scipy import stats

def diebold_mariano(loss_a: np.ndarray, loss_b: np.ndarray, h: int = 1):
    """Diebold-Mariano test of equal predictive accuracy.
    loss_a, loss_b: per-period losses (e.g. QLIKE) for forecasts A and B.
    h: forecast horizon; Newey-West lag = h - 1 (>=0), plus a small-sample buffer.
    Returns (DM_stat, p_value). Negative DM => A has lower loss (A better).
    """
    d = np.asarray(loss_a) - np.asarray(loss_b)
    T = len(d)
    d_bar = d.mean()

    lag = max(h - 1, 0)
    gamma0 = np.mean((d - d_bar) ** 2)
    lrv = gamma0
    for k in range(1, lag + 1):
        w = 1.0 - k / (lag + 1)
        cov = np.mean((d[k:] - d_bar) * (d[:-k] - d_bar))
        lrv += 2.0 * w * cov

    dm = d_bar / np.sqrt(lrv / T)

    adj = np.sqrt((T + 1 - 2 * h + h * (h - 1) / T) / T)
    dm *= adj
    p = 2.0 * (1.0 - stats.t.cdf(abs(dm), df=T - 1))
    return float(dm), float(p)

El resultado ilustrativo en ese comentario es el desenlace honesto y común, y es toda la razón por la que existe esta sección: GARCH a menudo registra una pérdida promedio ligeramente menor que EWMA, y con la misma frecuencia esa ventaja no supera la barra de significancia de DM. Si solo reportas el QLIKE promedio, te convencerás de ventajas que una prueba DM habría vetado. Reporta el estadístico DM. Esta es la misma disciplina que aplicamos a los retornos de estrategias en evaluación honesta sin ventaja robusta: una estimación puntual que vence a una referencia no es una ventaja hasta que hayas descartado que sea ruido.

El backtest: una estrategia de targeting de volatilidad walk-forward

Ahora combinamos las dos mitades (un pronosticador y una regla de sizing) en una estrategia y la evaluamos de la única manera que significa algo: walk-forward, fuera de muestra, con costos.

La estrategia es deliberadamente simple, porque la simplicidad es lo que nos permite atribuir el resultado al pronóstico de vol en lugar de a una señal ingeniosa. BTC solo-long, con targeting de volatilidad. Cada día, pronosticamos la volatilidad del día siguiente, fijamos la posición en wt=min(σtarget/σ^t, wmax)w_t = \min(\sigma_{\text{target}}/\hat\sigma_t,\ w_{\max}), mantenemos overnight, y repetimos. Una variante long/flat cierra la posición cuando un filtro de tendencia es negativo; una variante de portafolio pequeño dimensiona sobre la matriz de covarianza DCC de la Parte 3 en lugar de la varianza de un solo activo. Describimos el caso solo-long completo y anotamos las extensiones.

Mecánica walk-forward y el contrato de no look-ahead

La propiedad más importante de este backtest es que toda cantidad usada para dimensionar la posición del día t+1t+1 es computable usando solo datos disponibles al cierre del día tt. Los parámetros de GARCH se reestiman en una ventana rodante que termina en tt; el pronóstico es el σ^t+1\hat\sigma_{t+1} a un paso adelante de ese ajuste; la posición se fija a partir de ese pronóstico; y el retorno ganado es wt+1rt+1w_{t+1} r_{t+1}, donde rt+1r_{t+1} es el retorno del siguiente día, que el modelo nunca vio. Reajustar GARCH sobre toda la muestra y luego "pronosticar" el pasado es la forma más común en que la gente fabrica accidentalmente un backtest excelente. Tratamos esta trampa de look-ahead y la metodología general en optimización walk-forward.

import numpy as np
import pandas as pd
from arch import arch_model

def walk_forward_voltarget(
    returns: pd.Series,          # daily log returns in decimal (e.g. 0.021)
    proxy_var: pd.Series,        # variance proxy aligned to returns (RV or r^2)
    target_vol_annual: float = 0.20,
    window: int = 750,           # rolling estimation window (days)
    refit_every: int = 5,        # refit GARCH weekly, forecast daily (cost saver)
    w_max: float = 3.0,          # leverage cap
    cost_bps: float = 5.0,       # per-unit-turnover cost in basis points
    ann: int = 365,              # crypto trades 365 days/yr
):
    """Long-only vol-targeted BTC, GARCH(1,1)-t forecasts, strictly walk-forward.
    Returns a DataFrame with position, forecast vol, net returns, and turnover.
    """
    idx = returns.index
    target_daily = target_vol_annual / np.sqrt(ann)

    fcast_vol = pd.Series(index=idx, dtype=float, name="fcast_vol")
    last_res = None

    for i in range(window, len(returns) - 1):
        if (i - window) % refit_every == 0 or last_res is None:
            train = returns.iloc[i - window:i + 1] * 100.0   # scale for the optimizer
            am = arch_model(train, mean="Constant", vol="GARCH",
                            p=1, o=1, q=1, dist="t")           # GJR-t, Part 2
            last_res = am.fit(disp="off")

        f = last_res.forecast(horizon=1, reindex=False)
        var_next = f.variance.values[-1, 0] / (100.0 ** 2)    # unscale
        fcast_vol.iloc[i + 1] = np.sqrt(var_next)

    raw_w = (target_daily / fcast_vol).clip(upper=w_max)
    position = raw_w.shift(0)                                  # w_{i+1} known at close i
    gross_ret = position * returns                            # earned on day i+1

    turnover = position.diff().abs().fillna(0.0)
    cost = turnover * (cost_bps / 1e4)
    net_ret = (gross_ret - cost).dropna()

    out = pd.DataFrame({
        "position": position,
        "fcast_vol": fcast_vol,
        "gross_ret": gross_ret,
        "turnover": turnover,
        "net_ret": net_ret,
    }).dropna()
    return out

def performance_stats(net_ret: pd.Series, ann: int = 365) -> dict:
    """Sharpe, realized vol, max drawdown, CAGR, annual turnover."""
    mu = net_ret.mean() * ann
    sigma = net_ret.std() * np.sqrt(ann)
    sharpe = mu / sigma if sigma > 0 else np.nan
    equity = (1.0 + net_ret).cumprod()
    peak = equity.cummax()
    max_dd = (equity / peak - 1.0).min()
    cagr = equity.iloc[-1] ** (ann / len(net_ret)) - 1.0
    return {
        "sharpe": round(sharpe, 2),
        "realized_vol": round(sigma, 3),
        "max_drawdown": round(max_dd, 3),
        "cagr": round(cagr, 3),
    }

Algunas notas de implementación que importan más de lo que parecen:

  • Escalado para el optimizador. Los ajustes de arch son numéricamente más felices cuando los retornos están en porcentaje, de ahí el * 100 y el / 100**2 correspondiente al desescalar la varianza. Olvida el desescalado y tu vol objetivo estará desfasada por 10,000x.
  • Cadencia de reajuste. Reestimar los parámetros de GARCH todos los días es costoso y agrega casi nada, los parámetros son estables semana a semana. Reajustar semanalmente (refit_every=5) mientras se pronostica diariamente (la recursión actualiza σt2\sigma_t^2 con nuevos retornos incluso sin reajustar) es el compromiso estándar. Esto refleja el consejo de caching del pipeline de cópulas en modelos de cópula para riesgo conjunto.
  • El tope wmaxw_{\max} no es cosmético. Cuando la vol pronosticada colapsa en un régimen muerto de calma, σtarget/σ^t\sigma_{\text{target}}/\hat\sigma_t puede explotar a 5x, 10x de apalancamiento. El targeting de vol sin tope te entregará felizmente un apalancamiento catastrófico justo antes de un cambio de régimen de volatilidad, el momento exacto en que el pronóstico está a punto de estar más equivocado. Ponle un tope (3x aquí) y reconoce que el tope se activará precisamente en los periodos más tranquilos y, en retrospectiva, más peligrosos.
  • Los costos escalan con el turnover, y el targeting de vol es una máquina de turnover. Cada tambaleo en el pronóstico redimensiona la posición. En un activo de baja vol con un pronóstico nervioso puedes rotar el libro diariamente. El término cost_bps no es un detalle de redondeo; para un vol-target de alto turnover puede comerse una fracción significativa de la mejora bruta del Sharpe.

Cómo se ve la salida (ilustrativo)

Correr esto sobre datos diarios de BTC a lo largo de una ventana de varios años, comparando los cuatro pronosticadores como el denominador de sizing, tiende a producir una tabla con la siguiente forma. Los números de abajo son ilustrativos, elegidos a mano para mostrar el patrón típico, no la salida de un backtest real, pero el orden y las magnitudes son representativos de lo que reportan los practicantes.

Pronóstico de sizing Sharpe Vol realizada Vol objetivo Drawdown máximo Turnover anual
Nocional fijo (sin targeting) 0.71 0.68 -0.78 0.1x
RV rodante (60d) 0.94 0.24 0.20 -0.41 12x
EWMA (λ=0.94\lambda=0.94) 1.02 0.21 0.20 -0.35 19x
GARCH(1,1)-t 1.05 0.21 0.20 -0.33 22x
HAR-RV (proxy intradía) 1.09 0.20 0.20 -0.31 20x

Dos variantes que vale la pena construir

El caso solo-long aísla el pronóstico, pero dos extensiones son lo bastante comunes como para mostrarlas explícitamente.

Long/flat con una compuerta de tendencia. El targeting de vol dimensiona la posición pero no toma ninguna visión direccional, siempre está long. Una mejora barata y honesta es cerrar la posición cuando un filtro de tendencia lento se vuelve negativo, de modo que mantengas el long con targeting de vol solo en tendencias alcistas y te quedes plano en caso contrario. Esto mantiene la lógica de sizing idéntica y superpone un filtro de régimen tosco encima; no pretende cronometrar entradas, solo evitar mantener posiciones durante tendencias bajistas obvias.

def apply_trend_gate(position: pd.Series, price: pd.Series, fast: int = 20, slow: int = 100):
    """Zero out the (already vol-targeted) position when the fast SMA is below
    the slow SMA. Both SMAs use only past prices, so no look-ahead is introduced.
    """
    fast_ma = price.rolling(fast).mean().shift(1)   # shift: known at prior close
    slow_ma = price.rolling(slow).mean().shift(1)
    gate = (fast_ma > slow_ma).astype(float)        # 1.0 in uptrend, 0.0 otherwise
    return position * gate.reindex(position.index).fillna(0.0)

La compuerta de tendencia reduce el turnover en el lado bajista (dejas de rotar una posición menguante en un mercado bajista) pero agrega su propio riesgo de régimen: se sacude en mercados laterales agitados y se retrasa en los giros. Si ayuda es una pregunta empírica que debes responder con el mismo rigor walk-forward y probado con DM que la propia regla de sizing; un filtro de tendencia es exactamente el tipo de añadido que se ve genial dentro de muestra y se evapora fuera de muestra.

Targeting de volatilidad de portafolio sobre la covarianza DCC. Para un libro de varios activos, el pronóstico escalar σ^t\hat\sigma_t se convierte en la volatilidad del portafolio wΣtw\sqrt{w^\top \Sigma_t w}, donde Σt\Sigma_t es la matriz de covarianza variable en el tiempo del DCC-GARCH de la Parte 3. Eliges pesos base w0w_0 (peso igual, capitalización de mercado, o una inclinación de media-varianza), computas la vol pronosticada del portafolio bajo Σt\Sigma_t, y escalas todo el vector de pesos para alcanzar el objetivo del portafolio.

def portfolio_voltarget_weights(base_w, cov_forecast, target_vol_daily, w_gross_max=3.0):
    """Scale a base weight vector so forecast portfolio vol hits the target.
    base_w:        (n,) base allocation (need not sum to 1).
    cov_forecast:  (n, n) one-step-ahead covariance from DCC-GARCH (daily units).
    """
    base_w = np.asarray(base_w, dtype=float)
    port_var = float(base_w @ cov_forecast @ base_w)
    port_vol = np.sqrt(max(port_var, 1e-12))
    scale = min(target_vol_daily / port_vol, w_gross_max / np.abs(base_w).sum())
    return scale * base_w

Este es el puente natural hacia la literatura de construcción de portafolios: los pesos base w0w_0 pueden venir de Markowitz de media-varianza o un método basado en riesgo como HRP/CVaR, y el targeting de vol se sienta entonces encima como una capa que escala el riesgo agregado a una constante. La matriz DCC importa porque las correlaciones se disparan en los crashes (Parte 3): un portafolio que se ve diversificado en mercados tranquilos puede tener una vol pronosticada mucho mayor de lo que implica una covarianza estática exactamente cuando importa, y la capa recorta la exposición bruta en respuesta.

Diagnósticos que siempre deberías graficar

Nunca confíes solo en la tabla resumen. Para cualquier vol-target, grafica tres cosas y examínalas visualmente antes de creer cualquier número de Sharpe. Primero, la vol rodante realizada de la estrategia contra la línea objetivo: debería abrazar el objetivo; una deriva sistemática por encima significa que tu pronóstico está sesgado a la baja (la dirección costosa). Segundo, la serie de posición/apalancamiento: busca el tope activándose y picos de apalancamiento justo antes de los drawdowns, la firma de un pronóstico que fue sorprendido por un cambio de régimen. Tercero, el diagrama de dispersión pronóstico-vs-proxy (la imagen de Mincer-Zarnowitz): una nube con pendiente lejos de 1 te dice que el pronóstico está mal escalado de una forma que el promedio de QLIKE puede ocultar. Estos tres gráficos atrapan más errores y más autoengaños que cualquier estadístico individual.

Lee esta tabla de la forma en que deberías leer toda tabla de backtest: mira qué es robusto y qué es marginal. Los hechos robustos saltan a la vista. Cada variante con targeting de vol aplasta al nocional fijo en Sharpe y, más dramáticamente, en drawdown y estabilidad de vol realizada: la estrategia de nocional fijo corre al 68% de vol anualizada con un drawdown del 78%, que simplemente es no invertible. Y cada método de targeting entrega vol realizada cercana al objetivo del 20%, que es toda la promesa de que el mecanismo funciona. Los hechos marginales son las diferencias entre los pronosticadores: HAR supera a GARCH que supera a EWMA que supera a RV rodante, pero las brechas son pequeñas, una décima de punto de Sharpe, y, probadas con Diebold-Mariano sobre los pronósticos o un bootstrap sobre los retornos, frecuentemente fallarían en superar la significancia. Esa brecha pequeña, frágil y dependiente del régimen entre el pronosticador sofisticado y el ingenuo es el titular honesto de toda esta serie.

Siendo honestos sobre lo que esto compra

Este blog tiene toda una colección sobre backtesting sin engañarse a uno mismo, así que apliquémosla a nuestro propio resultado en lugar de esperar silenciosamente que no lo hagas.

El targeting de volatilidad mejora el retorno ajustado por riesgo y los drawdowns. No fabrica alfa de la nada. Mira de nuevo la tabla. La mejora de Sharpe por el targeting es real y vale la pena tenerla, pero descompónla y la mayor parte proviene de no mantener una posición constante en regímenes de alta vol, lo que mecánicamente evita los peores drawdowns y estabiliza la trayectoria de capitalización. La estrategia sigue siendo long BTC; no tiene ninguna visión que el mercado no le entregue. Si BTC tiene un Sharpe negativo en tu muestra, el targeting de vol te entregará un Sharpe negativo menos malo, no uno positivo. Reconfigura la distribución de retornos (colas más delgadas, vol más estable, mejor capitalización geométrica), pero la ventaja direccional cruda es la que sea que tenga el long subyacente. No dejes que una hermosa curva de capital te engañe haciéndote creer que encontraste alfa cuando encontraste gestión de riesgo. Moreira-Muir encontraron alfa genuino en factores de acciones a partir de la gestión de vol, pero ese resultado trata sobre el intercambio riesgo-retorno variable en el tiempo del factor, y no se transfiere automáticamente a un solo activo cripto en una muestra diferente.

La ventaja en calidad de pronóstico de GARCH sobre EWMA es a menudo pequeña y dependiente del régimen. Esta es la recompensa incómoda de las Partes 1-3. Construiste modelos cada vez más sofisticados (términos de apalancamiento, colas Student-t, correlaciones dinámicas) y la contribución marginal de cada uno a un P&L de targeting de vol, sobre un EWMA ingenuo, frecuentemente está dentro de la banda de ruido. La ventaja de GARCH (reversión a la media después de shocks) aparece principalmente en regímenes específicos: picos agudos que luego se normalizan. En tendencias sostenidas o regímenes persistentes de alta vol apenas difiere de EWMA. Esto no hace inútil a GARCH: la estructura de reversión a la media, los parámetros interpretables, la capacidad de simular trayectorias hacia adelante y valorar opciones contra el pronóstico, todo eso tiene un valor que EWMA no tiene, pero si tu único uso es el sizing, corre la prueba DM antes de pagar el costo de complejidad, y ten en cuenta que la detección de regímenes te está diciendo lo mismo desde otro ángulo: el modelo ganador depende del régimen.

El Sharpe de backtest es una cota superior del Sharpe en vivo, y el targeting de vol amplía esa brecha. Como la estrategia es intensiva en turnover y escalada por apalancamiento, es inusualmente sensible a las fricciones que un backtest ingenuo omite: tus fills son peores que el cierre sobre el que dimensionaste, los costos de financiamiento en posiciones perpetuas apalancadas se acumulan continuamente, y el tope de apalancamiento interactúa con la mecánica de margen y liquidación que un simple w * return ignora. Cada uno de estos hace que lo real sea peor que el backtest. Tratamos esta brecha sistemáticamente en paridad backtest-en vivo; para el targeting de vol específicamente, presupuéstala usando costos conservadores, un tope de apalancamiento realista (bajo), y ejecutando en la apertura de la siguiente barra en lugar del cierre sobre el que calculaste la señal.

Un apunte sobre la prima de riesgo de volatilidad. Todo lo anterior pronostica la volatilidad realizada. Hay un objeto paralelo y negociable: la volatilidad implícita, valorada en las opciones, que en promedio se sitúa por encima de la volatilidad realizada subsecuente: la prima de riesgo de volatilidad, compensación por asumir el riesgo de un pico de vol. Esa brecha es en sí misma una fuente de retorno (vender varianza la cosecha, comprarla cubre el riesgo de cola), y es un juego genuinamente distinto del targeting de vol: es una apuesta sobre el precio de la volatilidad en lugar de un uso de un pronóstico de volatilidad. No lo perseguimos aquí, pero la maquinaria comienza con el modelo de valoración en pricing de opciones Black-Scholes, y un buen pronóstico de vol realizada (Partes 1-2) es exactamente el insumo que necesitas para juzgar si la vol implícita está cara o barata. Comparar tu pronóstico GARCH con la vol implícita del mercado de opciones es uno de los usos más honestos de todo lo que construiste en esta serie.

Consideraciones prácticas

Un cajón de sastre de cosas que separan un vol-target funcional de uno frágil.

  • Estima en el horizonte correcto. Si dimensionas una posición mantenida por un día, pronostica la vol de un día. Si rebalanceas semanalmente, pronostica (y apunta) la vol semanal, o agrega el pronóstico diario de GARCH sobre el horizonte, el pronóstico GARCH multi-paso revierte a la media hacia σˉ2\bar\sigma^2, lo que el escalado ingenuo "hdiario\sqrt{h}\cdot\text{diario}" ignora. La Parte 1 cubre el pronóstico GARCH multi-paso.
  • Anualización en mercados 24/7. Cripto opera 365 días al año sin fines de semana ni feriados, así que anualiza la vol diaria con 365\sqrt{365}, no con el 252\sqrt{252} de las acciones. Equivocarse en esto desescala silenciosamente tu objetivo en aproximadamente 20%.
  • El denominador puede ser una matriz de covarianza. Para un libro multi-activo, reemplaza el escalar σ^t\hat\sigma_t con la vol de portafolio wΣtw\sqrt{w^\top \Sigma_t w} del DCC-GARCH de la Parte 3, y escala todo el vector de pesos para alcanzar la vol objetivo del portafolio. Esto conecta el targeting de vol con el sizing de media-varianza (Markowitz para cripto) y la asignación basada en riesgo (pipelines de HRP y CVaR): el targeting de vol es el caso especial de un solo activo de escalar a un presupuesto de riesgo de portafolio.
  • El targeting de vol es procíclico de una manera sutil. Cuando todos corren la misma regla 1/σ1/\sigma, un pico de vol fuerza un desapalancamiento sincronizado, que empuja los precios hacia abajo, que eleva la vol realizada, que fuerza más desapalancamiento. Esta retroalimentación (bien documentada en el "volmageddon" de 2018 y varias cascadas de desapalancamiento cripto) significa que la regla funciona peor exactamente cuando muchos jugadores la usan. No es una razón para abandonarla, pero sí es una razón para topar el apalancamiento y para no asumir que tus fills durante un pico de vol se parecerán a los fills de mercado tranquilo.
  • Pon piso y recorta el pronóstico. Un pronóstico de vol cero o cercano a cero produce apalancamiento infinito. Siempre pon un piso a σ^t\hat\sigma_t en un mínimo sensato y topa la posición, y registra con qué frecuencia se activa cada uno; si el tope se activa la mayor parte del tiempo, tu objetivo es demasiado agresivo para el activo.

Resumen

  • Un pronóstico de volatilidad no tiene valor hasta que cambia una decisión. El targeting de volatilidad, dimensionar la exposición como wt=σtarget/σ^tw_t = \sigma_{\text{target}}/\hat\sigma_t (topado), es la prueba más limpia del valor de un pronóstico, porque la calidad del pronóstico se traduce directamente en un perfil de vol realizada más plano y un Sharpe mayor.
  • El targeting de vol eleva el retorno ajustado por riesgo y, sobre todo, controla los drawdowns, porque la volatilidad es pronosticable (se agrupa) mientras que la dirección no lo es, y porque los ratios de Sharpe caen en regímenes de alta vol que la regla subpondera automáticamente. Es sizing Kelly bajo el supuesto de que el retorno esperado escala con la volatilidad.
  • Compara a GARCH honestamente contra referencias fuertes: volatilidad realizada rodante, EWMA/RiskMetrics (λ=0.94\lambda=0.94, un IGARCH con cero parámetros libres), y HAR-RV sobre un proxy de varianza realizada intradía. EWMA y HAR son difíciles de vencer.
  • No puedes observar la volatilidad verdadera, así que evalúa contra un proxy (r2r^2 o, mucho mejor, RVRV) usando funciones de pérdida robustas al ruido del proxy. Prefiere QLIKE sobre MSE: penaliza más el sub-pronóstico (el error costoso) y es invariante a la escala, así que no es secuestrado por unos pocos días de alta vol. Usa Mincer-Zarnowitz para diagnosticar el sesgo y la prueba de Diebold-Mariano para decidir si la ventaja de un pronóstico es real o ruido.
  • En un backtest walk-forward consciente de costos, el targeting de vol vence de forma fiable al nocional fijo en Sharpe y drawdown, y los cuatro pronosticadores se agrupan muy cerca entre sí: la ventaja de GARCH sobre EWMA es pequeña, dependiente del régimen, y a menudo no estadísticamente significativa. Reporta la prueba DM, no solo la pérdida promedio.
  • Sé honesto: el targeting de vol es gestión de riesgo, no alfa. Reconfigura la distribución de retornos de cualquier apuesta direccional que ya tenías; no crea ventaja de la nada. Y es intensivo en turnover y escalado por apalancamiento, así que los resultados en vivo se rezagan del backtest más de lo usual.

Referencias:

  • Patton, A. J. (2011). Volatility forecast comparison using imperfect volatility proxies. Journal of Econometrics, 160(1), 246-256. DOI
  • Corsi, F. (2009). A simple approximate long-memory model of realized volatility. Journal of Financial Econometrics, 7(2), 174-196. DOI
  • Diebold, F. X., & Mariano, R. S. (1995). Comparing predictive accuracy. Journal of Business & Economic Statistics, 13(3), 253-263. DOI
  • Mincer, J., & Zarnowitz, V. (1969). The evaluation of economic forecasts. In Economic Forecasts and Expectations, NBER.
  • Moreira, A., & Muir, T. (2017). Volatility-managed portfolios. Journal of Finance, 72(4), 1611-1644. DOI
  • J.P. Morgan / Reuters (1996). RiskMetrics Technical Document, 4th ed.
  • Bollerslev, T. (1986). Generalized autoregressive conditional heteroskedasticity. Journal of Econometrics, 31(3), 307-327. 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
  • Harvey, D., Leybourne, S., & Newbold, P. (1997). Testing the equality of prediction mean squared errors. International Journal of Forecasting, 13(2), 281-291. DOI
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.