Lección 3.4.6 · 70 min

Volatilidad realizada frente a implícita; prima de riesgo de volatilidad; VIX y VSTOXX

La volatilidad implícita es un precio y la realizada, un resultado. Entre las dos hay una prima que los vendedores de opciones cobran casi siempre y devuelven de golpe. Esta lección construye desde la cadena de opciones un índice tipo VIX con la fórmula de Cboe, comprueba que coincide con el publicado, mide la prima con datos sintéticos de respuesta conocida y compara la metodología con la del VSTOXX.

Fig. — VORX (índice tipo VIX de los datos) frente a la volatilidad realizada de ORX en las 21 sesiones siguientes, una observación cada diez sesiones, 2014-2025
Lecciones del nivel 3
  1. 3.1 · Futuros en profundidad

    1. 3.1.1 Contratos de futuros principales
    2. 3.1.2 Curva de futuros: base, cost of carry, contango y roll yield
    3. 3.1.3 Gestión del roll, márgenes, SPAN y apalancamiento implícito
    4. 3.1.4 Sistemas de futuros diversificados: el enfoque de Carver
  2. 3.2 · Spreads de futuros

    1. 3.2.1 Spreads de calendario, butterflies y condors
    2. 3.2.2 Spreads intercommodity: crack, spark y crush
    3. 3.2.3 Spreads de tipos y de índices: 2s10s, NOB, NQ/ES y Europa frente a EE. UU.
    4. 3.2.4 Ratios de cobertura: por nominal, por DV01 y por volatilidad
    5. 3.2.5 Datos y backtesting de spreads: cotizados, sintéticos y por patas
    6. 3.2.6 Estacionalidad de spreads: análisis multi-año, patrones y trampas
  3. 3.3 · Forex avanzado

    1. 3.3.1 Microestructura FX: OTC, ECN, last look y sesiones
    2. 3.3.2 Carry y riesgo de crash, momentum y value en divisas; cestas
    3. 3.3.3 Riesgo de divisa en carteras multi-moneda; ejecución vía OANDA, IBKR o MT5
  4. 3.4 · Teoría y valoración de opciones

    1. 3.4.1 Payoffs, paridad put-call y límites de arbitraje
    2. 3.4.2 Árboles binomiales, Black-Scholes-Merton, Black-76 y Monte Carlo
    3. 3.4.3 Griegas: delta, gamma, vega, theta, rho, vanna, volga y charm
    4. 3.4.4 Volatilidad implícita: Newton-Raphson, Brent, py_vollib y QuantLib
    5. 3.4.5 Sonrisa, skew y estructura temporal; superficie de volatilidad con SVI
    6. 3.4.6 Volatilidad realizada frente a implícita; prima de riesgo de volatilidad; VIX y VSTOXX
    7. 3.4.7 Modelos avanzados: Heston, SABR y volatilidad local
  5. 3.5 · Estructuras de opciones en Python

    1. 3.5.1 Estructuras direccionales y verticales: call, put, covered call, collar y spreads
    2. 3.5.2 Estructuras de volatilidad y rango: straddles, strangles, cóndores y mariposas
    3. 3.5.3 Calendarios, diagonales, ratios, backspreads, risk reversals y jade lizard
    4. 3.5.4 Herramienta propia: payoff, griegas agregadas y escenarios
    5. 3.5.5 Gestión: ajustes, roll, cierre anticipado, asignación temprana y pin risk
  6. 3.6 · Estrategias sistemáticas de volatilidad y opciones

    1. 3.6.1 Venta sistemática de prima: put-write, strangles y su riesgo de cola
    2. 3.6.2 Delta hedging, gamma scalping y dispersión
    3. 3.6.3 Estrategias con VIX y VSTOXX: futuros y ETP de volatilidad
    4. 3.6.4 Backtesting de opciones: datos, fills y ajustes por eventos
  7. 3.7 · Cripto derivados y estrategias específicas

    1. 3.7.1 Perpetuos: mecánica del funding, mark price y liquidaciones
    2. 3.7.2 Arbitraje de funding, basis con futuros y arbitraje entre exchanges
    3. 3.7.3 Opciones cripto, datos on-chain y DeFi
    4. 3.7.4 ccxt para operar en varios exchanges
    5. 3.7.5 MiCA: el marco de la UE para criptoactivos y sus proveedores (CASP)

Dos volatilidades distintas

La palabra «volatilidad» designa dos cosas que conviene no confundir:

  • La volatilidad realizada es una medida estadística del pasado: la desviación típica anualizada de los rendimientos de un periodo ya ocurrido. Con rendimientos logarítmicos diarios rtr_t y nn sesiones, σreal=(252/n)∑rt2\sigma_{\text{real}} = \sqrt{(252/n)\sum r_t^2} (sin restar la media, como en los contratos de varianza). Hay estimadores mejores con máximos y mínimos (el research «Estimadores de volatilidad» del sitio los compara), pero el de cierre a cierre es el que liquidan los derivados.
  • La volatilidad implícita es un precio: lo que el mercado de opciones cobra hoy por la volatilidad de un periodo futuro (lecciones 3.4.4 y 3.4.5). No es una previsión neutral, porque incluye lo que los compradores pagan por protegerse.

La diferencia entre la implícita de hoy y la realizada que llega después es la prima de riesgo de volatilidad. Si es positiva de media, vender volatilidad gana dinero de media; la pregunta es a cambio de qué riesgo. Para medirla hace falta una implícita a plazo constante, y el estándar del mercado es el VIX de Cboe: la volatilidad implícita a 30 días del S&P 500 calculada con toda la cadena de opciones, no con una sola.

Opciones OTMVarianza a T1 y T2Interpolar a 30 dVIX = 100 raízCompararRealizada 30 díasPrima de riesgo
Fuente Mermaid
flowchart TB
  O[Opciones OTM] --> V[Varianza a T1 y T2]
  V --> I[Interpolar a 30 d]
  I --> X[VIX = 100 raíz]
  X --> P{Comparar}
  R[Realizada 30 días] --> P
  P --> VRP[Prima de riesgo]

De la cadena al índice: la fórmula de Cboe

El VIX nació en 1993 sobre opciones ATM del S&P 100. En 2003 Cboe, con Goldman Sachs, lo rehízo sobre las opciones del S&P 500 con la fórmula actual, que no depende de ningún modelo de valoración1Nota 1 · Historia y reglas actuales: Cboe, Volatility Index Methodology: Cboe Volatility Index (versión 6.0) y Cboe Volatility Index Mathematics Methodology; versión mensual con al menos una semana de vida: libro blanco de 2009. Enlaces en los recursos de la lección.. La idea es de la teoría de los swaps de varianza (Demeterfi, Derman, Kamal y Zou, 1999; Carr y Madan, 1998): una cartera de opciones fuera del dinero con pesos inversamente proporcionales al cuadrado del strike replica la varianza realizada, sea cual sea el nivel del índice. Su precio, por tanto, es la varianza esperada bajo la medida de valoración. Para un vencimiento TT:

σ2=2T∑iΔKiKi2 eRT Q(Ki)−1T(FK0−1)2.\sigma^2 = \frac{2}{T}\sum_i \frac{\Delta K_i}{K_i^2}\,e^{RT}\,Q(K_i) - \frac{1}{T}\left(\frac{F}{K_0} - 1\right)^2 .

Cada término tiene su regla en la metodología de Cboe:

ElementoRegla
TTTiempo hasta el vencimiento en años de 365 días (en el VIX real, en minutos)
RRTipo libre de riesgo al plazo TT (en el VIX real, curva del Tesoro con spline cúbico)
FFForward por la paridad en el strike donde ∣C−P∣\lvert C - P \rvert es mínimo: F=K+eRT(C−P)F = K + e^{RT}(C - P), con medios
K0K_0Primer strike igual o inferior a FF
OpcionesPuts por debajo de K0K_0 y calls por encima, desde K0K_0 hacia fuera; se excluyen las de bid nulo y se para tras dos bids nulos seguidos; en K0K_0, la media de put y call
ΔKi\Delta K_iMedia de la distancia a los strikes vecinos; en los extremos, la distancia al único vecino
Q(Ki)Q(K_i)Punto medio del bid y el ask

Después se interpolan los dos vencimientos a 30 días en varianza total y se pasa a volatilidad:

VIX=100[T1σ12 N2−N30N2−N1+T2σ22 N30−N1N2−N1]N365N30.\text{VIX} = 100\sqrt{\left[T_1\sigma_1^2\,\frac{N_2 - N_{30}}{N_2 - N_1} + T_2\sigma_2^2\,\frac{N_{30} - N_1}{N_2 - N_1}\right] \frac{N_{365}}{N_{30}}} .

El VIX real usa hoy opciones con más de 23 y menos de 37 días, mensuales y semanales, y cuenta el tiempo en minutos. El libro blanco de 2009, con solo vencimientos mensuales, tomaba los dos primeros vencimientos con al menos una semana de vida. Esa versión mensual es la que usa el VORX, el índice tipo VIX de los datos del curso sobre el índice ficticio ORX (vol3_indices.csv): los dos primeros vencimientos con al menos 8 días, plazo en días naturales y tipo interpolado linealmente en la curva de opciones3_indice.csv. Su respuesta conocida es que se recalcula exactamente desde los ficheros publicados.

El VORX de un día, paso a paso

IN [01]python
import math
import numpy as np
import pandas as pd

cad = pd.read_parquet("opciones3_cadena.parquet")
ind = pd.read_csv("opciones3_indice.csv", parse_dates=["fecha"]) \
    .set_index("fecha")
vx = pd.read_csv("vol3_indices.csv", parse_dates=["fecha"]) \
    .set_index("fecha")

ancha = cad.pivot_table(index=["fecha", "vencimiento", "strike"],
                        columns="tipo", values=["bid", "ask"])
ancha.columns = [f"{a}_{t}" for a, t in ancha.columns]
ancha = ancha.dropna().reset_index()   # strikes con call y put

PLAZOS = np.array([1 / 12, 0.25, 0.5, 1.0, 2.0])
CURVA = ["tipo_1m", "tipo_3m", "tipo_6m", "tipo_1a", "tipo_2a"]


def tipo(fecha, T):
    """Tipo continuo a plazo T: interpolación lineal de la curva."""
    return float(np.interp(T, PLAZOS, ind.loc[fecha, CURVA])) / 100


hoy = pd.Timestamp("2025-06-04")
dia = ancha[ancha["fecha"] == hoy]
print({f"{(v - hoy).days} días": len(g)
       for v, g in dia.groupby("vencimiento")})
OUT [01]Cadena en formato ancho y curva de tipos
{'16 días': 48, '44 días': 45, '72 días': 40}

pivot_table deja una fila por strike con el bid y el ask de la call y de la put. Ese día hay tres vencimientos en la cadena diaria, a 16, 44 y 72 días, con 48, 45 y 40 strikes que tienen las dos patas.

IN [02]python
def hasta_dos_ceros(bid):
    """Desde K0 hacia fuera: fuera los bids nulos; se para al
    encontrar dos seguidos."""
    nulo = bid <= 0
    pares = np.flatnonzero(nulo[:-1] & nulo[1:])
    fin = pares[0] if len(pares) else len(bid)
    usar = ~nulo
    usar[fin:] = False
    return usar


def var_cboe(g, T, R, detalle=False):
    K = g["strike"].to_numpy()
    c = ((g["bid_C"] + g["ask_C"]) / 2).to_numpy()
    p = ((g["bid_P"] + g["ask_P"]) / 2).to_numpy()
    j = np.argmin(np.abs(c - p))
    F = K[j] + math.exp(R * T) * (c[j] - p[j])
    i0 = np.flatnonzero(K <= F)[-1]
    ip = np.arange(i0 - 1, -1, -1)       # puts, hacia abajo
    ic = np.arange(i0 + 1, len(K))       # calls, hacia arriba
    ip = ip[hasta_dos_ceros(g["bid_P"].to_numpy()[ip])][::-1]
    ic = ic[hasta_dos_ceros(g["bid_C"].to_numpy()[ic])]
    idx = np.r_[ip, i0, ic]
    Q = np.r_[p[ip], (c[i0] + p[i0]) / 2, c[ic]]
    Ks = K[idx]
    dK = np.gradient(Ks)   # centrado; en los bordes, el vecino
    aporte = 2 / T * dK / Ks ** 2 * Q * math.exp(R * T)
    s2 = aporte.sum() - (F / K[i0] - 1) ** 2 / T
    if detalle:
        return s2, F, K[i0], pd.DataFrame(
            {"strike": Ks, "tipo": ["P"] * len(ip) + ["PC"]
             + ["C"] * len(ic), "Q": Q, "aporte": aporte})
    return s2


for v, g in dia.groupby("vencimiento"):
    n = (v - hoy).days
    T = n / 365
    s2, F, K0, tabla = var_cboe(g, T, tipo(hoy, T), detalle=True)
    puts = tabla.loc[tabla["tipo"] == "P", "aporte"].sum()
    print(f"{n} días: F = {F:.2f}, K0 = {K0:.0f}, "
          f"{len(tabla)} strikes, σ² = {s2:.6f}, "
          f"vol = {100 * math.sqrt(s2):.2f}, "
          f"puts {puts / tabla['aporte'].sum():.0%}")
OUT [02]Varianza de un vencimiento con la regla de Cboe
16 días: F = 5143.89, K0 = 5100, 32 strikes, σ² = 0.065072, vol = 25.51, puts 56%
44 días: F = 5155.03, K0 = 5150, 40 strikes, σ² = 0.067723, vol = 26.02, puts 69%
72 días: F = 5166.23, K0 = 5150, 39 strikes, σ² = 0.069099, vol = 26.29, puts 69%

Para cada vencimiento, el forward sale de la paridad en el strike con ∣C−P∣|C - P| mínimo, K0K_0 es el strike justo por debajo y la selección deja 32, 40 y 39 strikes: en el de 16 días se quedan fuera las alas con bids nulos. Las puts aportan el 56 %, el 69 % y el 69 % de la varianza; con skew, la parte baja de la sonrisa pesa más (y los pesos 1/K21/K^2 la favorecen todavía más). La volatilidad de cada vencimiento, 25,5 %, 26,0 % y 26,3 %, es la de un swap de varianza a ese plazo, no la ATM: la ATM de 44 días de ese día era del 22,2 % (lección 3.4.5).

IN [03]python
def vorx(fecha, grupos):
    """Índice a 30 días con los dos primeros vencimientos de 8 días
    o más (libro blanco de Cboe, versión mensual)."""
    vencs = sorted(v for v in grupos if (v - fecha).days >= 8)[:2]
    n1, n2 = [(v - fecha).days for v in vencs]
    s2 = [var_cboe(grupos[v], n / 365, tipo(fecha, n / 365))
          for v, n in zip(vencs, (n1, n2))]
    w = (n1 / 365 * s2[0] * (n2 - 30) / (n2 - n1)
         + n2 / 365 * s2[1] * (30 - n1) / (n2 - n1))
    return 100 * math.sqrt(max(w * 365 / 30, 0.0))


grupos_hoy = dict(tuple(dia.groupby("vencimiento")))
print(f"VORX calculado: {vorx(hoy, grupos_hoy):.2f}")
print(f"VORX publicado: {vx.loc[hoy, 'vorx']:.2f}")
OUT [03]Interpolación a 30 días
VORX calculado: 25.89
VORX publicado: 25.89

Con N1=16N_1 = 16 y N2=44N_2 = 44, los 30 días quedan a mitad de camino y los dos vencimientos pesan lo mismo en varianza total. El resultado, 25,89, coincide con el publicado.

Fig. — Aporte de cada strike a la varianza del vencimiento de 44 días del VORX, 4 de junio de 2025, en % del total: puts por debajo de K0 y calls por encima

La figura descompone la varianza del vencimiento de 44 días por strike. Las puts se extienden hasta 2.400 puntos, un 53 % por debajo del índice, con aportes pequeños pero no despreciables; las calls se acaban mucho antes, porque las lejanas tienen bid nulo. La barra de K0K_0 es la más alta porque ahí se suman la put y la call (su media) con el peso de un strike cercano al dinero.

Doce años recalculados

IN [04]python
grupos = {}
for (f, v), g in ancha.groupby(["fecha", "vencimiento"]):
    grupos.setdefault(f, {})[v] = g
calc = pd.Series({f: vorx(f, gv) for f, gv in grupos.items()})
dif = (calc.round(2) - vx["vorx"].reindex(calc.index)).abs()
print(f"{len(calc)} días; diferencia máxima: {dif.max():.3f}")
sin = vx.index.difference(calc.index)
print("Días del índice sin cadena:", [d.date() for d in sin])
OUT [04]El VORX de todos los días desde la cadena
3017 días; diferencia máxima: 0.000
Días del índice sin cadena: [datetime.date(2019, 7, 23)]

Los 3.017 días con cadena dan el VORX publicado al céntimo: es la respuesta conocida del conjunto de datos, y la prueba de que el código aplica bien cada regla. El único día del índice que falta, el 2019-07-23, es la incidencia dia_sin_cadena de la lección 3.6.4: ese día el proveedor no entregó la cadena y el índice no se puede recalcular.

IN [05]python
ver = pd.read_parquet("opciones3_verdad.parquet")


def atm30(g):
    """Vol ATM verdadera a 30 días: θ interpolado, θ(0) = 0."""
    th = np.interp(30 / 365, np.r_[0, g["dias"] / 365],
                   np.r_[0, g["theta"]])
    return 100 * math.sqrt(th * 365 / 30)


vx["atm30"] = ver.groupby("fecha").apply(atm30)
d_mod = vx["vorx"] - vx["vorx_modelo"]
d_atm = vx["vorx"] - vx["atm30"]
print(f"VORX − swap del modelo: media {d_mod.mean():.2f}, "
      f"mín. {d_mod.min():.2f}, máx. {d_mod.max():.2f}")
print(f"VORX − ATM a 30 días: media {d_atm.mean():.2f}, "
      f"mediana {d_atm.median():.2f}")
vx[["vorx", "vorx_modelo", "atm30"]].describe().round(2) \
    .loc[["mean", "50%", "min", "max"]]
OUT [05]VORX frente al modelo y frente a la volatilidad ATM
VORX − swap del modelo: media 0.17, mín. -2.36, máx. 1.39
VORX − ATM a 30 días: media 2.86, mediana 2.58
       vorx  vorx_modelo  atm30
mean  19.80        19.63  16.94
50%   17.66        17.37  15.13
min    9.55         9.72   8.50
max   73.98        74.16  63.02

El VORX queda de media 0,17 puntos por encima del swap de varianza de la superficie verdadera (vorx_modelo), con desviaciones de −2,36 a 1,39: es el error de discretizar la integral en strikes, truncar las alas e interpolar entre dos vencimientos. Frente a la volatilidad ATM a 30 días, en cambio, está 2,86 puntos por encima de media (mediana 2,58): la diferencia no es un error, es el skew. Un índice tipo VIX no mide la volatilidad ATM, sino la raíz de una media de las varianzas de toda la sonrisa, y con skew esa media supera a la del dinero.

Volatilidad realizada y prima de riesgo

La comparación justa es la implícita de hoy frente a la realizada después: el VORX del día tt mira los 30 días naturales siguientes, unas 21 sesiones. Usar la realizada de las 21 sesiones anteriores sería comparar el precio de hoy con un dato que ya se conocía.

IN [06]python
r = np.log(ind["cierre"]).diff()
real_post = 100 * np.sqrt(252 * (r ** 2).rolling(21).mean()) \
    .shift(-21)                       # sesiones t+1 … t+21
real_prev = 100 * np.sqrt(252 * (r ** 2).rolling(21).mean())
prima = (vx["vorx"] - real_post).dropna()
print(f"Prima VORX − realizada: media {prima.mean():.2f}, "
      f"mediana {prima.median():.2f}")
print(f"Días con la realizada por encima: {(prima < 0).mean():.1%}")
print(f"Peor día: {prima.idxmin().date()}, {prima.min():.1f} puntos")
p_atm = (vx["atm30"] - real_post).dropna()
print(f"Con la ATM a 30 días: media {p_atm.mean():.2f}, "
      f"mediana {p_atm.median():.2f}")
var_prima = (vx["vorx"] ** 2 - real_post ** 2).dropna() / 100
print(f"En varianza (puntos² / 100): media {var_prima.mean():.2f}")
OUT [06]Realizada posterior y prima
Prima VORX − realizada: media 5.30, mediana 6.38
Días con la realizada por encima: 11.1%
Peor día: 2020-03-06, -76.5 puntos
Con la ATM a 30 días: media 2.43, mediana 3.88
En varianza (puntos² / 100): media 1.53

La prima media es de 5,30 puntos de volatilidad y la mediana, de 6,38. La realizada supera al VORX el 11,1 % de los días, y el peor caso es el 6 de marzo de 2020: el VORX marcaba 23,2 y la realizada de las 21 sesiones siguientes, que incluyen las caídas de doble dígito del 12 y el 16 de marzo, fue del 99,7 %, una diferencia de −76,5 puntos. Con la volatilidad ATM a 30 días, la prima es menor (2,43 de media) porque parte de lo que el VORX cobra es el skew. En varianza, que es lo que se negocia en un swap, la diferencia media es de 153 puntos de volatilidad al cuadrado (0,0153 de varianza anual).

En los datos la prima está plantada: la varianza implícita es entre 1,0 y 1,3 veces la esperada (más en calma que en estrés), según el README de los datos. La referencia académica sobre la prima de varianza en los mercados reales es Carr y Wu (2009), en los recursos; aquí no se da ninguna cifra real.

IN [07]python
anual = prima.groupby(prima.index.year).agg(
    media="mean", mediana="median", minimo="min",
    negativa=lambda z: (z < 0).mean())
anual.round(2).T
OUT [07]La prima año a año
fecha     2014   2015   2016  2017   2018  2019   2020  2021  2022  2023  2024   2025
media     3.91   4.38   4.19  4.26   6.04  6.90   5.08  7.18  5.67  6.35  5.41   4.18
mediana   4.23   5.68   5.50  5.02   8.13  7.07  11.92  8.62  6.37  7.15  5.63   6.33
minimo   -4.19 -15.67 -16.19 -8.09 -16.95 -1.40 -76.46 -7.03 -9.44 -1.15 -2.88 -24.94
negativa  0.11   0.22   0.15  0.17   0.16  0.02   0.10  0.09  0.08  0.04  0.06   0.14

La mediana es positiva todos los años, de 4,2 en 2014 a 11,9 en 2020; la fracción de días negativos va del 2 % (2019) al 22 % (2015). Los mínimos cuentan otra historia: −76,5 en 2020, −24,9 en 2025, −17,0 en 2018 y −16,2 en 2016, los años con tramos de estrés en el guion de los datos. La prima es estable en el centro y muy inestable en la cola.

Fig. — Distribución diaria de la prima de riesgo de volatilidad de los datos, VORX menos la realizada de las 21 sesiones siguientes, en puntos de volatilidad, 2014-2025 (número de días en escala log)

La figura muestra toda la distribución, con el número de días en escala logarítmica para que se vea la cola: la masa entre 0 y 15 puntos y, a la izquierda de cero (destacado), una cola larga con un grupo aislado entre −76 y −62 que son las 16 sesiones del 13 de febrero al 6 de marzo de 2020.

IN [08]python
import statsmodels.api as sm

d = pd.concat({"real_post": real_post, "vorx": vx["vorx"],
               "real_prev": real_prev}, axis=1).dropna()
d = d.iloc[::21]          # sin solapamiento: una cada 21 sesiones
for x_cols in (["vorx"], ["real_prev"], ["vorx", "real_prev"]):
    m = sm.OLS(d["real_post"], sm.add_constant(d[x_cols])).fit()
    coef = ", ".join(f"{c} {m.params[c]:.2f} (t {m.tvalues[c]:.1f})"
                     for c in x_cols)
    print(f"{coef} · R² {m.rsquared:.2f} · n {int(m.nobs)}")
OUT [08]¿Predice el VORX la volatilidad?
vorx 0.70 (t 7.9) · R² 0.31 · n 142
real_prev 0.41 (t 5.3) · R² 0.17 · n 142
vorx 0.91 (t 5.5), real_prev -0.19 (t -1.5) · R² 0.32 · n 142

Con una observación cada 21 sesiones (142, sin solapamiento), el VORX explica el 31 % de la varianza de la realizada posterior, frente al 17 % de la realizada pasada; juntas, el 32 %, y la pasada pierde su significación (t = −1,5). El VORX contiene la información de la volatilidad pasada y algo más. Su pendiente de 0,70 (sola) dice que es un predictor sesgado al alza: lleva la prima dentro.

El VSTOXX y el índice europeo de los datos

El VSTOXX es el índice de volatilidad del EURO STOXX 50, desarrollado por Goldman Sachs y Deutsche Börse y calculado por STOXX con las opciones OESX de Eurex. Su varianza por vencimiento usa la misma fórmula que el VIX, pero la guía de STOXX2Nota 2 · STOXX, Strategy Index Guide, edición de septiembre de 2024, sección 9 (EURO STOXX 50 Volatility). Enlace en los recursos. cambia bastantes detalles:

AspectoVIX (Cboe)VSTOXX (STOXX)
OpcionesSPX mensuales y semanales, de 23 a 37 díasOESX de Eurex; 8 subíndices por vencimiento, de 1 a 24 meses
Índice principal30 días, interpolando dos vencimientos30 días (y otros 11 plazos hasta 360), interpolando dos subíndices
Precio de cada opciónPunto medio del bid y el askEl más reciente entre operación, medio y liquidación; el medio solo si bid y ask son de al menos 0,1 y la horquilla no supera el 8 % del bid (entre 1,2 y 18 puntos en mercado normal)
ExclusionesBids nulos; se para tras dos seguidosPrecios por debajo de 0,5 puntos; un subíndice necesita al menos 5 opciones
TiposCurva del Tesoro de EE. UU.€STR, EURIBOR y REX a 2 años, interpolados
TiempoMinutosSegundos; fotos cada 5 segundos de 9:15 a 17:30 CET

La consecuencia práctica es que los dos índices no son intercambiables aunque se muevan juntos: con horquillas anchas (estrés), el VSTOXX puede usar precios de liquidación o de la última operación donde el VIX usa medios, y el filtro de 0,5 puntos recorta sus alas antes. En los datos del curso, el índice europeo ficticio VGX tiene su índice de volatilidad VVGX, calculado como la raíz del swap de varianza a 30 días de su propio modelo (no hay cadena de VGX publicada, así que no se puede recalcular):

IN [09]python
rg = np.log(vx["vgx"]).diff()
real_post_g = 100 * np.sqrt(252 * (rg ** 2).rolling(21).mean()) \
    .shift(-21)
prima_g = (vx["vvgx"] - real_post_g).dropna()
print(f"VVGX: media {vx['vvgx'].mean():.1f}, "
      f"VORX: media {vx['vorx'].mean():.1f}")
print(f"Prima VVGX: media {prima_g.mean():.2f}, mediana "
      f"{prima_g.median():.2f}, negativa {(prima_g < 0).mean():.1%}")
print(f"Correlación de rendimientos ORX-VGX: {r.corr(rg):.2f}")
print("Correlación de cambios diarios VORX-VVGX: "
      f"{vx['vorx'].diff().corr(vx['vvgx'].diff()):.2f}")
print("Correlación de ORX con los cambios del VORX: "
      f"{r.corr(vx['vorx'].diff()):.2f}")
OUT [09]VVGX: el índice tipo VSTOXX de los datos
VVGX: media 21.3, VORX: media 19.8
Prima VVGX: media 5.41, mediana 6.16, negativa 11.0%
Correlación de rendimientos ORX-VGX: 0.82
Correlación de cambios diarios VORX-VVGX: 0.96
Correlación de ORX con los cambios del VORX: -0.61

El VVGX tiene una media de 21,3 frente a 19,8 del VORX, una prima parecida (5,41 de media, 6,16 de mediana, negativa el 11,0 % de los días) y unos cambios diarios casi idénticos a los del VORX (correlación 0,96), porque en el modelo de los datos su varianza es 1,2 veces la de ORX con un ruido propio pequeño. En los mercados reales la relación entre el VIX y el VSTOXX es estrecha, pero no tanto: cada uno tiene su propio mercado de opciones, sus horarios y sus reglas. La correlación negativa de −0,61 entre el rendimiento de ORX y el cambio del VORX es el efecto apalancamiento: la volatilidad implícita sube cuando el índice cae.

Ejercicios

  1. Calcula el VORX del miércoles 18 de junio de 2025, cuando el primer vencimiento está a dos días. ¿Qué vencimientos entran y por qué? Comprueba el resultado con el publicado.
Solución
IN [10]python
f = pd.Timestamp("2025-06-18")
print({(v - f).days: len(g) for v, g in grupos[f].items()})
print(f"Calculado {vorx(f, grupos[f]):.2f}, "
      f"publicado {vx.loc[f, 'vorx']:.2f}")
OUT [10]El roll de vencimientos
{2: 48, 30: 45, 58: 40}
Calculado 25.11, publicado 25.11

El vencimiento de 2 días tiene menos de 8 y se salta: entran los de 30 y 58 días. Con N1=30N_1 = 30, todo el peso cae en el primero, y el VORX es exactamente la volatilidad del swap de 30 días: 25,11, igual que el publicado. La regla de los 8 días evita usar opciones a punto de vencer, cuyo precio está dominado por el ruido y el valor intrínseco.

  1. Repite la prima con la realizada de las 21 sesiones anteriores en vez de las siguientes. ¿Cuánto cambia la media y la fracción de días negativos? ¿Por qué esa versión no sirve para evaluar una estrategia?
Solución
IN [11]python
prima_prev = (vx["vorx"] - real_prev).dropna()
print(f"Media {prima_prev.mean():.2f} (posterior {prima.mean():.2f})")
print(f"Negativa {(prima_prev < 0).mean():.1%} "
      f"(posterior {(prima < 0).mean():.1%})")
print(f"Mínimo {prima_prev.min():.1f} el {prima_prev.idxmin().date()}")
OUT [11]Prima con la realizada pasada
Media 5.33 (posterior 5.30)
Negativa 12.3% (posterior 11.1%)
Mínimo -36.6 el 2020-04-06

La media apenas cambia (5,33 frente a 5,30), pero la fracción de días negativos sube al 12,3 % y el peor día pasa al 6 de abril de 2020, con −36,6: es el día en que la realizada de las 21 sesiones anteriores recoge todo el desplome de marzo mientras el VORX ya ha bajado. Esa comparación no sirve para evaluar una venta de volatilidad, porque la posición abierta hoy gana o pierde con la volatilidad que llega después, no con la que ya pasó.

  1. En la cadena del 4 de junio de 2025, el VORX usa 32 de los 48 strikes del vencimiento de 16 días. ¿Qué pasaría con su varianza si se usaran todos, con el medio de las opciones de bid nulo? Razónalo con la fórmula y compruébalo.
Solución
IN [12]python
g = grupos_hoy[pd.Timestamp("2025-06-20")]
T = 16 / 365
R = tipo(hoy, T)
K = g["strike"].to_numpy()
c = ((g["bid_C"] + g["ask_C"]) / 2).to_numpy()
p = ((g["bid_P"] + g["ask_P"]) / 2).to_numpy()
j = np.argmin(np.abs(c - p))
F = K[j] + math.exp(R * T) * (c[j] - p[j])
i0 = np.flatnonzero(K <= F)[-1]
Q = np.r_[p[:i0], (c[i0] + p[i0]) / 2, c[i0 + 1:]]
todos = (2 / T * np.gradient(K) / K ** 2 * Q
         * math.exp(R * T)).sum() - (F / K[i0] - 1) ** 2 / T
regla = var_cboe(g, T, R)
print(f"Regla de Cboe: σ² = {regla:.6f} "
      f"(vol {100 * math.sqrt(regla):.2f})")
print(f"Todos los strikes: σ² = {todos:.6f} "
      f"(vol {100 * math.sqrt(todos):.2f})")
OUT [12]Sin la regla de los bids nulos
Regla de Cboe: σ² = 0.065072 (vol 25.51)
Todos los strikes: σ² = 0.065269 (vol 25.55)

Cada término ΔKi Q(Ki)/Ki2\Delta K_i\,Q(K_i)/K_i^2 es positivo, así que añadir opciones solo puede subir la suma (los ΔK\Delta K de los vecinos cambian poco). En los datos, la varianza pasa de 0,065072 a 0,065269 y la volatilidad, de 25,51 a 25,55: poco, porque esas opciones son baratas y lejanas. Pero su «precio» no es un precio: con bid nulo nadie compra, y el medio de 0,025 es una convención. La regla de Cboe las excluye y corta el ala tras dos bids nulos seguidos para que el índice no dependa de cotizaciones que no se pueden ejecutar.

Footnotes

  1. Historia y reglas actuales: Cboe, Volatility Index Methodology: Cboe Volatility Index (versión 6.0) y Cboe Volatility Index Mathematics Methodology; versión mensual con al menos una semana de vida: libro blanco de 2009. Enlaces en los recursos de la lección. ↩

  2. STOXX, Strategy Index Guide, edición de septiembre de 2024, sección 9 (EURO STOXX 50 Volatility). Enlace en los recursos. ↩

Respuesta plegada en cada pregunta

Autoevaluación

  1. En la fórmula del VIX, ¿por qué cada opción pesa ΔK/K²?

    • APorque así lo decidió Cboe para dar más peso a las opciones baratas
    • BPorque una cartera de opciones fuera del dinero con pesos 1/K² replica un contrato que paga la varianza realizada (un swap de varianza), con sensibilidad a la varianza que no depende del nivel del índice
    • CPara corregir la paridad put-call
    • DPorque las opciones de strike alto son más líquidas
    Ver respuesta

    Respuesta B. Porque una cartera de opciones fuera del dinero con pesos 1/K² replica un contrato que paga la varianza realizada (un swap de varianza), con sensibilidad a la varianza que no depende del nivel del índice

    Es el resultado de Demeterfi, Derman, Kamal y Zou (1999) y de Carr y Madan: la varianza esperada bajo la medida riesgo neutral es 2/T por la integral de las OTM ponderadas por 1/K². El VIX es 100 veces la raíz de esa varianza a 30 días.

  2. El VORX de los datos está de media 2,9 puntos por encima de la volatilidad ATM a 30 días del modelo. ¿Por qué?

    • APor el ruido de las cotizaciones
    • BPorque un swap de varianza integra toda la sonrisa, y con skew las puts fuera del dinero tienen más volatilidad que el ATM: la raíz de la varianza del swap queda por encima de la volatilidad ATM
    • CPorque el VORX usa días hábiles
    • DPorque el VORX incluye el dividendo
    Ver respuesta

    Respuesta B. Porque un swap de varianza integra toda la sonrisa, y con skew las puts fuera del dinero tienen más volatilidad que el ATM: la raíz de la varianza del swap queda por encima de la volatilidad ATM

    La varianza del swap es una media de las varianzas de toda la sonrisa, con más peso en los strikes bajos (1/K²). Con un skew como el de un índice, esa media supera a la varianza ATM.

  3. La volatilidad implícita a 30 días ha superado a la realizada de los 30 días siguientes en el 89 % de los días de los datos. ¿Es vender volatilidad una renta segura?

    • ASí: gana casi nueve de cada diez veces
    • BNo: en el 11 % restante la realizada puede superar a la implícita por decenas de puntos (76 en marzo de 2020 en los datos), y una venta sin cobertura pierde en pocos días lo ganado en años
    • CSí, si se hace con futuros en vez de opciones
    • DNo, porque la prima es negativa de media
    Ver respuesta

    Respuesta B. No: en el 11 % restante la realizada puede superar a la implícita por decenas de puntos (76 en marzo de 2020 en los datos), y una venta sin cobertura pierde en pocos días lo ganado en años

    La prima de riesgo de volatilidad es la compensación por cargar con ese riesgo de cola. Su media es positiva (5,3 puntos en los datos) y su distribución tiene una cola izquierda muy larga. La lección 3.6.1 mide el resultado de venderla.

  4. ¿Cuál de estas diferencias entre el VSTOXX y el VIX es correcta según los documentos de STOXX y Cboe?

    • AEl VSTOXX usa solo opciones ATM
    • BEl VSTOXX toma como precio de cada opción el más reciente entre la última operación, el punto medio (si la horquilla pasa un filtro) y la liquidación; el VIX usa el punto medio del bid y el ask
    • CEl VSTOXX no interpola entre vencimientos
    • DEl VSTOXX se calcula sobre el DAX
    Ver respuesta

    Respuesta B. El VSTOXX toma como precio de cada opción el más reciente entre la última operación, el punto medio (si la horquilla pasa un filtro) y la liquidación; el VIX usa el punto medio del bid y el ask

    La guía de STOXX define un precio de inclusión que puede ser la operación, el medio o la liquidación, con filtros de horquilla; Cboe usa medios y excluye las opciones con bid nulo. La fórmula de varianza por vencimiento es la misma.

Ideas clave

  • El VIX es 100 veces la raíz de la varianza riesgo neutral a 30 días, estimada con una cartera de opciones fuera del dinero ponderadas por ΔK/K² e interpolada entre dos vencimientos.
  • Con la fórmula de Cboe sobre la cadena publicada se recalcula el VORX de los datos al céntimo en los 3.017 días con cadena.
  • Las puts aportan la mayor parte de la varianza del índice por el skew: en el vencimiento de 44 días del 4 de junio de 2025, el 69 %.
  • Prima de riesgo de volatilidad en los datos: VORX menos realizada posterior, media de 5,3 puntos y mediana de 6,4; la realizada lo supera el 11 % de los días, con un máximo de 76 puntos en marzo de 2020.
  • El VSTOXX usa la misma fórmula de varianza por vencimiento, pero otros precios (operación, medio filtrado o liquidación), otros tipos (€STR, EURIBOR) y subíndices por vencimiento.

Recursos

Datos