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.
- Módulo
- 3.4 · Teoría y valoración de opciones
- Duración
- 70 min
Lecciones del nivel 3
3.1 · Futuros en profundidad
3.2 · Spreads de futuros
- 3.2.1 Spreads de calendario, butterflies y condors
- 3.2.2 Spreads intercommodity: crack, spark y crush
- 3.2.3 Spreads de tipos y de índices: 2s10s, NOB, NQ/ES y Europa frente a EE. UU.
- 3.2.4 Ratios de cobertura: por nominal, por DV01 y por volatilidad
- 3.2.5 Datos y backtesting de spreads: cotizados, sintéticos y por patas
- 3.2.6 Estacionalidad de spreads: análisis multi-año, patrones y trampas
3.3 · Forex avanzado
3.4 · Teoría y valoración de opciones
- 3.4.1 Payoffs, paridad put-call y límites de arbitraje
- 3.4.2 Árboles binomiales, Black-Scholes-Merton, Black-76 y Monte Carlo
- 3.4.3 Griegas: delta, gamma, vega, theta, rho, vanna, volga y charm
- 3.4.4 Volatilidad implícita: Newton-Raphson, Brent, py_vollib y QuantLib
- 3.4.5 Sonrisa, skew y estructura temporal; superficie de volatilidad con SVI
- 3.4.6 Volatilidad realizada frente a implícita; prima de riesgo de volatilidad; VIX y VSTOXX
- 3.4.7 Modelos avanzados: Heston, SABR y volatilidad local
3.5 · Estructuras de opciones en Python
- 3.5.1 Estructuras direccionales y verticales: call, put, covered call, collar y spreads
- 3.5.2 Estructuras de volatilidad y rango: straddles, strangles, cóndores y mariposas
- 3.5.3 Calendarios, diagonales, ratios, backspreads, risk reversals y jade lizard
- 3.5.4 Herramienta propia: payoff, griegas agregadas y escenarios
- 3.5.5 Gestión: ajustes, roll, cierre anticipado, asignación temprana y pin risk
3.6 · Estrategias sistemáticas de volatilidad y opciones
3.7 · Cripto derivados y estrategias específicas
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 y sesiones, (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.
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 :
Cada término tiene su regla en la metodología de Cboe:
| Elemento | Regla |
|---|---|
| Tiempo hasta el vencimiento en años de 365 días (en el VIX real, en minutos) | |
| Tipo libre de riesgo al plazo (en el VIX real, curva del Tesoro con spline cúbico) | |
| Forward por la paridad en el strike donde es mínimo: , con medios | |
| Primer strike igual o inferior a | |
| Opciones | Puts por debajo de y calls por encima, desde hacia fuera; se excluyen las de bid nulo y se para tras dos bids nulos seguidos; en , la media de put y call |
| Media de la distancia a los strikes vecinos; en los extremos, la distancia al único vecino | |
| 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:
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
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")}){'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.
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%}")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 mínimo, 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 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).
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}")VORX calculado: 25.89
VORX publicado: 25.89Con y , 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.
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 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
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])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.
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"]]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.02El 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 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.
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}")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.53La 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.
anual = prima.groupby(prima.index.year).agg(
media="mean", mediana="median", minimo="min",
negativa=lambda z: (z < 0).mean())
anual.round(2).Tfecha 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.14La 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.
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.
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)}")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 142Con 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:
| Aspecto | VIX (Cboe) | VSTOXX (STOXX) |
|---|---|---|
| Opciones | SPX mensuales y semanales, de 23 a 37 días | OESX de Eurex; 8 subíndices por vencimiento, de 1 a 24 meses |
| Índice principal | 30 días, interpolando dos vencimientos | 30 días (y otros 11 plazos hasta 360), interpolando dos subíndices |
| Precio de cada opción | Punto medio del bid y el ask | El 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) |
| Exclusiones | Bids nulos; se para tras dos seguidos | Precios por debajo de 0,5 puntos; un subíndice necesita al menos 5 opciones |
| Tipos | Curva del Tesoro de EE. UU. | €STR, EURIBOR y REX a 2 años, interpolados |
| Tiempo | Minutos | Segundos; 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):
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}")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.61El 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
- 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
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}"){2: 48, 30: 45, 58: 40}
Calculado 25.11, publicado 25.11El vencimiento de 2 días tiene menos de 8 y se salta: entran los de 30 y 58 días. Con , 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.
- 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
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()}")Media 5.33 (posterior 5.30)
Negativa 12.3% (posterior 11.1%)
Mínimo -36.6 el 2020-04-06La 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ó.
- 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
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})")Regla de Cboe: σ² = 0.065072 (vol 25.51)
Todos los strikes: σ² = 0.065269 (vol 25.55)Cada término es positivo, así que añadir opciones solo puede subir la suma (los 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
-
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. ↩
-
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
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.
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.
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.
¿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
Documentación
- Cboe: Volatility Index Methodology, Cboe Volatility Index (versión 6.0, febrero de 2026)
- Cboe: Cboe Volatility Index Mathematics Methodology
- Cboe (2009): The CBOE Volatility Index, VIX (libro blanco con vencimientos mensuales; copia de la OIC)
- STOXX: Strategy Index Guide, sección 9, EURO STOXX 50 Volatility (VSTOXX) (septiembre de 2024)