Lección 3.4.3 · 70 min

Griegas: delta, gamma, vega, theta, rho, vanna, volga y charm

Las griegas son las derivadas del precio de una opción respecto a lo que lo mueve. Esta lección las calcula de tres formas (fórmula, diferencias finitas y QuantLib), deja claras sus unidades, que es donde se equivoca casi todo el mundo, y las usa para explicar, término a término, lo que ganó una put de ORX el día que el índice cayó un 4,9 %.

Fig. — Gamma de una call de ORX de strike 5.150 según el índice, a 7, 30 y 90 días del vencimiento (volatilidad del 21,7 %)
03/07
Módulo
3.4 · Teoría y valoración de opciones
Duración
70 min
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é mide cada griega

Una opción cambia de valor porque se mueven el subyacente, la volatilidad, el tiempo y el tipo de interés. Las griegas son las derivadas parciales del precio respecto a cada uno: dicen cuánto cambia el precio por unidad de cambio de cada factor, con los demás quietos. Son la herramienta con la que se cubre una cartera (lección 3.6.2), se mide su riesgo y se explica su P&L. Las de primer orden son derivadas primeras; las de segundo orden miden cuánto cambian las primeras.

Precio VDeltaVegaThetaRhoGammaVannaCharmVolga
Fuente Mermaid
flowchart TB
  V[Precio V] --> D[Delta]
  V --> VE[Vega]
  V --> TH[Theta]
  V --> RH[Rho]
  D --> G[Gamma]
  D --> VA[Vanna]
  D --> CH[Charm]
  VE --> VO[Volga]

La vanna está debajo de la delta y podría estar debajo de la vega: es la derivada cruzada, y las dos lecturas son la misma. La tabla fija las unidades que se usan en todo el curso. Casi todos los errores con griegas son de unidades, y cada biblioteca tiene las suyas:

GriegaDerivadaUnidad operativa (curso)Lectura
Delta Δ\Delta∂V/∂S\partial V/\partial Spuntos de prima por punto de subyacenteequivalente en subyacente; ×100 = acciones o «delta en dólares» / S
Gamma Γ\Gamma∂2V/∂S2\partial^2 V/\partial S^2cambio de delta por puntocuánto hay que reajustar la cobertura
Vega ν\nu∂V/∂σ\partial V/\partial\sigmapuntos por punto de volatilidad (0,01)ganancia si la volatilidad sube un punto
Theta Θ\Theta∂V/∂t\partial V/\partial tpuntos por día naturalvalor temporal que se consume en un día
Rho ρ\rho∂V/∂r\partial V/\partial rpuntos por punto porcentual de tipoganancia si el tipo sube del 4 al 5 %
Vanna∂2V/∂S ∂σ\partial^2 V/\partial S\,\partial\sigmacambio de delta por punto de volatilidaddelta que cambia cuando se mueve la volatilidad
Volga∂2V/∂σ2\partial^2 V/\partial\sigma^2cambio de vega por punto de volatilidadconvexidad en volatilidad
Charm∂Δ/∂t\partial \Delta/\partial tcambio de delta por día naturalderiva de la delta con el paso del tiempo

Los precios de ORX son por punto de índice con un multiplicador de 100 USD: una delta de 0,52 en un contrato equivale a 52 «índices» y, con el índice a 5.148, a 268.000 USD de exposición; una theta de −2,32 son 232 USD por contrato y día.

Fórmulas cerradas en Black-Scholes-Merton

Con las mismas variables de la lección 3.4.2 (d1d_1, d2d_2, qq continuo) y n(⋅)n(\cdot) la densidad normal estándar, las de primer orden de una call son:

Δ=e−qTN(d1),Γ=e−qTn(d1)SσT,ν=Se−qTn(d1)T,\Delta = e^{-qT} N(d_1), \qquad \Gamma = \frac{e^{-qT} n(d_1)}{S\sigma\sqrt{T}}, \qquad \nu = S e^{-qT} n(d_1)\sqrt{T}, Θ=−Se−qTn(d1) σ2T−rKe−rTN(d2)+qSe−qTN(d1),ρ=KTe−rTN(d2),\Theta = -\frac{S e^{-qT} n(d_1)\,\sigma}{2\sqrt{T}} - rKe^{-rT}N(d_2) + qSe^{-qT}N(d_1), \qquad \rho = KTe^{-rT}N(d_2),

y las de segundo orden, iguales para call y put salvo el charm:

vanna=−e−qTn(d1)d2σ,volga=ν d1d2σ,charm=qe−qTN(d1)−e−qTn(d1) 2(r−q)T−d2σT2TσT.\text{vanna} = -e^{-qT} n(d_1)\frac{d_2}{\sigma}, \qquad \text{volga} = \nu\,\frac{d_1 d_2}{\sigma}, \qquad \text{charm} = q e^{-qT}N(d_1) - e^{-qT} n(d_1)\,\frac{2(r-q)T - d_2\sigma\sqrt{T}}{2T\sigma\sqrt{T}}.

Gamma, vega, vanna y volga son iguales para la call y la put del mismo strike, porque la diferencia entre las dos es un forward (paridad, lección 3.4.1), lineal en SS e independiente de σ\sigma. La función devuelve todas ya en las unidades de la tabla, para una call o una put:

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

def griegas(S, K, T, r, q, sig, call=True):
    sq = np.sqrt(T)
    sd = sig * sq
    d1 = (np.log(S / K) + (r - q) * T) / sd + sd / 2
    d2 = d1 - sd
    eq, er, n1 = np.exp(-q * T), np.exp(-r * T), norm.pdf(d1)
    w = 1 if call else -1                 # signo de call o put
    precio = w * (S * eq * norm.cdf(w * d1)
                  - K * er * norm.cdf(w * d2))
    delta = w * eq * norm.cdf(w * d1)
    theta = (-S * eq * n1 * sig / (2 * sq)
             - w * r * K * er * norm.cdf(w * d2)
             + w * q * S * eq * norm.cdf(w * d1))
    rho = w * K * T * er * norm.cdf(w * d2)
    charm = (w * q * eq * norm.cdf(w * d1)
             - eq * n1 * (2 * (r - q) * T - d2 * sd) / (2 * T * sd))
    vega = S * eq * n1 * sq
    return {"precio": precio, "delta": delta,
            "gamma": eq * n1 / (S * sd),
            "vega": vega / 100,               # por punto de vol.
            "theta": theta / 365,             # por día natural
            "rho": rho / 100,                 # por punto de tipo
            "vanna": -eq * n1 * d2 / sig / 100,
            "volga": vega * d1 * d2 / sig / 100**2,
            "charm": charm / 365}

# la call de ORX de la lección 3.4.2
S, K, T, r, q, sig = 5148.06, 5150.0, 30 / 365, 0.04191, 0.013438, 0.217
call = griegas(S, K, T, r, q, sig)
put = griegas(S, K, T, r, q, sig, call=False)
pd.DataFrame({"call": call, "put": put}).map("{:.6g}".format)
OUT [01]Griegas analíticas, en unidades operativas
                call           put
precio        132.59       122.503
delta       0.524404     -0.474492
gamma     0.00124182    0.00124182
vega         5.86996       5.86996
theta       -2.31833      -1.91836
rho          2.10992       -2.1084
vanna   -8.31677e-06  -8.31677e-06
volga    7.69206e-06   7.69206e-06
charm   -0.000476373  -0.000513149

La call en el dinero a 30 días vale 132,59 puntos con una delta de 0,524 y la put, una de −0,474: la diferencia es la delta del forward sintético, e−qTe^{-qT} = 0,9989. Gamma, vega, vanna y volga coinciden en las dos. Vanna y volga son casi nulas en el dinero (d2≈0d_2 \approx 0); se ven en las alas, más abajo. La rho de la call es positiva (un tipo más alto encarece el forward) y la de la put, negativa.

Tres formas de calcularlas: fórmula, diferencias finitas y bibliotecas

Las fórmulas se pueden equivocar al copiarlas; las diferencias finitas, no. Una diferencia central aproxima la derivada con dos precios alrededor del punto:

Δ≈V(S+h)−V(S−h)2h,Γ≈V(S+h)−2V(S)+V(S−h)h2.\Delta \approx \frac{V(S+h) - V(S-h)}{2h}, \qquad \Gamma \approx \frac{V(S+h) - 2V(S) + V(S-h)}{h^2}.

La celda compara las fórmulas con diferencias finitas sobre la propia función de precio, con py_vollib (paquete vollib, lección 3.4.2) y con QuantLib, imprimiendo cada biblioteca en sus unidades de origen:

IN [02]python
import QuantLib as ql
from vollib.black_scholes_merton.greeks import analytical as vl

def precio(S=S, T=T, r=r, sig=sig):
    return griegas(S, K, T, r, q, sig)["precio"]

def delta_de(S=S, T=T, sig=sig):
    return griegas(S, K, T, r, q, sig)["delta"]

h, hs, hr = 1.0, 1e-4, 1e-4
dif = {
    "delta": (precio(S + h) - precio(S - h)) / (2 * h),
    "gamma": (precio(S + h) - 2 * precio() + precio(S - h)) / h**2,
    "vega": (precio(sig=sig + hs) - precio(sig=sig - hs))
            / (2 * hs) / 100,
    "theta": (precio(T=T - 1 / 365) - precio()),      # un día
    "rho": (precio(r=r + hr) - precio(r=r - hr)) / (2 * hr) / 100,
    "vanna": (delta_de(sig=sig + hs) - delta_de(sig=sig - hs))
             / (2 * hs) / 100,
    "charm": delta_de(T=T - 1 / 365) - delta_de(),     # un día
}
F = S * math.exp((r - q) * T)
bc = ql.BlackCalculator(ql.PlainVanillaPayoff(ql.Option.Call, K),
                        F, sig * math.sqrt(T), math.exp(-r * T))
origen = {
    "delta": (vl.delta("c", S, K, T, r, sig, q), bc.delta(S)),
    "gamma": (vl.gamma("c", S, K, T, r, sig, q), bc.gamma(S)),
    "vega": (vl.vega("c", S, K, T, r, sig, q), bc.vega(T)),
    "theta": (vl.theta("c", S, K, T, r, sig, q), bc.theta(S, T)),
    "rho": (vl.rho("c", S, K, T, r, sig, q), bc.rho(T)),
    "vanna": (np.nan, bc.vanna(S, T)),
    "charm": (np.nan, np.nan),
}
pd.DataFrame({"fórmula": {k: call[k] for k in dif},
              "dif. finitas": dif,
              "vollib": {k: v[0] for k, v in origen.items()},
              "QuantLib (origen)": {k: v[1]
                                    for k, v in origen.items()}}
             ).map("{:.6g}".format)
OUT [02]Fórmula, diferencias finitas, py_vollib y QuantLib
            fórmula  dif. finitas      vollib QuantLib (origen)
delta      0.524404      0.524404    0.524404          0.524404
gamma    0.00124182    0.00124182  0.00124182        0.00124182
vega        5.86996       5.86996     5.86996           586.996
theta      -2.31833      -2.33649    -2.31833          -846.192
rho         2.10992       2.10992     2.10992           210.992
vanna  -8.31677e-06   -8.3169e-06         nan      -0.000831677
charm  -0.000476373  -0.000481304         nan               nan

Fórmula y diferencias finitas coinciden en todas las griegas instantáneas. Theta y charm por diferencia de un día son algo mayores en valor absoluto (−2,336 frente a −2,318 por día): la theta de la fórmula es una tasa instantánea, y el valor temporal se consume cada vez más deprisa al acercarse el vencimiento. py_vollib da ya las unidades operativas: vega y rho divididas por 100 y theta por 365. QuantLib da la vega (587,0) y la rho (210,99) por unidad, es decir, por un salto de 100 puntos de volatilidad o de tipo, y la theta por año (−846,19); para la theta diaria tiene thetaPerDay. Ninguna está mal: miden lo mismo en otras unidades, y un sistema que mezcle dos bibliotecas sin convertir las unidades puede cubrir cien veces de más o de menos.

Con la volga hay una sorpresa. QuantLib 1.44 tiene BlackCalculator.volga(T), pero su valor no coincide con la derivada segunda respecto a σ salvo con T=1T = 1:

IN [03]python
filas = []
for Tx in [0.25, 1.0, 4.0]:
    Fx = S * math.exp((r - q) * Tx)
    v = lambda s: ql.BlackCalculator(
        ql.PlainVanillaPayoff(ql.Option.Call, 5000.0), Fx,
        s * math.sqrt(Tx), math.exp(-r * Tx)).value()
    bcx = ql.BlackCalculator(ql.PlainVanillaPayoff(ql.Option.Call,
                                                   5000.0),
                             Fx, sig * math.sqrt(Tx),
                             math.exp(-r * Tx))
    fd = (v(sig + 1e-3) - 2 * v(sig) + v(sig - 1e-3)) / 1e-6
    filas.append({"T": Tx, "QuantLib volga": bcx.volga(Tx),
                  "dif. finitas": fd,
                  "cociente": bcx.volga(Tx) / fd,
                  "1/sqrt(T)": 1 / math.sqrt(Tx)})
pd.DataFrame(filas).round(4)
OUT [03]La volga de QuantLib: comprobarla con diferencias finitas
      T  QuantLib volga  dif. finitas  cociente  1/sqrt(T)
0  0.25        953.2139      476.6165       2.0        2.0
1  1.00        512.1042      512.1166       1.0        1.0
2  4.00        475.6863      951.4064       0.5        0.5

El cociente es exactamente 1/T1/\sqrt{T}: la «volga» de BlackCalculator es la derivada de la vega respecto a la desviación típica total σT\sigma\sqrt{T}, no respecto a σ\sigma. Para usarla como volga hay que multiplicarla por T\sqrt{T}. No es un error del método sino de convención (o de documentación), y solo se ve comprobándolo. Esa es la regla de la lección: ninguna griega de una biblioteca se usa sin contrastarla una vez con diferencias finitas.

El paso de la diferencia finita

Un paso hh grande da un error de truncamiento (la diferencia central yerra en h2h^2 por la derivada siguiente); uno pequeño da un error de redondeo: con precisión doble (ε≈2,2⋅10−16\varepsilon \approx 2{,}2\cdot 10^{-16}), restar dos precios casi iguales de 132 puntos y dividir por h2h^2 amplifica ese épsilon. Para gamma el error total es del orden de c1h2+c2 εV/h2c_1 h^2 + c_2\,\varepsilon V/h^2, mínimo con h∝ε1/4h \propto \varepsilon^{1/4}, del orden de una diezmilésima del subyacente (unos 0,6 puntos con ORX a 5.148):

IN [04]python
pasos = 10.0 ** np.arange(-4, 2.75, 0.25)
gamma_fd = np.array([(precio(S + x) - 2 * precio() + precio(S - x))
                     / x**2 for x in pasos])
err_rel = np.abs(gamma_fd / call["gamma"] - 1)
mejor = pasos[err_rel.argmin()]
print(f"Paso con menor error: {mejor:.2f} puntos "
      f"(error relativo {err_rel.min():.1e})")
for x in [1e-4, 1e-3, 1e-2, 1.0, 100.0]:
    i = np.argmin(abs(pasos - x))
    print(f"h = {x:g}: error relativo {err_rel[i]:.1e}")
OUT [04]Error de gamma según el paso
Paso con menor error: 0.06 puntos (error relativo 1.1e-08)
h = 0.0001: error relativo 1.1e-02
h = 0.001: error relativo 2.9e-04
h = 0.01: error relativo 3.8e-06
h = 1: error relativo 7.9e-07
h = 100: error relativo 7.9e-03
Fig. — Error relativo de la gamma calculada con una diferencia central según el paso, call de ORX en el dinero a 30 días

Con pasos de una diezmilésima de punto la gamma numérica es basura (un error del 1,1 %); entre 0,01 y unos pocos puntos, el error es de millonésimas o menos (el mínimo medido, en 0,06 puntos, es en parte suerte del redondeo); con 100 puntos vuelve a crecer por truncamiento. La figura dibuja la V completa. Para la vega y la rho el compromiso es el mismo con otra escala: 10−410^{-4} en volatilidad o tipo está cerca del óptimo.

Diferencias finitas con QuantLib: mover las cotizaciones

Para lo que una biblioteca no da (vanna, volga, charm en QuantLib con un motor cualquiera) se mueve la entrada y se revalora. En QuantLib las entradas son cotizaciones observables (SimpleQuote): al cambiar su valor, todo lo que depende de ellas se recalcula solo. Para el charm, se mueve la fecha de valoración un día con curvas que se desplazan con ella (las que se crean con settlementDays = 0):

IN [05]python
hoy = ql.Date(18, 6, 2025)
ql.Settings.instance().evaluationDate = hoy
dc, cal = ql.Actual365Fixed(), ql.NullCalendar()
q_s, q_v = ql.SimpleQuote(S), ql.SimpleQuote(sig)

def plana(x):
    return ql.YieldTermStructureHandle(ql.FlatForward(
        0, cal, ql.QuoteHandle(ql.SimpleQuote(x)), dc))

proceso = ql.BlackScholesMertonProcess(
    ql.QuoteHandle(q_s), plana(q), plana(r),
    ql.BlackVolTermStructureHandle(
        ql.BlackConstantVol(0, cal, ql.QuoteHandle(q_v), dc)))
opcion = ql.VanillaOption(ql.PlainVanillaPayoff(ql.Option.Call, K),
                          ql.EuropeanExercise(hoy + 30))
opcion.setPricingEngine(ql.AnalyticEuropeanEngine(proceso))

def con(quote, valor, f):
    viejo = quote.value()
    quote.setValue(valor)
    x = f()
    quote.setValue(viejo)
    return x

vanna = (con(q_v, sig + hs, opcion.delta)
         - con(q_v, sig - hs, opcion.delta)) / (2 * hs) / 100
volga = (con(q_v, sig + hs, opcion.vega)
         - con(q_v, sig - hs, opcion.vega)) / (2 * hs) / 100**2
d_hoy = opcion.delta()
ql.Settings.instance().evaluationDate = hoy + 1
charm = opcion.delta() - d_hoy
ql.Settings.instance().evaluationDate = hoy
print(f"vanna {vanna:.3e} (fórmula {call['vanna']:.3e})")
print(f"volga {volga:.3e} (fórmula {call['volga']:.3e})")
print(f"charm a un día {charm:.3e} (fórmula {call['charm']:.3e})")
OUT [05]Bump and reprice con cotizaciones de QuantLib
vanna -8.317e-06 (fórmula -8.317e-06)
volga 7.692e-06 (fórmula 7.692e-06)
charm a un día -4.813e-04 (fórmula -4.764e-04)

Las tres coinciden con las fórmulas (el charm, con la diferencia de un día frente a la tasa instantánea ya vista en theta). Mover una cotización y revalorar es también la forma de calcular griegas en modelos sin fórmula: un árbol para una americana (lección 3.4.2) o Heston (lección 3.4.7).

Perfiles: dónde se concentra cada griega

La figura de la cabecera dibuja gamma en función del índice para tres plazos, con la volatilidad de la call de ORX: gamma es una campana centrada cerca del strike que se estrecha y crece al acercarse el vencimiento. En el dinero crece como 1/T1/\sqrt{T}: a 7 días es el doble que a 30 y casi cuatro veces la de 90. Theta sigue a gamma, porque la ecuación de Black-Scholes las ata. Para cualquier opción europea en ese modelo:

Θ+12σ2S2 Γ+(r−q)S Δ−rV=0.\Theta + \tfrac{1}{2}\sigma^2 S^2\,\Gamma + (r - q) S\,\Delta - rV = 0.

Quien está largo de gamma (gana con los movimientos grandes en cualquier dirección) paga theta cada día; quien la vende, la cobra. Es el intercambio que explotan el gamma scalping y la venta de prima (lecciones 3.6.1 y 3.6.2). La celda calcula las griegas en el dinero para los tres plazos y comprueba la ecuación con las unidades anuales:

IN [06]python
filas = []
for dias in [7, 30, 90]:
    g = griegas(S, K, dias / 365, r, q, sig)
    pde = (365 * g["theta"] + 0.5 * sig**2 * S**2 * g["gamma"]
           + (r - q) * S * g["delta"] - r * g["precio"])
    filas.append({"días": dias, "precio": g["precio"],
                  "gamma": g["gamma"], "theta": g["theta"],
                  "vega": g["vega"],
                  "theta/precio (%)": 100 * g["theta"] / g["precio"],
                  "ecuación BS": pde})
pd.DataFrame(filas).set_index("días").map("{:.4g}".format)
OUT [06]Griegas en el dinero según el plazo y la ecuación de Black-Scholes
     precio     gamma   theta   vega theta/precio (%) ecuación BS
días
7     62.13  0.002578  -4.603  2.843           -7.409  -2.078e-13
30    132.6  0.001242  -2.318   5.87           -1.749  -2.567e-13
90    237.1  0.000712  -1.409   10.1           -0.594  -1.492e-13

A 7 días la call vale 62,13 puntos y pierde 4,60 al día, un 7,4 % de su valor; a 90 días pierde 1,41 al día, un 0,59 %. La vega se comporta al revés que la gamma: crece con T\sqrt{T}, así que las opciones largas son las que más exponen a la volatilidad y las cortas las que más exponen al movimiento. La ecuación de Black-Scholes da cero en los tres plazos salvo el redondeo de la coma flotante.

Las griegas de una cadena: delta, probabilidad y alas

En una cadena real cada strike tiene su volatilidad implícita (la sonrisa, lección 3.4.5), y las griegas se calculan con la de su strike. La celda lo hace con el vencimiento de 30 días de ORX del 18 de junio de 2025 y la columna iv_modelo:

IN [07]python
cad = pd.read_parquet("opciones3_cadena.parquet")
c30 = cad[(cad["fecha"] == "2025-06-18")
          & (cad["vencimiento"] == "2025-07-18")
          & (cad["tipo"] == "C")].copy()
g = griegas(S, c30["strike"], T, r, q, c30["iv_modelo"])
for k in ["delta", "gamma", "vega", "vanna", "volga"]:
    c30[k] = g[k]
sd = c30["iv_modelo"] * math.sqrt(T)
d2 = (np.log(S / c30["strike"]) + (r - q) * T) / sd - sd / 2
c30["N(d2)"] = norm.cdf(d2)
c30["delta_usd"] = 100 * S * c30["delta"]   # por contrato
sel = c30["strike"].isin([4600.0, 4900.0, 5150.0, 5400.0, 5700.0])
c30.loc[sel, ["strike", "iv_modelo", "delta", "N(d2)", "delta_usd",
              "gamma", "vega", "vanna", "volga"]].round(5)
OUT [07]Griegas por strike en la cadena de ORX
        strike  iv_modelo    delta    N(d2)     delta_usd    gamma     vega    vanna    volga
519219  4600.0     0.3300  0.89551  0.87844  461011.41198  0.00037  2.65304 -0.00636  0.11841
519225  4900.0     0.2705  0.75899  0.73507  390734.43659  0.00078  4.58487 -0.00721  0.07515
519230  5150.0     0.2170  0.52440  0.50018  269966.39702  0.00124  5.86996 -0.00001  0.00001
519235  5400.0     0.1722  0.18500  0.17231   95237.01786  0.00105  3.93792  0.01464  0.19359
519240  5700.0     0.1615  0.01673  0.01492    8614.73892  0.00017  0.61380  0.00559  0.17552

Dos lecturas de la tabla:

  • Delta no es la probabilidad de ejercicio. La probabilidad neutral al riesgo de acabar dentro es N(d2)N(d_2), no N(d1)N(d_1): en la call de 5.400 la delta es 0,185 y N(d2)N(d_2), 0,172. La diferencia crece con la volatilidad y el plazo. Y las dos son probabilidades neutrales al riesgo, no previsiones (lección 3.4.2).
  • Vanna y volga viven en las alas. En el dinero son casi nulas; en la call de 4.600 (muy dentro) la vanna es −0,0064 y en la de 5.400 (fuera), +0,0146: subir la volatilidad baja la delta de las calls dentro del dinero y sube la de las de fuera. La volga es positiva en las alas: las opciones lejanas ganan con la volatilidad de forma convexa, y por eso el mercado las paga más caras (parte de la explicación de la sonrisa, lección 3.4.5).
Fig. — Vanna y volga de las calls de ORX por strike, vencimiento de 30 días, 18 de junio de 2025, con la volatilidad de cada strike

Explicar un P&L con las griegas

La utilidad diaria de las griegas es descomponer lo que ha ganado o perdido una posición. Con una expansión de Taylor del precio hasta el segundo orden:

ΔV≈Δ δS+12Γ δS2+ν δσ+Θ δt+ρ δr+vanna δS δσ+12volga δσ2+charm δt δS.\Delta V \approx \Delta\,\delta S + \tfrac{1}{2}\Gamma\,\delta S^2 + \nu\,\delta\sigma + \Theta\,\delta t + \rho\,\delta r + \text{vanna}\,\delta S\,\delta\sigma + \tfrac{1}{2}\text{volga}\,\delta\sigma^2 + \text{charm}\,\delta t\,\delta S.

El 3 de abril de 2025 ORX cayó un 4,9 % en un día (uno de los saltos plantados en los datos, como el del S&P 500 de ese día). Se toma la put en el dinero del vencimiento de mayo, valorada al cierre del 2 de abril y del 3 con Black-Scholes-Merton, el índice, la curva, el dividendo y la iv_modelo de su strike de cada día:

IN [08]python
ind = pd.read_csv("opciones3_indice.csv", parse_dates=["fecha"],
                  index_col="fecha")
plazos = np.array([1 / 12, 0.25, 0.5, 1.0, 2.0])
cols = ["tipo_1m", "tipo_3m", "tipo_6m", "tipo_1a", "tipo_2a"]

def estado(fecha, venc, strike, tipo):
    f, v = pd.Timestamp(fecha), pd.Timestamp(venc)
    Tf = (v - f).days / 365
    fila = cad[(cad["fecha"] == f) & (cad["vencimiento"] == v)
               & (cad["strike"] == strike)
               & (cad["tipo"] == tipo)].iloc[0]
    return {"S": float(ind.loc[f, "cierre"]), "T": Tf,
            "r": float(np.interp(Tf, plazos,
                                 ind.loc[f, cols].to_numpy(float)
                                 / 100)),
            "q": float(ind.loc[f, "rentabilidad_dividendo"]) / 100,
            "sig": float(fila["iv_modelo"]),
            "medio": (fila["bid"] + fila["ask"]) / 2}

def atribucion(f0, f1, venc, strike, tipo):
    a, b = estado(f0, venc, strike, tipo), estado(f1, venc, strike,
                                                  tipo)
    es_call = tipo == "C"
    g = griegas(a["S"], strike, a["T"], a["r"], a["q"], a["sig"],
                es_call)
    v1 = griegas(b["S"], strike, b["T"], b["r"], b["q"], b["sig"],
                 es_call)["precio"]
    dS, dsig = b["S"] - a["S"], 100 * (b["sig"] - a["sig"])
    dt = (pd.Timestamp(f1) - pd.Timestamp(f0)).days
    dr = 100 * (b["r"] - a["r"])
    partes = pd.Series({
        "delta": g["delta"] * dS, "gamma": 0.5 * g["gamma"] * dS**2,
        "vega": g["vega"] * dsig, "theta": g["theta"] * dt,
        "rho": g["rho"] * dr, "vanna": g["vanna"] * dS * dsig,
        "volga": 0.5 * g["volga"] * dsig**2,
        "charm": g["charm"] * dt * dS})
    partes["residuo"] = (v1 - g["precio"]) - partes.sum()
    partes["total (modelo)"] = v1 - g["precio"]
    partes["total (medios)"] = b["medio"] - a["medio"]
    print(f"S {a['S']} a {b['S']} ({dS / a['S']:+.2%}); vol. "
          f"{a['sig']} a {b['sig']}; {dt} día(s)")
    return partes.round(2)

atribucion("2025-04-02", "2025-04-03", "2025-05-16", 5200.0, "P")
OUT [08]Atribución del P&L de una put el 3 de abril de 2025
S 5211.08 a 4956.94 (-4.88%); vol. 0.2252 a 0.2314; 1 día(s)
delta             115.59
gamma              31.37
vega                4.44
theta              -1.62
rho                -0.01
vanna               0.09
volga               0.00
charm               0.07
residuo            -0.06
total (modelo)    149.87
total (medios)    150.05
dtype: float64

La put ganó 149,87 puntos en el modelo (150,05 con los precios medios de pantalla: 15.005 USD por contrato). La delta explica 115,59; la gamma, 31,37 más, porque con 254 puntos de caída la aproximación lineal se queda corta; la vega, 4,44 (la volatilidad de ese strike subió 0,62 puntos); la theta resta 1,62. Rho, vanna, volga y charm no llegan a 0,1. Queda sin explicar −0,06: los términos de tercer orden. La figura lo muestra en barras:

Fig. — Atribución del P&L de una put de ORX en el dinero el 3 de abril de 2025, día en que el índice cayó un 4,9 %

Ese día el reparto es el de libro: una posición corta en esa put con cobertura delta habría perdido la gamma (31 puntos) más la vega (4,4) y solo habría cobrado 1,6 de theta. Es el riesgo de la venta de opciones en una frase, y la razón por la que la lección 3.6.1 presenta la venta de prima con su peor episodio y no con su Sharpe.

Errores típicos

  • Unidades de biblioteca. Vega y rho de QuantLib por unidad, theta por año; py_vollib por punto y por día; la volga de BlackCalculator respecto a σT\sigma\sqrt{T}. Se convierten en un único sitio del código y se prueban.
  • Theta por día hábil o natural. Con TT en días naturales / 365, el fin de semana consume tres días de theta. Si el sistema usa días hábiles para TT, la volatilidad tiene que estar en la misma base.
  • Diferencias finitas con pasos ridículos. h=10−8h = 10^{-8} no es más preciso: es ruido. Para gamma, del orden de una diezmilésima del subyacente; para la vega, 10−410^{-4} en volatilidad.
  • Griegas con la volatilidad equivocada. Una griega se calcula con la volatilidad de su strike. Y cuando el índice se mueve, la volatilidad de cada strike también se mueve (en ORX subió al caer el índice): la delta «de la fórmula» supone el strike pegado a su volatilidad (sticky strike) y no incluye ese efecto. Se discute con la superficie en la lección 3.4.5.
  • Griegas en dólares sin el multiplicador. Una gamma de 0,00124 por punto parece nada, pero en un contrato de 100 USD sobre un índice a 5.148 la delta en dólares cambia unos 640 USD por punto (100 × 5.148 × 0,00124) y unos 33.000 USD con un movimiento del 1 %: la escala del contrato decide si una griega es pequeña.

Ejercicios

  1. Repite la atribución de la call y la put en el dinero del vencimiento de mayo entre el 8 y el 9 de abril de 2025, cuando ORX subió un 9,5 % y la volatilidad de esos strikes subió casi 9 puntos. ¿Cuánto queda sin explicar y por qué?
Solución
IN [09]python
pd.DataFrame({
    "call": atribucion("2025-04-08", "2025-04-09", "2025-05-16",
                       4800.0, "C"),
    "put": atribucion("2025-04-08", "2025-04-09", "2025-05-16",
                      4800.0, "P")})
OUT [09]Un día extremo: el 9 de abril de 2025
S 4820.64 a 5279.9 (+9.53%); vol. 0.3532 a 0.4403; 1 día(s)
S 4820.64 a 5279.9 (+9.53%); vol. 0.3532 a 0.4403; 1 día(s)
                  call     put
delta           251.29 -207.29
gamma            75.92   75.92
vega             53.58   53.58
theta            -3.04   -2.67
rho               0.01   -0.01
vanna            -0.29   -0.29
volga             0.01    0.01
charm            -0.10   -0.12
residuo         -19.26  -19.92
total (modelo)  358.11 -100.80
total (medios)  358.80 -101.15

Con un salto del 9,5 % y casi 9 puntos de volatilidad, el residuo es de unos −19 puntos en las dos opciones, un 5 % del cambio de la call. Los términos de tercer orden ya no son despreciables: la derivada de gamma respecto al subyacente (speed) multiplicada por δS3/6\delta S^3/6, y las derivadas cruzadas de gamma y de vega con la volatilidad. El reparto sigue siendo útil (delta, gamma y vega explican casi todo), pero un sistema de riesgo que solo use griegas en días así infravalora las pérdidas: para los escenarios extremos se revalora la posición entera (lección 3.5.4).

  1. Comprueba la ecuación de Black-Scholes en todas las calls y puts de 30 días de la cadena de la lección, cada una con su iv_modelo. ¿Se cumple con volatilidades distintas en cada strike?
Solución
IN [10]python
c30_all = cad[(cad["fecha"] == "2025-06-18")
              & (cad["vencimiento"] == "2025-07-18")]
max_err = 0.0
for tipo in ["C", "P"]:
    x = c30_all[c30_all["tipo"] == tipo]
    sx = x["iv_modelo"]
    gx = griegas(S, x["strike"], T, r, q, sx, call=tipo == "C")
    pde = (365 * gx["theta"] + 0.5 * sx**2 * S**2 * gx["gamma"]
           + (r - q) * S * gx["delta"] - r * gx["precio"])
    max_err = max(max_err, float(np.abs(pde).max()))
print(f"{len(c30_all)} opciones; residuo máximo {max_err:.1e}")
OUT [10]La ecuación de Black-Scholes strike a strike
90 opciones; residuo máximo 3.1e-13

Se cumple en todas, con residuos de coma flotante, porque cada opción se valora con una volatilidad constante: la suya. Eso no significa que la superficie sea coherente con un único proceso del subyacente (con volatilidades distintas por strike, no lo es: es justo lo que modelan la volatilidad local y Heston, lección 3.4.7), solo que cada precio, aislado, es un precio de Black-Scholes.

  1. Tienes una cartera de opciones de ORX con delta 0, gamma de −0,05 por punto, vega de −800 por punto de volatilidad y theta de +90 por día (todo en puntos de índice, sin multiplicador). Si mañana el índice cae 100 puntos y la volatilidad sube 3 puntos, ¿cuánto pierdes aproximadamente? ¿Qué griega domina?
Solución

Con la expansión de Taylor: gamma, 12⋅(−0,05)⋅1002=−250\tfrac{1}{2} \cdot (-0{,}05) \cdot 100^2 = -250 puntos; vega, −800⋅3=−2.400-800 \cdot 3 = -2.400; theta, +90+90. En total, unos −2.560 puntos de índice, 256.000 USD con el multiplicador de 100. Domina la vega: la cartera está corta de volatilidad, y un día de caída con subida de volatilidad es su peor escenario. Habría que añadir vanna y volga si la cartera tiene opciones lejanas, y revalorar la posición completa para movimientos mayores.

Respuesta plegada en cada pregunta

Autoevaluación

  1. py_vollib devuelve para una call una vega de 5,87 y QuantLib, para la misma opción, de 587. ¿Qué pasa?

    • AUna de las dos bibliotecas está mal
    • BSon la misma vega en unidades distintas: py_vollib la da por punto de volatilidad (0,01) y QuantLib por unidad de volatilidad (1,00)
    • CQuantLib la da por contrato con el multiplicador de 100
    • Dpy_vollib usa días hábiles
    Ver respuesta

    Respuesta B. Son la misma vega en unidades distintas: py_vollib la da por punto de volatilidad (0,01) y QuantLib por unidad de volatilidad (1,00)

    Una subida de la volatilidad del 21,7 % al 22,7 % sube la call 5,87 puntos. QuantLib devuelve la derivada respecto a σ en tanto por uno (un salto de 100 puntos de volatilidad), así que hay que dividir por 100. Con theta pasa lo mismo: QuantLib la da por año (theta) o por día (thetaPerDay); py_vollib, por día.

  2. Al calcular gamma con una diferencia central de pasos cada vez más pequeños, el error primero baja y después vuelve a subir. ¿Por qué?

    • APorque gamma no existe en el dinero
    • BPorque el error de truncamiento cae con el paso al cuadrado, pero el de redondeo crece como el épsilon de la máquina por el precio dividido por el paso al cuadrado
    • CPorque NumPy pierde precisión con los logaritmos
    • DPorque la fórmula analítica está mal
    Ver respuesta

    Respuesta B. Porque el error de truncamiento cae con el paso al cuadrado, pero el de redondeo crece como el épsilon de la máquina por el precio dividido por el paso al cuadrado

    Con un precio de 132 puntos y precisión doble, restar dos precios casi iguales y dividir por h² amplifica el redondeo. En la call de ORX el error es de millonésimas o menos con pasos de entre 0,01 y unos pocos puntos de índice; con pasos de 0,0001 llega al 1 %.

  3. Una call en el dinero a 30 días tiene theta de −2,32 puntos por día. ¿Cuánto pierde, con todo lo demás igual, entre el viernes y el lunes?

    • A2,32: solo cuenta un día hábil
    • BUnos 7: el plazo se mide en días naturales y pasan tres
    • CNada: en fin de semana no hay mercado
    • D4,64
    Ver respuesta

    Respuesta B. Unos 7: el plazo se mide en días naturales y pasan tres

    Con T en días naturales / 365, el valor temporal se consume también el fin de semana. Una theta por día natural se multiplica por los días naturales que pasan. Quien mida theta por día hábil la tiene que definir así en todo el sistema; mezclar convenciones descuadra el P&L de los lunes.

  4. ¿Qué mide la vanna?

    • ACuánto cambia la vega con el paso del tiempo
    • BCuánto cambia la delta cuando sube la volatilidad (o, lo que es lo mismo, cuánto cambia la vega cuando sube el subyacente)
    • CLa curvatura del precio respecto a la volatilidad
    • DLa sensibilidad al dividendo
    Ver respuesta

    Respuesta B. Cuánto cambia la delta cuando sube la volatilidad (o, lo que es lo mismo, cuánto cambia la vega cuando sube el subyacente)

    Es la derivada cruzada del precio respecto al subyacente y a la volatilidad. Es casi nula en el dinero y grande en las alas, con signo contrario a cada lado: por eso una cartera delta neutral en strikes lejanos deja de serlo cuando la volatilidad se mueve. La curvatura respecto a la volatilidad es la volga; el cambio de la delta con el tiempo, el charm.

  5. El 3 de abril de 2025 ORX cae un 4,9 % y una put en el dinero de mayo gana 149,87 puntos. La delta explica 115,59. ¿De dónde sale casi todo el resto?

    • ADe la vega
    • BDe la gamma: con una caída de 254 puntos, el término de segundo orden en el índice aporta 31,37
    • CDe la rho
    • DDel redondeo
    Ver respuesta

    Respuesta B. De la gamma: con una caída de 254 puntos, el término de segundo orden en el índice aporta 31,37

    Con movimientos grandes la aproximación lineal se queda corta y el término de segundo orden en el precio pesa: 31,37 puntos, frente a 4,44 de la vega (la volatilidad de ese strike subió 0,62 puntos) y −1,62 de theta. Lo que queda sin explicar es −0,06.

Ideas clave

  • Las griegas son derivadas del precio: delta y gamma respecto al subyacente, vega y volga respecto a la volatilidad, theta y charm respecto al tiempo, rho respecto al tipo y vanna, cruzada entre subyacente y volatilidad.
  • Las unidades deciden: vega por punto de volatilidad, theta por día natural, rho por punto porcentual de tipo; QuantLib da vega y rho por unidad y theta por año, py_vollib ya las divide.
  • Comprobar con diferencias finitas es obligatorio: así se ve que BlackCalculator.volga de QuantLib 1.44 está escrita respecto a la desviación típica total (σ por la raíz de T) y no respecto a σ.
  • El paso de una diferencia finita no puede ser arbitrariamente pequeño: el redondeo crece al reducirlo. Para gamma sobre un índice de 5.000 puntos, entre centésimas y unos pocos puntos.
  • Gamma se concentra en el dinero y crece al acercarse el vencimiento; theta es su contrapartida por la ecuación de Black-Scholes.
  • Una expansión de Taylor con delta, gamma, vega y theta explica el P&L diario de una opción casi entero; en días de movimientos extremos, los términos de tercer orden dejan residuos visibles.

Recursos