Lección 3.6.2 · 75 min

Delta hedging, gamma scalping y dispersión

Una opción con la delta cubierta deja de ser una apuesta sobre la dirección y pasa a ser una apuesta sobre la volatilidad: gana si el mercado se mueve más de lo que el precio de la opción descontaba. Esta lección lo mide en 143 straddles cubiertos de los datos del curso, separa el P&L en gamma, vega y resto, y prueba cuánto importa la frecuencia de la cobertura.

Fig. — Straddle ATM comprado y cubierto en delta cada día, 143 operaciones mensuales: P&L real acumulado frente al término de gamma acumulado, ½·Γ·S²·(r² − σ²·Δt) (suma simple de cada operación en % del índice; datos sintéticos de ORX, 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)

Qué queda al cubrir la delta

Una call comprada gana si el subyacente sube. Si al mismo tiempo se vende la cantidad de subyacente que dice su delta, el primer orden del movimiento desaparece: lo que gana la call por una subida pequeña lo pierde la venta. Lo que queda es el segundo orden, la curvatura. La lección 3.4.3 hace la atribución completa del P&L de una put ATM en delta, gamma, vega y theta; aquí basta su resultado para la cartera cubierta (la opción y la posición −Δ-\Delta en el subyacente). Con el desarrollo de Taylor del valor de la opción V(S,σ,t)V(S, \sigma, t), el P&L de un día es

dΠ≈12Γ (dS)2+Θ dt⏟gamma y theta+V dσ⏟vega+…d\Pi \approx \underbrace{\tfrac12 \Gamma\, (dS)^2 + \Theta\, dt}_{\text{gamma y theta}} + \underbrace{\mathcal{V}\, d\sigma}_{\text{vega}} + \dots

En Black-Scholes (sin tipos ni dividendos, para ver la idea) la theta y la gamma están atadas por la ecuación de valoración: Θ=−12ΓS2σi2\Theta = -\tfrac12 \Gamma S^2 \sigma_i^2, con σi\sigma_i la volatilidad implícita con la que se valora la opción. Sustituyendo y llamando rt=dS/Sr_t = dS/S al rendimiento del día:

dΠ≈12ΓS2(rt2−σi2 Δt)+V dσi.d\Pi \approx \tfrac12 \Gamma S^2 \left(r_t^2 - \sigma_i^2\, \Delta t\right) + \mathcal{V}\, d\sigma_i .

Es la fórmula central de la lección. El primer término dice que una opción comprada y cubierta gana el día en que el movimiento al cuadrado supera al que descontaba la implícita (σi2Δt\sigma_i^2 \Delta t) y pierde el resto de días, cuando paga la theta sin compensación. Sumado sobre la vida de la opción, el resultado es la diferencia entre la varianza realizada y la implícita ponderada por ΓS2\Gamma S^2 de cada día. El segundo término es el efecto de que la propia implícita cambie (lección 3.4.3). Bakshi y Kapadia (2003) usan esta misma descomposición para medir la prima de riesgo de volatilidad en opciones reales del S&P 500: las ganancias de las opciones compradas y cubiertas son negativas de media.

muchopocoCompra straddleCalcula deltaVende deltaSe mueve SGana gammaPaga thetaRehace delta
Fuente Mermaid
flowchart TB
  A[Compra straddle] --> B[Calcula delta]
  B --> C[Vende delta]
  C --> D{Se mueve S}
  D -- mucho --> E[Gana gamma]
  D -- poco --> F[Paga theta]
  E --> G[Rehace delta]
  F --> G
  G --> B

El bucle es lo que en las mesas se llama gamma scalping: con gamma positiva, la delta sube cuando el precio sube y baja cuando baja, así que rehacer la cobertura obliga a vender después de las subidas y a comprar después de las bajadas. Cada vuelta del bucle captura un trozo del movimiento. La theta es el precio de ese privilegio.

Convenciones de esta lección: precios por unidad de índice (el contrato real multiplica por 100 USD), TT en días naturales / 365 (las opciones de ORX vencen al cierre), tipo y rentabilidad por dividendo continuos, volatilidad anual en tanto por uno, vega por unidad de volatilidad (1,00 = 100 puntos; dividida entre 100, por punto) y P&L de cada operación en % del índice al abrirla.

Los datos: un straddle ATM cada mes

Los datos son los del índice ficticio ORX, que imita al S&P 500 (lección 3.4.6): opciones3_indice.csv con el cierre, la curva de tipos y la rentabilidad por dividendo, y opciones3_cadena.parquet, la cadena diaria con los tres vencimientos mensuales más cercanos. Son datos sintéticos con una prima de riesgo de varianza plantada: la superficie del modelo cotiza la varianza 1,30 veces por encima de la esperada en calma y 1,00 en el estrés máximo (public/curso/datos/nivel-3/README.md). Esa es la respuesta conocida que la lección tiene que recuperar.

IN [01]python
import numpy as np
import pandas as pd
from scipy.special import ndtr
from scipy.stats import norm

ind = pd.read_csv("opciones3_indice.csv", parse_dates=["fecha"])
ind = ind.set_index("fecha")
cad = pd.read_parquet("opciones3_cadena.parquet")
cad["medio"] = (cad["bid"] + cad["ask"]) / 2
S = ind["cierre"]
r = ind["tipo_1m"] / 100                 # continuo, tanto por uno
q = ind["rentabilidad_dividendo"] / 100  # continuo
print(len(cad), "filas,", cad["fecha"].nunique(), "sesiones")
dia = cad[(cad["fecha"] == "2020-02-21")
          & (cad["vencimiento"] == "2020-03-20")]
k = dia.loc[(dia["strike"] - S["2020-02-21"]).abs().idxmin(),
            "strike"]
print("S =", S["2020-02-21"], " strike ATM =", k)
dia[dia["strike"] == k][["tipo", "bid", "ask", "iv_modelo"]]
OUT [01]Índice, cadena y convenciones
544548 filas, 3017 sesiones
S = 2978.34  strike ATM = 3000.0
       tipo   bid   ask  iv_modelo
275893    C  28.1  28.5     0.1191
275911    P  52.4  52.9     0.1191

El 21 de febrero de 2020 el índice cerraba en 2.978,34 y el strike más cercano era 3.000. La call y la put de ese strike, con 28 días, tienen la misma iv_modelo, la volatilidad implícita verdadera del modelo, sin el ruido de las cotizaciones: un 11,9 %. En unos datos reales no existe esa columna y la implícita se calcula desde el medio con los métodos de la lección 3.4.4; aquí se usa para que el error de la implícita no se mezcle con lo que se quiere medir.

Las griegas, con el forward F=Se(r−q)TF = S e^{(r-q)T}, salen de las fórmulas de Black-Scholes-Merton de la lección 3.4.2. Antes de usarlas, una comprobación contra QuantLib con la call de ese día:

IN [02]python
import QuantLib as ql

def bs(tipo, S, K, T, iv, r, q):
    """Precio, delta, gamma y vega (por unidad de volatilidad)."""
    F = S * np.exp((r - q) * T)
    sd = iv * np.sqrt(T)
    d1 = (np.log(F / K) + 0.5 * sd * sd) / sd
    df = np.exp(-r * T)
    call = df * (F * ndtr(d1) - K * ndtr(d1 - sd))
    precio = np.where(tipo == "C", call, call - df * (F - K))
    delta = np.exp(-q * T) * (ndtr(d1) - (tipo == "P"))
    gamma = np.exp(-q * T) * norm.pdf(d1) / (S * sd)
    vega = S * np.exp(-q * T) * norm.pdf(d1) * np.sqrt(T)
    return precio, delta, gamma, vega

f0 = pd.Timestamp("2020-02-21")
mio = bs("C", S[f0], k, 28 / 365, 0.1191, r[f0], q[f0])

hoy = ql.Date(21, 2, 2020)
ql.Settings.instance().evaluationDate = hoy
dc = ql.Actual365Fixed()
curva = lambda x: ql.YieldTermStructureHandle(
    ql.FlatForward(hoy, float(x), dc))
vol = ql.BlackVolTermStructureHandle(
    ql.BlackConstantVol(hoy, ql.NullCalendar(), 0.1191, dc))
proc = ql.BlackScholesMertonProcess(
    ql.QuoteHandle(ql.SimpleQuote(float(S[f0]))),
    curva(q[f0]), curva(r[f0]), vol)
op = ql.EuropeanOption(ql.PlainVanillaPayoff(ql.Option.Call, k),
                       ql.EuropeanExercise(hoy + 28))
op.setPricingEngine(ql.AnalyticEuropeanEngine(proc))
pd.DataFrame({"propio": [float(x) for x in mio],
              "QuantLib": [op.NPV(), op.delta(), op.gamma(),
                           op.vega()]},
             index=["precio", "delta", "gamma", "vega"]).round(6)
OUT [02]Black-Scholes con dividendo continuo, contra QuantLib
            propio    QuantLib
precio   28.323279   28.323279
delta     0.408275    0.408275
gamma     0.003949    0.003949
vega    320.040712  320.040712

Coinciden en las seis cifras. La vega de 320,0 es por unidad de volatilidad: un punto (0,01) mueve la call 3,20 puntos del índice, 320 USD por contrato.

La operación es la de la tabla de realismo del README: el día de cada vencimiento mensual se abre un straddle ATM del vencimiento siguiente con al menos 20 días, se cubre la delta al cierre de cada sesión y se liquida al vencimiento por su valor intrínseco. La primera operación entra el 2 de enero de 2014 y cada una de las siguientes, el día en que vence la anterior; salen 143:

IN [03]python
vencs = sorted(cad["vencimiento"].unique())
fechas = S.index
con_cadena = set(cad["fecha"])
ops, j = [], 0
while True:
    f = fechas[j]
    v = next((x for x in vencs if (x - f).days >= 20), None)
    if v is None or v > fechas[-1]:
        break
    if f not in con_cadena:
        j += 1
        continue
    sub = cad[(cad["fecha"] == f) & (cad["vencimiento"] == v)]
    kk = sub.loc[(sub["strike"] - S[f]).abs().idxmin(), "strike"]
    for d in fechas[(fechas >= f) & (fechas <= v)]:
        ops.append((f, v, kk, d))
    j = fechas.get_loc(v)      # la siguiente entra al vencer esta

pan = pd.DataFrame(ops, columns=["entrada", "vencimiento",
                                 "strike", "fecha"])
pan["op"] = pan.groupby("entrada").ngroup()
pc = cad.pivot_table(index=["fecha", "vencimiento", "strike"],
                     columns="tipo", values=["medio", "iv_modelo"])
pc.columns = [f"{a}_{b}" for a, b in pc.columns]
pan = pan.merge(pc.reset_index(), how="left",
                on=["fecha", "vencimiento", "strike"])
for col, serie in (("S", S), ("r", r), ("q", q)):
    pan[col] = serie.reindex(pan["fecha"]).to_numpy()
pan["T"] = (pan["vencimiento"] - pan["fecha"]).dt.days / 365
print(pan["op"].nunique(), "operaciones,", len(pan), "filas")
print("sin cotización:", pan["medio_C"].isna().sum(),
      "filas (los días de vencimiento y el 2019-07-23)")
OUT [03]Las 143 operaciones y su panel diario
143 operaciones, 3153 filas
sin cotización: 144 filas (los días de vencimiento y el 2019-07-23)

Primero, la respuesta conocida

El README da el resultado de la versión vendida de esta operación (cobro al medio, delta de Black-Scholes con iv_modelo, caja al tipo a 1 mes y dividendos de la cobertura): 143 operaciones, P&L medio del 0,44 % del índice, positivo en el 71 % y peor operación de −11,8 %, la que entra el 2020-02-21. Reproducirlo es la prueba de que el panel y las griegas están bien:

IN [04]python
def vendido_cubierto(g):
    g = g.reset_index(drop=True)
    kk, s0 = g["strike"][0], g["S"][0]
    caja, h = g["medio_C"][0] + g["medio_P"][0], 0.0
    for t in range(len(g) - 1):
        if np.isfinite(g["iv_modelo_C"][t]):  # hay cadena
            dl = sum(bs(tp, g["S"][t], kk, g["T"][t],
                        g[f"iv_modelo_{tp}"][t], g["r"][t],
                        g["q"][t])[1] for tp in ("C", "P"))
            caja -= (dl - h) * g["S"][t]   # compra la delta
            h = dl
        caja += h * g["S"][t] * g["q"][t] / 252   # dividendos
        caja *= np.exp(g["r"][t] / 252)           # caja al 1 m
    s_fin = g["S"].iloc[-1]
    caja += h * s_fin - abs(s_fin - kk)           # liquidación
    return 100 * caja / s0

corto = pan.groupby("op").apply(vendido_cubierto)
entradas = pan.groupby("op")["entrada"].first()
print(f"{len(corto)} operaciones; media {corto.mean():.2f} %; "
      f"positivas {(corto > 0).mean():.0%}")
print(f"peor {corto.min():.1f} % (entrada "
      f"{entradas[corto.idxmin()].date()})")
OUT [04]El straddle vendido y cubierto del README
143 operaciones; media 0.44 %; positivas 71%
peor -11.8 % (entrada 2020-02-21)

Las cuatro cifras coinciden con el README. Vender el straddle y cubrirlo gana de media, y gana casi tres de cada cuatro meses. La peor operación borra de un golpe lo que ganan 27 operaciones medias: es la forma de la venta de prima de la lección 3.6.1, ahora con la dirección neutralizada. Lo que queda en el resultado es la diferencia entre la volatilidad que se cobró y la que hubo.

La descomposición del P&L, operación a operación

A partir de aquí la lección mira el lado comprado, el del gamma scalper, y sin caja ni dividendos (suman menos de 0,01 puntos por operación) para que cada término se vea limpio. Para cada día se calcula el P&L real (cambio del medio del straddle menos la delta por el cambio del índice; el último día, el valor intrínseco) y los dos términos de la fórmula: el de gamma, 12Γ(ΔS2−S2σi2Δt)\tfrac12 \Gamma (\Delta S^2 - S^2 \sigma_i^2 \Delta t), con Δt\Delta t en días naturales entre sesiones (el fin de semana cuenta tres días de theta), y el de vega, VΔσi\mathcal{V} \Delta \sigma_i con la implícita del mismo strike.

IN [05]python
p = pan.dropna(subset=["medio_C"]).copy()
p["V"] = p["medio_C"] + p["medio_P"]
p["iv"] = p["iv_modelo_C"]          # misma en la call y la put
args = [p[c].to_numpy() for c in ("S", "strike", "T", "iv",
                                  "r", "q")]
gc, gp = bs("C", *args), bs("P", *args)
p["delta"], p["gamma"] = gc[1] + gp[1], gc[2] + gp[2]
p["vega"] = gc[3] + gp[3]
fin = pan[pan["fecha"] == pan["vencimiento"]].copy()
fin["V"] = (fin["S"] - fin["strike"]).abs()   # intrínseco
a = (pd.concat([p, fin]).sort_values(["op", "fecha"])
     .reset_index(drop=True))
sig = a.groupby("op").shift(-1)       # la sesión siguiente
dS = sig["S"] - a["S"]
dt = (sig["fecha"] - a["fecha"]).dt.days / 365
a["real"] = (sig["V"] - a["V"]) - a["delta"] * dS
a["gamma_pnl"] = 0.5 * a["gamma"] * (dS**2
                                     - a["S"]**2 * a["iv"]**2 * dt)
a["vega_pnl"] = (a["vega"] * (sig["iv"] - a["iv"])).fillna(0)
dias = a.dropna(subset=["real"])    # sin la fila del vencimiento

s0 = pan.groupby("op")["S"].first()
por = (dias.groupby("op")[["real", "gamma_pnl", "vega_pnl"]].sum()
       .div(s0, axis=0) * 100)
por["resto"] = por["real"] - por["gamma_pnl"] - por["vega_pnl"]
print(por.describe().loc[["mean", "std", "min", "max"]].round(2))
c1 = por["real"].corr(por["gamma_pnl"])
c2 = por["real"].corr(por["gamma_pnl"] + por["vega_pnl"])
print(f"correlación con el real: gamma {c1:.2f}; "
      f"gamma + vega {c2:.2f}")
OUT [05]Gamma, vega y resto
       real  gamma_pnl  vega_pnl  resto
mean  -0.45      -0.64      0.35  -0.15
std    1.66       1.43      0.98   0.45
min   -3.46      -3.32     -1.81  -2.55
max   11.72       9.62      5.34   2.09
correlación con el real: gamma 0.89; gamma + vega 0.97

La figura de la cabecera es esta tabla acumulada operación a operación: el P&L real y el término de gamma bajan casi en paralelo, mes a mes, y suben de golpe en las crisis. El salto mayor es el del straddle comprado el 2020-02-21: 11,7 puntos de P&L, de los que 9,6 los explica la gamma. Al final de la muestra el real acumula −64,3 puntos (suma simple de las 143 operaciones) y el término de gamma, −92,1; la distancia entre las dos líneas es, sobre todo, el término de vega.

Tres lecturas de la tabla:

  • La media del término de gamma es negativa (−0,64 % por operación). Es la prima de riesgo de varianza plantada: la implícita suele estar por encima de la realizada, y quien compra la opción paga más theta de la que recupera con los movimientos. El P&L real medio, −0,45 %, es el espejo del +0,44 % del vendido (sin la caja y los dividendos).
  • El término de vega no es ruido. Su media es positiva (+0,35 %) por un efecto de la sonrisa: la implícita de un strike fijo sube cuando el índice se aleja de él hacia arriba (el strike queda por debajo del dinero, en la zona cara del skew), y en una muestra con el índice al alza eso pasa más a menudo. Con la vega, la correlación con el real sube a 0,97.
  • El resto recoge los términos de orden superior (vanna y volga, lección 3.4.3), el ruido de las cotizaciones alrededor del medio y el error de cubrir solo al cierre. Su media es de −0,15 puntos y su desviación típica, 0,45; en alguna operación llega a 2,5 puntos.

Realizada frente a implícita: el camino importa

La versión de bolsillo de la fórmula dice que el P&L es proporcional a σR2−σi2\sigma_R^2 - \sigma_i^2, la diferencia entre la varianza realizada en la vida de la opción y la implícita al comprarla. Es una buena primera aproximación, pero supone una gamma constante, y la gamma no lo es: es máxima en el dinero y crece al acercarse el vencimiento. El mismo movimiento del 2 % vale mucho más la última semana y en el strike que el primer día y con el índice lejos.

IN [06]python
lr = np.log(S).diff()
filas = []
for o, g in pan.groupby("op"):
    f, v = g["entrada"].iloc[0], g["vencimiento"].iloc[0]
    rr = lr.loc[f:v].iloc[1:]
    rv = np.sqrt((rr**2).sum() / ((v - f).days / 365))
    filas.append((f.date(), g["iv_modelo_C"].iloc[0], rv))
e = pd.DataFrame(filas, columns=["entrada", "iv0", "rv"])
e["pnl"] = por["real"].to_numpy()
e["dif_var"] = e["rv"]**2 - e["iv0"]**2
print(f"realizada > implícita en el {(e.rv > e.iv0).mean():.0%}"
      f" de las operaciones")
print(f"correlación P&L con rv² − iv²: "
      f"{e['pnl'].corr(e['dif_var']):.2f}")
e.sort_values("pnl").iloc[[0, 1, 2, -3, -2, -1]].round(3)
OUT [06]P&L frente a la diferencia de varianzas del mes
realizada > implícita en el 23% de las operaciones
correlación P&L con rv² − iv²: 0.81
        entrada    iv0     rv     pnl  dif_var
81   2020-10-16  0.242  0.123  -3.460   -0.044
84   2021-01-15  0.222  0.116  -3.229   -0.036
83   2020-12-18  0.253  0.125  -2.707   -0.049
29   2016-06-17  0.132  0.271   3.290    0.056
134  2025-03-21  0.228  0.518   7.617    0.216
73   2020-02-21  0.119  0.857  11.719    0.720

La realizada supera a la implícita solo en el 23 % de las operaciones, y la correlación del P&L con la diferencia de varianzas del mes es de 0,81, menos que los 0,89 del término de gamma diario. La tabla enseña los dos extremos. Las tres peores operaciones del comprador son de finales de 2020 y principios de 2021: se compró con implícitas del 22 al 25 % tras la crisis y el mercado se movió un 12 %. Las mejores son las crisis: la entrada del 2020-02-21 se compró al 11,9 % y el índice se movió al 86 % anualizado (+11,7 %); la del 2025-03-21 se compró al 22,8 % y la realizada fue del 51,8 % (+7,6 %). Lo que gana el comprador depende de la distancia entre las dos, y en la mayoría de los meses esa distancia es negativa.

Frecuencia de cobertura: lo que se sabe y lo que miden los datos

¿Cada cuánto hay que rehacer la delta? La fórmula sale de cubrir en tiempo continuo; en la práctica se cubre de vez en cuando y cada cobertura cuesta horquilla. Para separar las dos cosas, primero una simulación con la respuesta conocida: un paseo lognormal con la volatilidad realizada igual a la implícita (20 %), una call ATM a 28 días vendida a su precio de Black-Scholes y cubierta NN veces en su vida. Con la volatilidad bien estimada, el valor esperado del resultado es 0 sea cual sea NN; lo que cambia es su dispersión:

IN [07]python
rng = np.random.default_rng(362)
S0, K0, T0, sg, n_tray, pasos = 100.0, 100.0, 28 / 365, 0.20, 4000, 160

def call_bs(s, t):
    t = np.maximum(t, 1e-12)
    sd = sg * np.sqrt(t)
    d1 = (np.log(s / K0) + 0.5 * sd * sd) / sd
    return s * ndtr(d1) - K0 * ndtr(d1 - sd), ndtr(d1)

prima = call_bs(S0, T0)[0]
dtp = T0 / pasos
z = rng.standard_normal((n_tray, pasos))
cam = S0 * np.exp(np.cumsum(-0.5 * sg**2 * dtp
                            + sg * np.sqrt(dtp) * z, axis=1))
cam = np.c_[np.full(n_tray, S0), cam]
sim = []
for N in [1, 5, 20, 80, 160]:
    i = np.arange(0, pasos + 1, pasos // N)
    s_, t_ = cam[:, i], T0 - i * dtp
    d_ = call_bs(s_[:, :-1], t_[:-1])[1]
    pnl = (prima + (d_ * np.diff(s_, axis=1)).sum(axis=1)
           - np.maximum(cam[:, -1] - K0, 0))
    sim.append((N, pnl.mean(), pnl.std(), pnl.std() * np.sqrt(N)))
sim = pd.DataFrame(sim, columns=["coberturas", "media", "desv",
                                "desv_x_raizN"])
sim = sim.set_index("coberturas")
print(f"prima de la call: {prima:.3f}")
sim.round(3)
OUT [07]Simulación: σ realizada = σ implícita
prima de la call: 2.210
            media   desv  desv_x_raizN
coberturas
1           0.020  1.657         1.657
5           0.016  0.814         1.820
20          0.011  0.428         1.916
80          0.004  0.212         1.899
160         0.001  0.149         1.890

La media queda en torno a cero en todas las filas (entre +0,001 y +0,02 puntos sobre una prima de 2,21: error de muestreo con 4.000 caminos), y la desviación típica cae con 1/N1/\sqrt{N}: la última columna, la desviación por N\sqrt N, se mantiene cerca de 1,7-1,9. Pasar de 5 a 80 coberturas (16 veces más) divide la dispersión por 4. Cada error de cobertura de un intervalo es 12ΓS2σ2Δt (Z2−1)\tfrac12 \Gamma S^2 \sigma^2 \Delta t\,(Z^2 - 1), con varianza proporcional a Δt2\Delta t^2; sumados NN intervalos independientes de longitud T/NT/N, la varianza total queda en N(T/N)2∝1/NN (T/N)^2 \propto 1/N.

Fig. — Simulación con la volatilidad realizada igual a la implícita (20 %): desviación típica del P&L de una call ATM a 28 días vendida y cubierta N veces, frente a la recta 1/√N (4.000 caminos)

En los datos solo hay cierres diarios, así que la frecuencia máxima es una vez al día: lo que pase dentro de la sesión no se puede medir aquí. Sí se pueden comparar coberturas menos frecuentes y bandas de delta (rehacer la cobertura solo cuando la delta de la cartera se aleja más de un umbral):

IN [08]python
def comprado(g, cada=1, banda=None, coste=0.0):
    """Straddle comprado; vende la delta cada `cada` sesiones o
    cuando se aleja más de `banda`. `coste`: fracción del nominal
    negociado en cada cobertura."""
    s, val, dl = (g[c].to_numpy() for c in ("S", "V", "delta"))
    h, pnl, n, cst = 0.0, -val[0], 0, 0.0
    for t in range(len(s) - 1):
        if np.isfinite(dl[t]):
            obj = -dl[t]
            toca = (t % cada == 0) if banda is None \
                else abs(obj - h) > banda
            if toca:
                cst += abs(obj - h) * s[t] * coste
                h, n = obj, n + 1
        pnl += h * (s[t + 1] - s[t])
    return 100 * (pnl + val[-1] - cst) / s[0], n

def resumen(**kw):
    res = [comprado(g, **kw) for _, g in a.groupby("op")]
    x = np.array([v for v, _ in res])
    return {"media": x.mean(), "desv": x.std(),
            "coberturas": np.median([n for _, n in res])}

reglas = {"cada sesión": {}, "cada 2": {"cada": 2},
          "cada 5": {"cada": 5}, "cada 10": {"cada": 10},
          "solo al abrir": {"cada": 10_000},
          "banda 0,05": {"banda": 0.05}, "banda 0,10": {"banda": 0.1},
          "banda 0,20": {"banda": 0.2}}
pd.DataFrame({k: resumen(**v) for k, v in reglas.items()}).T.round(2)
OUT [08]Frecuencia y bandas en los datos
               media  desv  coberturas
cada sesión    -0.45  1.66        20.0
cada 2         -0.38  1.91        10.0
cada 5         -0.17  3.06         4.0
cada 10        -0.17  3.50         2.0
solo al abrir  -0.03  3.63         1.0
banda 0,05     -0.44  1.67        14.0
banda 0,10     -0.43  1.75         9.0
banda 0,20     -0.41  1.83         5.0

La desviación típica sube al cubrir menos: 1,66 puntos con la cobertura diaria, 3,06 cada cinco sesiones y 3,63 si la delta solo se cubre al abrir. La media también cambia (−0,45 % diaria, −0,17 % cada cinco, −0,03 % sin rehacer), y es tentador leerlo como que cubrir menos «es mejor». No lo es: cubrir menos deja exposición a la dirección del índice, y en una muestra en la que el índice sube un 12,5 % al año esa exposición ha sumado. Es una apuesta direccional escondida, no una mejora de la cobertura. Las bandas sí son una mejora: con un umbral de 0,20 de delta la desviación típica es 1,83, casi la de la cobertura diaria, con 5 coberturas por operación en lugar de 20.

El coste completa la cuenta. Con la cobertura hecha en un futuro del índice o en una cesta, cada rebalanceo paga horquilla y comisión sobre el nominal negociado. Las cifras que siguen son supuestos (1 y 5 puntos básicos del nominal negociado), no precios de ningún mercado:

IN [09]python
cost = {f"{k} · {c * 1e4:.0f} pb": resumen(coste=c, **v)
        for c in (0.0001, 0.0005)
        for k, v in (("cada sesión", {}),
                     ("banda 0,20", {"banda": 0.2}))}
pd.DataFrame(cost).T.round(2)
OUT [09]Coste de la cobertura
                    media  desv  coberturas
cada sesión · 1 pb  -0.47  1.65        20.0
banda 0,20 · 1 pb   -0.42  1.83         5.0
cada sesión · 5 pb  -0.57  1.65        20.0
banda 0,20 · 5 pb   -0.49  1.82         5.0

Con un coste de 1 pb, la cobertura diaria resta unos 2-3 pb por operación; con 5 pb, más de una décima. Leland (1985) estudió la réplica con costes proporcionales y revisión a intervalos fijos: acortar el intervalo reduce el error de réplica pero dispara el coste, y propuso valorar la opción con una volatilidad ajustada por esos costes. En un straddle del índice a un mes, con horquillas de futuros de una fracción de punto básico, el coste importa poco; en opciones sobre acciones poco líquidas o con coberturas intradía, mucho.

Gamma scalping: cuándo gana el comprador

El gamma scalper compra la opción y vive de rehacer la cobertura. La tabla de la descomposición ya dio la respuesta media: pierde, porque paga la prima de riesgo de varianza. Gana cuando la realizada supera a la implícita, y eso ocurre casi solo en los inicios de las crisis. Por terciles del VORX (el índice de volatilidad de ORX, que imita al VIX) el día de entrada:

IN [10]python
vx = pd.read_csv("vol3_indices.csv", parse_dates=["fecha"])
vx = vx.set_index("fecha")["vorx"]
e["vorx"] = vx.reindex(pd.to_datetime(e["entrada"])).to_numpy()
e["tercil"] = pd.qcut(e["vorx"], 3, labels=["bajo", "medio", "alto"])
e.groupby("tercil", observed=True)["pnl"].agg(
    ["count", "mean", "median",
     ("positivas", lambda s: (s > 0).mean())]).round(2)
OUT [10]El comprador por nivel del VORX al entrar
        count  mean  median  positivas
tercil
bajo       48  0.11   -0.30       0.38
medio      47 -0.83   -0.88       0.17
alto       48 -0.64   -0.96       0.27

Con el VORX bajo al entrar, la media es positiva (+0,11 %) pero la mediana es negativa (−0,30 %) y solo gana el 38 % de las veces: la media la levanta la operación del 2020-02-21, comprada con el VORX en 15. Con el VORX medio o alto, el comprador pierde de media y gana en una de cada cuatro o cinco. Es el perfil de un seguro: pérdidas pequeñas casi todos los meses y una ganancia grande muy de vez en cuando, que llega cuando la volatilidad estaba barata y el mercado se rompe.

En el 2020-02-21, el gamma scalping se ve a la vista. El strike (3.000) estaba algo por encima del índice, así que el straddle empezó con delta negativa y la cobertura era una pequeña compra de índice. Al caer el índice en marzo, la delta del straddle llegó a −0,89 y la cobertura pasó a ser una compra de casi 0,9 unidades de índice, rehecha cada cierre: cada día de movimiento fuerte la posición gana por la curvatura y, al rehacer la cobertura, compra después de las caídas y vende después de los rebotes. Los cuatro mejores días (el P&L de cada fila es el del intervalo que empieza ese día):

IN [11]python
d0 = dias[dias["op"] == e["pnl"].idxmax()]
dd = d0.set_index("fecha")[["S", "delta", "gamma_pnl", "real"]]
dd[["gamma_pnl", "real"]] *= 100 / d0["S"].iloc[0]
print(f"{len(dd)} días; P&L real {dd['real'].sum():.2f} %; "
      f"de gamma {dd['gamma_pnl'].sum():.2f} %")
dd.sort_values("real").tail(4).round(3)
OUT [11]El mejor mes del comprador, día a día
20 días; P&L real 11.72 %; de gamma 9.62 %
                  S  delta  gamma_pnl   real
fecha
2020-03-11  2713.79 -0.891      1.404  0.753
2020-03-09  2803.60 -0.669      0.598  1.158
2020-03-10  2933.60 -0.223      2.697  2.286
2020-03-06  3034.07  0.205      4.636  5.556

Los cuatro intervalos suman 9,8 de los 11,7 puntos; el del 2020-03-06 recoge la caída del 7,6 % hasta el lunes 9, con la delta todavía en +0,21 tras el rebote del día 6. Sin esos días el mes habría sido el habitual del comprador: theta pagada día a día. Por eso una cartera de gamma comprada se presenta con su distribución, no con su media, y por eso quien la vende (lección 3.6.1) debe dimensionar su posición para sobrevivir a ese mes.

Estructura temporal y skew

Las dos dimensiones de la superficie que no son el nivel también se operan con la delta cubierta. La estructura temporal compara implícitas de vencimientos distintos: un calendario vende el vencimiento corto y compra el largo (lección 3.5.3) y apuesta a que la pendiente vuelve a su forma habitual. El skew compara strikes: un risk reversal cubierto en delta (comprar la call y vender la put del mismo vencimiento, o al revés) apuesta a que la diferencia de implícitas entre puts y calls está cara o barata frente a lo que el índice hará.

opciones3_verdad.parquet tiene la superficie verdadera: la varianza total ATM (theta) de cada vencimiento. Con ella se interpola la implícita ATM a 30 y a 60 días constantes (interpolación lineal de la varianza total, lección 3.4.5) y se mide la pendiente:

IN [12]python
ver = pd.read_parquet("opciones3_verdad.parquet")
ver["T"] = ver["dias"] / 365
diaria = (ver.sort_values(["fecha", "vencimiento"])
          .groupby("fecha").head(3))

def atm_cte(g, dias):
    t, w = g["T"].to_numpy(), g["theta"].to_numpy()
    tt = dias / 365
    if t[0] >= tt:
        return np.sqrt(w[0] / t[0])
    b = min(np.searchsorted(t, tt), len(t) - 1)
    wi = w[b - 1] + (w[b] - w[b - 1]) * (tt - t[b - 1]) \
        / (t[b] - t[b - 1])
    return np.sqrt(wi / tt)

ts = diaria.groupby("fecha").apply(
    lambda g: pd.Series({"atm30": atm_cte(g, 30),
                         "atm60": atm_cte(g, 60)})) * 100
ts["pendiente"] = ts["atm60"] - ts["atm30"]
ts["vorx"] = vx.reindex(ts.index)
print(f"invertida (30 d > 60 d): {(ts.pendiente < 0).mean():.0%}"
      f" de las sesiones")
print(f"correlación de la pendiente con el VORX: "
      f"{ts['pendiente'].corr(ts['vorx']):.2f}")
ts["pendiente"].describe().round(2)
OUT [12]Pendiente 30-60 días y nivel de la volatilidad
invertida (30 d > 60 d): 28% de las sesiones
correlación de la pendiente con el VORX: -0.67
count    3018.00
mean        0.38
std         1.21
min        -8.26
25%        -0.09
50%         0.56
75%         1.09
max         3.55
Name: pendiente, dtype: float64
Fig. — VORX (arriba) y pendiente de la estructura temporal ATM, implícita a 60 días menos a 30 (abajo), cada dos semanas de 2014 a 2025: la pendiente se invierte en las crisis (datos sintéticos)

La pendiente es positiva casi siempre que la volatilidad está baja (la implícita a 60 días por encima de la de 30: la estructura en contango) y se invierte en las crisis, con una correlación de −0,67 con el VORX. Está invertida en el 28 % de las sesiones, en línea con el 27 % de los miércoles que el README mide entre 1 mes y 1 año. Es la respuesta conocida del modelo: la varianza a corto plazo vuelve deprisa a una media lenta, así que tras un choque la implícita corta queda muy por encima de la larga.

Para el skew, la superficie da la implícita a cualquier k=ln⁡(K/F)k = \ln(K/F) con los parámetros SVI. A unos 30 días, la diferencia entre la implícita a un 5 % por debajo y a un 5 % por encima del forward, y la correlación entre el rendimiento diario del índice y el cambio de la implícita ATM a 30 días (la correlación spot-vol, lo que el skew descuenta):

IN [13]python
def iv_svi(g, k):
    w = g["svi_a"] + g["svi_b"] * (
        g["svi_rho"] * (k - g["svi_m"])
        + np.sqrt((k - g["svi_m"])**2 + g["svi_sigma"]**2))
    return np.sqrt(w / g["T"])

c30 = (ver[ver["dias"].between(20, 40)].drop_duplicates("fecha")
       .set_index("fecha"))
skew = 100 * (iv_svi(c30, -0.05) - iv_svi(c30, 0.05))
print(f"skew −5 % / +5 %: media {skew.mean():.1f} puntos, "
      f"de {skew.min():.1f} a {skew.max():.1f}")
print(f"correlación del skew con el VORX: "
      f"{skew.corr(vx.reindex(skew.index)):.2f}")
rend = np.log(S).diff()
print(f"correlación rendimiento / cambio de la ATM a 30 días: "
      f"{rend.corr(ts['atm30'].diff()):.2f}")
OUT [13]Skew a 30 días y correlación spot-vol
skew −5 % / +5 %: media 9.1 puntos, de 6.8 a 11.9
correlación del skew con el VORX: 0.54
correlación rendimiento / cambio de la ATM a 30 días: -0.62

La put a un 5 % por debajo del forward cotiza de media 9,1 puntos de volatilidad por encima de la call a un 5 % por encima, y la diferencia crece con el nivel de la volatilidad (correlación de 0,54 con el VORX). La correlación spot-vol medida es de −0,62: cuando el índice cae, la implícita sube. El modelo la planta con una correlación de −0,72 entre los choques de la varianza y los rendimientos; la implícita ATM es una media suavizada de la varianza futura y recoge una parte menor.

Con estos datos se pueden contrastar las dos operaciones, siempre cubiertas en delta: un calendario vendido cuando la estructura está muy invertida (apuesta a que se normaliza) o un risk reversal cuando el skew se separa de su relación habitual con el nivel. Dos advertencias antes de hacerlo. La primera, que los datos tienen solo tres vencimientos diarios y la superficie completa los miércoles: un calendario de más de tres meses solo se puede valorar una vez por semana. La segunda, que las dos operaciones llevan vega de signo contrario en cada pata y se mueven con el nivel de la volatilidad: un calendario vendido «porque la estructura está invertida» se abre justo en la crisis, cuando el nivel puede seguir subiendo, y pierde por la pata corta lo que no gana la larga.

Dispersión: lo que estos datos no permiten

La varianza de un índice depende de las varianzas de sus componentes y de sus correlaciones. Con pesos wiw_i y volatilidades implícitas σi\sigma_i de los componentes, y una sola correlación media ρ‾\overline{\rho}:

σI2=∑iwi2σi2+ρ‾∑i≠jwiwjσiσj⟹ρ‾imp=σI2−∑iwi2σi2∑i≠jwiwjσiσj.\sigma_I^2 = \sum_i w_i^2 \sigma_i^2 + \overline{\rho} \sum_{i \neq j} w_i w_j \sigma_i \sigma_j \quad\Longrightarrow\quad \overline{\rho}_{imp} = \frac{\sigma_I^2 - \sum_i w_i^2 \sigma_i^2}{\sum_{i \neq j} w_i w_j \sigma_i \sigma_j} .

Es la correlación implícita. Cboe publica índices de correlación implícita del S&P 500 con esta fórmula, con las implícitas ATM del índice y de sus 50 mayores componentes por capitalización ajustada por free float (libro blanco de la metodología en los recursos). Una operación de dispersión vende volatilidad del índice y compra la de los componentes, todo cubierto en delta: gana si la correlación realizada queda por debajo de la implícita. Driessen, Maenhout y Vilkov (2009) estudian opciones del S&P 100 y de todos sus componentes y encuentran que el riesgo de correlación tiene precio: explica bien los rendimientos de las opciones del índice frente a las de las acciones. Según su resumen, con fricciones de negociación realistas esa prima no se puede explotar.

bajaaltaVende vol índiceCartera cubiertaCompra vol acciónCorrelación realGanaPierde: crisis
Fuente Mermaid
flowchart TB
  I[Vende vol índice] --> C[Cartera cubierta]
  A[Compra vol acción] --> C
  C --> R{Correlación real}
  R -- baja --> G[Gana]
  R -- alta --> P[Pierde: crisis]

Los datos del curso no permiten contrastarla: hay opciones del índice ORX, pero no de sus componentes (la acción KRN, la única con opciones, no forma parte del índice ni se generó a partir de él). Lo que sí se puede hacer es calcular la correlación implícita como fórmula. Los números de la celda siguiente son inventados, solo para ver cómo se mueve:

IN [14]python
def correlacion_implicita(sig_indice, w, sig):
    w, sig = np.asarray(w), np.asarray(sig)
    sin_corr = np.sum(w**2 * sig**2)
    cruzados = np.sum(np.outer(w * sig, w * sig)) - sin_corr
    return (sig_indice**2 - sin_corr) / cruzados

w = [0.30, 0.25, 0.20, 0.15, 0.10]    # pesos (inventados)
sig = [0.28, 0.32, 0.25, 0.35, 0.40]  # implícitas de las acciones
for sig_i in (0.15, 0.20, 0.25):
    rho = correlacion_implicita(sig_i, w, sig)
    print(f"índice al {sig_i:.0%}: correlación implícita {rho:.2f}")
OUT [14]Correlación implícita con una cesta inventada
índice al 15%: correlación implícita 0.03
índice al 20%: correlación implícita 0.27
índice al 25%: correlación implícita 0.57

Con las acciones al 25-40 %, un índice al 15 % implica una correlación media de 0,03; al 20 %, de 0,27, y al 25 %, de 0,57: subir la implícita del índice sin que se muevan las de las acciones es subir la correlación. En una crisis las correlaciones reales se disparan hacia 1, que es justo cuando el vendedor de volatilidad del índice pierde: la dispersión es otra forma de vender prima con riesgo de cola, y los costes (decenas de opciones sobre acciones, cada una con su horquilla) se comen buena parte de la prima.

Ejercicios

  1. En la simulación, haz que la volatilidad realizada sea del 30 % con la call vendida al 20 %. Compara cubrir con la delta de la implícita (20 %) y con la de la realizada (30 %) con 160 coberturas: ¿qué pasa con la media y con la dispersión?
Solución
IN [15]python
z2 = np.random.default_rng(7).standard_normal((n_tray, pasos))
s_r = 0.30
cam2 = S0 * np.exp(np.cumsum(-0.5 * s_r**2 * dtp
                             + s_r * np.sqrt(dtp) * z2, axis=1))
cam2 = np.c_[np.full(n_tray, S0), cam2]
t_ = T0 - np.arange(pasos + 1) * dtp

def delta_con(s, t, sv):
    sd = sv * np.sqrt(np.maximum(t, 1e-12))
    return ndtr((np.log(s / K0) + 0.5 * sd * sd) / sd)

for nombre, sv in (("implícita 20 %", 0.20), ("realizada 30 %", 0.30)):
    d_ = delta_con(cam2[:, :-1], t_[:-1], sv)
    pnl = (prima + (d_ * np.diff(cam2, axis=1)).sum(axis=1)
           - np.maximum(cam2[:, -1] - K0, 0))
    print(f"{nombre}: media {pnl.mean():.3f}, desv {pnl.std():.3f}")
OUT [15]Cubrir con la implícita o con la realizada
implícita 20 %: media -1.081, desv 0.546
realizada 30 %: media -1.099, desv 0.224

Las dos medias son parecidas y negativas (−1,08 y −1,10 puntos sobre una prima de 2,21): quien vendió al 20 % una volatilidad que fue del 30 % pierde, aproximadamente, la diferencia entre el precio de la call al 30 % y al 20 %. Lo que cambia es la dispersión. Cubriendo con la delta de la realizada el resultado es mucho menos disperso (0,22 frente a 0,55; lo que queda es el error de cubrir 160 veces y no en continuo), porque la cobertura replica la opción al 30 %; cubriendo con la de la implícita depende del camino, porque la pérdida diaria se pondera con la gamma de la implícita. Como la realizada no se conoce de antemano, en la práctica se cubre con la implícita y se acepta esa dependencia del camino.

  1. Repite la descomposición del P&L solo con las operaciones que entran con la estructura temporal invertida (pendiente 30-60 días negativa el día de entrada). ¿Gana más o menos el comprador que en el conjunto?
Solución
IN [16]python
e["pend"] = ts["pendiente"].reindex(
    pd.to_datetime(e["entrada"])).to_numpy()
e.groupby(e["pend"] < 0)["pnl"].agg(
    ["count", "mean", "median",
     ("positivas", lambda s: (s > 0).mean())]).round(2)
OUT [16]El comprador con la estructura invertida al entrar
       count  mean  median  positivas
pend
False    102 -0.44   -0.75       0.25
True      41 -0.48   -0.84       0.32

Con la estructura invertida al entrar (True, 41 operaciones), el comprador está en una crisis o justo después: compra una implícita corta muy alta. La media (−0,48 %) es casi la del resto (−0,44 %) y gana algo más a menudo (32 % frente a 25 %): el resultado depende de si la crisis sigue o se calma. Con 41 operaciones y la desviación típica de toda la muestra (1,66 puntos), el error típico de la media es de 1,66 / √41 ≈ 0,26 puntos: la diferencia de medias no se distingue del azar. Es el mismo problema que la estacionalidad de la lección 3.2.6: pocas observaciones independientes por estado.

  1. Una mesa propone vender la volatilidad del índice y comprar la de sus diez mayores componentes porque la correlación implícita está en 0,60 y la realizada del último año fue 0,35. Enumera lo que puede salir mal.
Solución

La realizada del último año no es la de los próximos meses: la correlación sube en las caídas, y la pata vendida del índice pierde justo cuando sube. Con diez componentes la cesta no replica el índice (el resto de valores también aporta varianza), así que queda un riesgo de base. Cada pata necesita su cobertura delta y paga su horquilla, y las opciones sobre acciones tienen horquillas mucho más anchas que las del índice. Los resultados empresariales y los dividendos de cada acción mueven sus implícitas por separado (lección 3.6.4). Driessen, Maenhout y Vilkov (2009) concluyen que, con fricciones realistas, la prima por el riesgo de correlación no se puede explotar: los costes de las decenas de patas se la comen. Y quien la cobra asume pérdidas grandes en las crisis: hay que dimensionarla con su peor episodio, no con la media.

Respuesta plegada en cada pregunta

Autoevaluación

  1. Un straddle ATM comprado y cubierto en delta cada día gana dinero cuando...

    • AEl índice sube durante la vida de la opción
    • BLa volatilidad realizada, ponderada por la gamma de cada día, supera a la implícita con la que se compró
    • CLa volatilidad implícita baja después de la compra
    • DEl tipo de interés sube
    Ver respuesta

    Respuesta B. La volatilidad realizada, ponderada por la gamma de cada día, supera a la implícita con la que se compró

    Cada día el P&L de gamma es ½·Γ·S²·(r² − σ²·Δt): positivo si el movimiento al cuadrado supera al que descontaba la implícita. La dirección del índice la neutraliza la cobertura. En los datos, el término de gamma explica por sí solo una correlación de 0,89 con el P&L real de las 143 operaciones.

  2. Dos straddles con la misma volatilidad realizada en el mes tienen P&L distinto. ¿Por qué?

    • APor un error de los datos
    • BPorque la gamma no es constante: el mismo movimiento cerca del strike y del vencimiento pesa mucho más que lejos del strike o al principio
    • CPorque uno se compró al bid y otro al ask
    • DNo puede ocurrir si la cobertura es diaria
    Ver respuesta

    Respuesta B. Porque la gamma no es constante: el mismo movimiento cerca del strike y del vencimiento pesa mucho más que lejos del strike o al principio

    El P&L suma los movimientos al cuadrado ponderados por Γ·S². La gamma es máxima en el dinero y crece al acercarse el vencimiento. Por eso la correlación del P&L con la diferencia de varianzas del mes (0,81) es menor que con el término de gamma día a día (0,89).

  3. En la simulación con σ realizada = σ implícita, ¿qué cambia al pasar de 5 a 80 coberturas por opción?

    • ALa media del P&L pasa de negativa a positiva
    • BLa media sigue cerca de cero y la desviación típica cae aproximadamente con 1/√N
    • CNada: la cobertura discreta es igual que la continua
    • DLa desviación típica sube porque hay más operaciones
    Ver respuesta

    Respuesta B. La media sigue cerca de cero y la desviación típica cae aproximadamente con 1/√N

    Con la volatilidad bien estimada, cubrir no crea ni destruye valor esperado: reduce el ruido. Multiplicar por 16 las coberturas divide por unas 4 la desviación típica (0,81 frente a 0,21 en la simulación de la lección).

  4. En los datos, cubrir cada 5 sesiones en vez de cada día deja el P&L medio del straddle comprado en −0,17 % en lugar de −0,45 %. ¿Qué significa?

    • AQue cubrir menos es una estrategia mejor
    • BQue la cobertura menos frecuente deja exposición a la dirección, y en una muestra con el índice al alza esa exposición ha sumado; a cambio, la desviación típica casi se duplica
    • CQue la cobertura diaria tiene un error de programación
    • DQue la prima de riesgo de varianza desaparece al cubrir menos
    Ver respuesta

    Respuesta B. Que la cobertura menos frecuente deja exposición a la dirección, y en una muestra con el índice al alza esa exposición ha sumado; a cambio, la desviación típica casi se duplica

    La diferencia de medias es deriva direccional, no ventaja de la cobertura: la desviación típica pasa de 1,66 a 3,06 puntos. Una banda de delta de 0,2 consigue casi lo mismo que la cobertura diaria (1,83) con 5 coberturas en lugar de 20.

  5. ¿Qué hace falta para contrastar una operación de dispersión?

    • ASolo las opciones del índice
    • BLas opciones del índice y las de sus componentes principales, con sus pesos, para comparar la varianza implícita del índice con la de la cesta y obtener la correlación implícita
    • CEl VIX y el VSTOXX
    • DUn año de datos diarios del índice
    Ver respuesta

    Respuesta B. Las opciones del índice y las de sus componentes principales, con sus pesos, para comparar la varianza implícita del índice con la de la cesta y obtener la correlación implícita

    La correlación implícita sale de la varianza implícita del índice frente a la de una cesta sin correlación de sus componentes. Los datos del curso solo tienen opciones del índice ORX y de una acción que no forma parte de él: la operación se puede plantear, pero no contrastar.

Ideas clave

  • Con la delta cubierta, el P&L diario de una opción es ½·Γ·S²·(r² − σ²·Δt) más vega por el cambio de la implícita y un resto pequeño: en los datos, gamma y vega juntos dan una correlación de 0,97 con el P&L real.
  • El straddle ATM comprado y cubierto pierde de media un 0,45 % del índice por operación y gana solo en el 23 % de los meses (la prima de riesgo de varianza), pero gana un 11,7 % al entrar el 2020-02-21: es un seguro, no una renta.
  • Cubrir más a menudo no cambia el valor esperado; reduce la dispersión del resultado con 1/√N y cuesta más horquilla. Las bandas de delta son más eficientes que el calendario fijo.
  • La estructura temporal se invierte en el 28 % de las sesiones (30 frente a 60 días) y el skew sube con el nivel de la volatilidad: las dos son operables con opciones del índice, siempre con la cobertura delta y con el riesgo de cola a la vista.
  • La dispersión necesita opciones de los componentes; con estos datos solo se puede calcular la correlación implícita como fórmula, no backtestearla.

Recursos