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 %.
- 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
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.
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:
| Griega | Derivada | Unidad operativa (curso) | Lectura |
|---|---|---|---|
| Delta | puntos de prima por punto de subyacente | equivalente en subyacente; ×100 = acciones o «delta en dólares» / S | |
| Gamma | cambio de delta por punto | cuánto hay que reajustar la cobertura | |
| Vega | puntos por punto de volatilidad (0,01) | ganancia si la volatilidad sube un punto | |
| Theta | puntos por día natural | valor temporal que se consume en un día | |
| Rho | puntos por punto porcentual de tipo | ganancia si el tipo sube del 4 al 5 % | |
| Vanna | cambio de delta por punto de volatilidad | delta que cambia cuando se mueve la volatilidad | |
| Volga | cambio de vega por punto de volatilidad | convexidad en volatilidad | |
| Charm | cambio de delta por día natural | deriva 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 (, , continuo) y la densidad normal estándar, las de primer orden de una call son:
y las de segundo orden, iguales para call y put salvo el charm:
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 e independiente de . La función devuelve todas ya en las unidades de la tabla, para una call o una put:
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) 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.000513149La 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, = 0,9989. Gamma, vega, vanna y volga coinciden en las dos. Vanna y volga son casi nulas en el dinero (); 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:
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:
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) 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 nanFó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 :
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) 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.5El cociente es exactamente : la «volga» de BlackCalculator es la derivada de la vega respecto a la
desviación típica total , no respecto a . Para usarla como volga hay que multiplicarla por
. 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 grande da un error de truncamiento (la diferencia central yerra en por la derivada siguiente); uno pequeño da un error de redondeo: con precisión doble (), restar dos precios casi iguales de 132 puntos y dividir por amplifica ese épsilon. Para gamma el error total es del orden de , mínimo con , del orden de una diezmilésima del subyacente (unos 0,6 puntos con ORX a 5.148):
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}")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-03Con 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: 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):
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})")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 : 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:
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:
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) 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-13A 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 , 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:
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) 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.17552Dos lecturas de la tabla:
- Delta no es la probabilidad de ejercicio. La probabilidad neutral al riesgo de acabar dentro es , no : en la call de 5.400 la delta es 0,185 y , 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).
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:
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:
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")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: float64La 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:
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
BlackCalculatorrespecto a . Se convierten en un único sitio del código y se prueban. - Theta por día hábil o natural. Con en días naturales / 365, el fin de semana consume tres días de theta. Si el sistema usa días hábiles para , la volatilidad tiene que estar en la misma base.
- Diferencias finitas con pasos ridículos. no es más preciso: es ruido. Para gamma, del orden de una diezmilésima del subyacente; para la vega, 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
- 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
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")})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.15Con 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 , 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).
- 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
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}")90 opciones; residuo máximo 3.1e-13Se 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.
- 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, puntos; vega, ; theta, . 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
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.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 %.
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.
¿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.
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.volgade 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.