Lección 4.2.6 · 65 min
Riesgo de derivados: griegas de cartera, margen y liquidaciones
Un libro de derivados no se gestiona con el precio de cada contrato, sino con sus sensibilidades sumadas, con el margen que exige y con la distancia a la liquidación. Esta lección agrega las griegas de un libro de opciones sobre índice y acción en dólares, le pone límites, lo lleva por la caída de abril de 2025 de los datos y compara sus márgenes; después mide cuántas veces se liquida un par de perpetuos con margen aislado y con margen cruzado.
- Módulo
- 4.2 · Gestión del riesgo y tamaño de posición
- Duración
- 65 min
- Requisitos
- 3.4.3 · Griegas: delta, gamma, vega, theta, rho, vanna, volga y charm, 3.5.4 · Herramienta propia: payoff, griegas agregadas y escenarios, 3.6.1 · Venta sistemática de prima: put-write, strangles y su riesgo de cola, 3.7.1 · Perpetuos: mecánica del funding, mark price y liquidaciones, 3.1.3 · Gestión del roll, márgenes, SPAN y apalancamiento implícito, 4.2.4 · Stress testing y riesgos de correlación, liquidez, gap y contraparte, 4.2.5 · Control del drawdown y kill switch
Lecciones del nivel 4
4.1 · Teoría y optimización de carteras
- 4.1.1 Markowitz: frontera eficiente y restricciones reales
- 4.1.2 Estimación de covarianzas: shrinkage, modelos factoriales y denoising
- 4.1.3 Black-Litterman: del equilibrio del mercado a las visiones
- 4.1.4 Risk parity, mínima varianza, HRP y HERC
- 4.1.5 Optimización con CVaR, drawdown, rotación y costes
- 4.1.6 Modelos factoriales: CAPM, Fama-French, PCA y atribución
- 4.1.7 Rebalanceo y carteras de estrategias
- 4.1.8 Herramientas de carteras: Riskfolio-Lib, PyPortfolioOpt, skfolio y bt
4.2 · Gestión del riesgo y tamaño de posición
- 4.2.1 Riesgo por operación, stops y riesgo de ruina
- 4.2.2 Criterio de Kelly y volatility targeting: cuánto arriesgar
- 4.2.3 VaR, Expected Shortfall y backtesting del VaR
- 4.2.4 Stress testing y riesgos de correlación, liquidez, gap y contraparte
- 4.2.5 Control del drawdown y kill switch
- 4.2.6 Riesgo de derivados: griegas de cartera, margen y liquidaciones
4.3 · Microestructura y ejecución
4.4 · Brokers y APIs
- 4.4.1 Elegir bróker desde España o la UE: regulación, activos, costes y API
- 4.4.2 Interactive Brokers: TWS e IB Gateway, ib_async, contratos, datos y órdenes
- 4.4.3 Alpaca, Tradier, MetaTrader 5 con Python y OANDA v20
- 4.4.4 APIs de exchanges cripto: ccxt, Binance, Bybit, Kraken y Coinbase
- 4.4.5 Credenciales seguras: variables de entorno, gestores de secretos y permisos mínimos
4.5 · Sistema de trading en vivo
- 4.5.1 Arquitectura de un sistema en vivo: de los datos a la reconciliación
- 4.5.2 Gestión de órdenes: estados, fills parciales, cancelaciones y reconexiones
- 4.5.3 Paper trading, shadow trading y seguimiento del slippage
- 4.5.4 Programación de tareas y alertas: cron, APScheduler, Prefect, Telegram, Slack y email
- 4.5.5 Tolerancia a fallos: idempotencia, estado persistente y reconciliación al arrancar
- 4.5.6 NautilusTrader o LEAN en real: del backtest a producción con el mismo código
Del contrato al libro: griegas en dinero
La lección 3.4.3 calculó las griegas de una opción y la 3.5.4 construyó una herramienta que las suma para una posición de varias patas sobre un mismo subyacente. Un libro real tiene opciones sobre varios subyacentes y varios vencimientos, y quien lo vigila no pregunta por la delta de una put, sino por cosas como «cuánto pierdo si el mercado cae un 1 %» o «cuánto gano si la volatilidad sube un punto». Para eso las griegas se pasan a dinero, contrato a contrato, y se suman:
| Griega del libro | Fórmula por pata ( contratos de multiplicador ) | Lectura |
|---|---|---|
| Delta por 1 % | USD que gana el libro si el subyacente sube un 1 % | |
| Gamma por 1 % | USD que añade la curvatura en un movimiento del 1 % (en uno del %, veces) | |
| Vega por punto | USD por cada punto de volatilidad implícita | |
| Theta por día | USD que gana o pierde el libro por día natural con todo igual |
Con ellas, el P&L de un movimiento del % se aproxima por Taylor como
. La lección pone a
prueba esa aproximación con los datos sintéticos del nivel 3: la cadena diaria de opciones de ORX, el índice ficticio
que imita al S&P 500 (multiplicador 100), y la de KRN, una acción ficticia con opciones americanas. El motor de
valoración es el Black-Scholes-Merton de la lección 3.5.4, sin cambios, con la volatilidad iv_modelo de cada
opción, el tipo interpolado en la curva y el rendimiento por dividendo del índice:
import numpy as np
import pandas as pd
from scipy.special import ndtr
AÑO, MULT = 365.0, 100 # T en días naturales / 365; 100 USD por punto
def bsm(tipo, S, K, T, vol, r, q):
"""Black-Scholes-Merton de la lección 3.5.4: precio y griegas por
unidad; vega por punto de volatilidad, theta por día natural."""
S, K, T, vol = np.broadcast_arrays(
*(np.asarray(x, dtype=float) for x in (S, K, T, vol)))
vivo = T > 0
Tv = np.where(vivo, T, 1.0)
raiz = vol * np.sqrt(Tv)
d1 = (np.log(S / K) + (r - q + vol**2 / 2) * Tv) / raiz
d2 = d1 - raiz
dq, dr = np.exp(-q * Tv), np.exp(-r * Tv)
n1 = np.exp(-d1**2 / 2) / np.sqrt(2 * np.pi)
w = 1.0 if tipo == "C" else -1.0
precio = w * (S * dq * ndtr(w * d1) - K * dr * ndtr(w * d2))
theta = (-S * dq * n1 * vol / (2 * np.sqrt(Tv))
+ w * q * S * dq * ndtr(w * d1)
- w * r * K * dr * ndtr(w * d2))
intr = np.maximum(w * (S - K), 0.0)
return {"precio": np.where(vivo, precio, intr),
"delta": np.where(vivo, w * dq * ndtr(w * d1),
w * (intr > 0)),
"gamma": np.where(vivo, dq * n1 / (S * raiz), 0.0),
"vega": np.where(vivo, S * dq * n1 * np.sqrt(Tv) / 100,
0.0),
"theta": np.where(vivo, theta / AÑO, 0.0)}
idx = pd.read_csv("opciones3_indice.csv", parse_dates=["fecha"])
idx = idx.set_index("fecha")
krn = pd.read_csv("accion3_diario.csv", parse_dates=["fecha"])
krn = krn.set_index("fecha")
cadenas = {"ORX": pd.read_parquet("opciones3_cadena.parquet"),
"KRN": pd.read_parquet("accion3_opciones.parquet")}
cadenas = {k: v[v["fecha"] >= "2025-03-01"].set_index(
["fecha", "vencimiento", "tipo", "strike"]).sort_index()
for k, v in cadenas.items()}
def subyacente(sub, f):
"""Precio y rendimiento por dividendo continuo del subyacente.
KRN no tiene fecha ex antes del 17 de abril: q = 0."""
if sub == "ORX":
return (idx.at[f, "cierre"],
idx.at[f, "rentabilidad_dividendo"] / 100)
return krn.at[f, "cierre"], 0.0
def tipo_libre(f, dias):
"""Tipo continuo interpolado en la curva de ORX (tanto por uno)."""
plazos = [30, 91, 182, 365, 730]
t = idx.loc[f, ["tipo_1m", "tipo_3m", "tipo_6m", "tipo_1a",
"tipo_2a"]].to_numpy(dtype=float) / 100
return float(np.interp(dias, plazos, t))El libro se monta el lunes 24 de marzo de 2025 con un capital de 500.000 USD. Es un libro típico de venta de volatilidad: un strangle vendido de abril (8 puts 5000 y 8 calls 5500), un straddle vendido de mayo (2 calls y 2 puts 5300), 8 puts 4600 de mayo compradas como seguro de cola y 30 puts 205 de abril vendidas sobre KRN. Las cifras de los datos son de un mercado ficticio:
libro = pd.DataFrame(
[("ORX", "2025-04-17", "P", 5000, -8),
("ORX", "2025-04-17", "C", 5500, -8),
("ORX", "2025-05-16", "P", 5300, -2),
("ORX", "2025-05-16", "C", 5300, -2),
("ORX", "2025-05-16", "P", 4600, 8),
("KRN", "2025-04-17", "P", 205, -30)],
columns=["sub", "venc", "tipo", "strike", "n"])
libro["venc"] = pd.to_datetime(libro["venc"])
CAPITAL = 500_000
def griegas(f, mov=0.0, dvol=0.0, patas=None):
"""Valor y griegas en USD de cada pata el día f. mov: movimiento
relativo de los subyacentes; dvol: cambio de la volatilidad en
tanto por uno (0,10 = 10 puntos). patas: libro (por defecto)."""
filas = []
patas = libro if patas is None else patas
for p in patas.itertuples():
S, q = subyacente(p.sub, f)
S = S * (1 + mov)
dias = (p.venc - f).days
clave = (f, p.venc, p.tipo, float(p.strike))
iv = cadenas[p.sub].at[clave, "iv_modelo"] if dias > 0 else 0.2
g = bsm(p.tipo, S, p.strike, dias / AÑO, iv + dvol,
tipo_libre(f, max(dias, 1)), q)
u = p.n * MULT
filas.append({
"pata": f"{p.sub} {p.venc:%d-%m} {p.tipo}{p.strike}",
"valor": u * float(g["precio"]),
"delta_1pct": u * float(g["delta"]) * S * 0.01,
"gamma_1pct": u * 0.5 * float(g["gamma"]) * (0.01 * S)**2,
"vega_punto": u * float(g["vega"]),
"theta_dia": u * float(g["theta"])})
return pd.DataFrame(filas).set_index("pata")
F0 = pd.Timestamp("2025-03-24")
g0 = griegas(F0)
g0.loc["TOTAL"] = g0.sum()
g0.round(0) valor delta_1pct gamma_1pct vega_punto theta_dia
pata
ORX 17-04 P5000 -36227.0 8712.0 -844.0 -3087.0 1713.0
ORX 17-04 C5500 -18951.0 -8011.0 -1304.0 -2934.0 1109.0
ORX 16-05 P5300 -31437.0 5114.0 -281.0 -1600.0 251.0
ORX 16-05 C5300 -31355.0 -5419.0 -281.0 -1600.0 337.0
ORX 16-05 P4600 23842.0 -4173.0 319.0 2798.0 -761.0
KRN 17-04 P205 -4590.0 1254.0 -138.0 -456.0 222.0
TOTAL -98718.0 -2524.0 -2530.0 -6879.0 2871.0El libro vale −98.718 USD: es la prima neta cobrada, lo que costaría recomprarlo todo. Sus griegas dicen que es un libro corto de volatilidad: vega de −6.879 USD por punto, theta de +2.871 USD al día y una gamma de −2.530 USD por cada movimiento del 1 % (unos −253.000 en uno del 10 %, si la gamma no cambiara). La delta total es pequeña, −2.524 USD por 1 %, porque las patas se compensan: la put vendida de abril aporta +8.712 y la call vendida, −8.011. Una delta pequeña hecha de dos deltas grandes de signo contrario es frágil: cualquier movimiento deshace el equilibrio.
Sumar así tiene dos trampas. La delta de KRN está en USD por 1 % de KRN, no de ORX: para sumarla hay que convertirla con la beta de KRN frente al índice (un movimiento del 1 % de ORX mueve KRN, de media, un %). Y la vega de un vencimiento cercano no equivale a la de uno lejano: la volatilidad implícita de los plazos cortos se mueve más. Una convención habitual pondera la vega de cada plazo por , con los días al vencimiento, para expresarla en vega equivalente a 30 días:
r_orx = idx["cierre"].pct_change()
r_krn = krn["cierre"].pct_change()
ventana = slice("2024-03-25", "2025-03-24")
beta = (np.cov(r_krn[ventana], r_orx[ventana])[0, 1]
/ r_orx[ventana].var())
print(f"Beta de KRN frente a ORX: {beta:.2f}")
d = g0.drop("TOTAL")["delta_1pct"]
es_krn = d.index.str.startswith("KRN")
print(f"Delta sumada sin más: {d.sum():9.0f} USD por 1 %")
print(f"Delta en 1 % de ORX: "
f"{d[~es_krn].sum() + beta * d[es_krn].sum():9.0f}")
orx = libro["sub"].eq("ORX").to_numpy()
tabla = pd.DataFrame({
"vega": g0["vega_punto"].iloc[:-1].to_numpy()[orx],
"dias": (libro.loc[orx, "venc"] - F0).dt.days.to_numpy()})
tabla["vega_30d"] = tabla["vega"] * np.sqrt(30 / tabla["dias"])
tabla.groupby("dias")[["vega", "vega_30d"]].sum().round(0)Beta de KRN frente a ORX: 0.00
Delta sumada sin más: -2524 USD por 1 %
Delta en 1 % de ORX: -3773
vega vega_30d
dias
24 -6021.0 -6732.0
53 -402.0 -303.0La beta de KRN frente a ORX en el último año es 0,00: en los datos del nivel 3, KRN se generó sin factor de mercado (en toda la muestra, 0,09). Es una limitación de los datos que viene bien aquí: con la beta medida, la delta de KRN no añade riesgo de mercado y la delta del libro en términos de ORX es −3.773 USD por 1 %, la de las patas de ORX. Con una acción real de beta 1 se sumarían los 1.254 de KRN. La vega por vencimiento enseña lo que oculta el total: el libro está corto de 6.021 USD por punto de la volatilidad de abril (6.732 en vega equivalente a 30 días) y casi plano en mayo (−402), porque las puts 4600 compradas compensan el straddle vendido. Una subida de la volatilidad de corto plazo mayor que la de largo, lo normal en una caída, le hace más daño que el total de −6.879.
Límites de griegas y por qué no bastan
Un límite de griegas fija cuánta sensibilidad acepta el libro. Los de la lección, para 500.000 USD de capital: delta entre −5.000 y +5.000 USD por 1 % (un 1 % del capital por cada 1 % de movimiento), gamma de −3.000 USD por 1 % como mucho y vega de −8.000 USD por punto como mucho. El libro se mantiene sin ajustes hasta el 16 de abril, la víspera del vencimiento de abril, y se revisa al cierre de cada sesión:
fechas = idx.loc["2025-03-24":"2025-04-16"].index
serie = pd.DataFrame({f: griegas(f).sum() for f in fechas}).T
serie["pyg"] = serie["valor"] - serie["valor"].iloc[0]
serie["ORX"] = idx.loc[fechas, "cierre"]
LIM = {"delta_1pct": 5_000, "gamma_1pct": -3_000,
"vega_punto": -8_000}
serie["fuera"] = ((serie["delta_1pct"].abs() > LIM["delta_1pct"])
| (serie["gamma_1pct"] < LIM["gamma_1pct"])
| (serie["vega_punto"] < LIM["vega_punto"]))
cols = ["ORX", "pyg", "delta_1pct", "gamma_1pct", "vega_punto",
"theta_dia", "fuera"]
serie[cols].round(0) ORX pyg delta_1pct gamma_1pct vega_punto theta_dia fuera
2025-03-24 5277.0 0.0 -2524.0 -2530.0 -6879.0 2871.0 False
2025-03-25 5279.0 -500.0 -3059.0 -2503.0 -6698.0 3013.0 False
2025-03-26 5334.0 -9951.0 -8233.0 -2690.0 -7092.0 3357.0 True
2025-03-27 5333.0 -11068.0 -7588.0 -2745.0 -7000.0 3509.0 True
2025-03-28 5243.0 1063.0 807.0 -2425.0 -5889.0 3279.0 False
2025-03-31 5266.0 11521.0 -1396.0 -2468.0 -5297.0 3505.0 False
2025-04-01 5208.0 15550.0 3185.0 -2182.0 -4400.0 3331.0 False
2025-04-02 5211.0 20555.0 3521.0 -2210.0 -4176.0 3287.0 False
2025-04-03 4957.0 -28259.0 18487.0 -1542.0 -1484.0 2426.0 True
2025-04-04 4659.0 -168869.0 24871.0 -467.0 1844.0 426.0 True
2025-04-07 4824.0 -76774.0 22948.0 -1157.0 341.0 2379.0 True
2025-04-08 4821.0 -73556.0 22895.0 -1088.0 618.0 2363.0 True
2025-04-09 5280.0 -8359.0 -6330.0 -2227.0 -3310.0 9434.0 True
2025-04-10 5057.0 9814.0 10048.0 -1677.0 -1250.0 6304.0 True
2025-04-11 4942.0 -9342.0 17850.0 -1583.0 -51.0 4767.0 True
2025-04-14 4966.0 18609.0 18429.0 -2261.0 394.0 6953.0 True
2025-04-15 4953.0 21936.0 21754.0 -2842.0 802.0 7363.0 True
2025-04-16 5074.0 61733.0 6641.0 -2777.0 373.0 14606.0 TrueEl libro nace dentro de los tres límites. Sale por la delta el 26 y el 27 de marzo, con una subida de ORX del 1 % (−8.233 y −7.588 USD), y vuelve a entrar. El 2 de abril va ganando 20.555 USD. El 3 de abril ORX cae un 4,9 % y el 4 de abril otro 6,0 %: el libro pierde 168.869 USD desde el inicio, y su delta pasa de +3.521 a +24.871 USD por 1 %, cinco veces el límite. Las puts vendidas de abril han pasado a estar dentro del dinero y se comportan como un largo del índice. Desde ahí la delta queda fuera del límite todas las sesiones hasta el 16, también el 9 de abril, cuando ORX sube un 9,5 % y la delta salta a −6.330.
La gamma nunca rompe su límite: en la caída baja de −2.210 a −467 USD, porque las puts muy dentro del dinero y la call muy fuera tienen poca curvatura. La vega incluso cambia de signo (+1.844 el 4 de abril), por el peso de las puts 4600 compradas. Un límite de griegas mide la sensibilidad en el punto de hoy; lo que cambia en una caída grande es el punto. La figura de la cabecera compara, para el libro del 2 de abril, lo que predicen sus griegas con lo que da revalorar cada opción:
F1 = pd.Timestamp("2025-04-02")
base = griegas(F1)
v0 = base["valor"].sum()
d1, g1 = base["delta_1pct"].sum(), base["gamma_1pct"].sum()
filas = {}
for mov in (-0.15, -0.106, -0.08, -0.05, -0.02, 0.02, 0.05, 0.10):
m = 100 * mov
filas[f"{mov:+.1%}"] = {
"delta": d1 * m,
"delta_gamma": d1 * m + g1 * m**2,
"completa": griegas(F1, mov)["valor"].sum() - v0,
"completa_vol+10": griegas(F1, mov, 0.10 * (mov < 0))[
"valor"].sum() - v0}
pd.DataFrame(filas).T.round(0) delta delta_gamma completa completa_vol+10
-15.0% -52816.0 -550059.0 -372678.0 -337789.0
-10.6% -37323.0 -285635.0 -226400.0 -209292.0
-8.0% -28169.0 -169607.0 -144220.0 -143128.0
-5.0% -17605.0 -72855.0 -66246.0 -83912.0
-2.0% -7042.0 -15882.0 -15403.0 -50036.0
+2.0% 7042.0 -1798.0 -2337.0 -2337.0
+5.0% 17605.0 -37644.0 -45505.0 -45505.0
+10.0% 35211.0 -185786.0 -221008.0 -221008.0Para una caída del 10,6 %, la de los dos días siguientes, la delta sola predice −37.323 USD; delta y gamma, −285.635; la revaloración completa, −226.400. Taylor exagera la pérdida en las caídas grandes porque la gamma de las puts vendidas baja a medida que entran en el dinero (la derivada de la gamma, la speed, no está en la aproximación), y la subestima en las subidas grandes: con +10 %, −185.786 frente a −221.008, porque ahí las calls vendidas ganan gamma al acercarse a sus strikes. Con −15 % la diferencia es de 177.000 USD.
La volatilidad complica más la cuenta. Con 10 puntos más en las caídas, la pérdida de un −2 % pasa de −15.403 a −50.036 USD (el libro está corto de vega en el corto plazo), pero la de un −10,6 % baja de −226.400 a −209.292: a esas alturas las puts 4600 compradas, con mucha vega, ganan más con la subida de la volatilidad que lo que pierden las vendidas. El signo del efecto de la volatilidad depende del tamaño de la caída. Lo que pasó de verdad:
F2 = pd.Timestamp("2025-04-04")
real = serie.at[F2, "valor"] - serie.at[F1, "valor"]
print(f"ORX: {idx.at[F2, 'cierre'] / idx.at[F1, 'cierre'] - 1:+.2%}"
f" KRN: {krn.at[F2, 'cierre'] / krn.at[F1, 'cierre'] - 1:+.2%}")
print(f"P&L real del libro: {real:,.0f} USD"
f" ({real / CAPITAL:+.1%} del capital)")
iv = cadenas["ORX"].xs(("2025-05-16", "P", 5300.0),
level=[1, 2, 3])["iv_modelo"]
print(f"IV de la put 5300 de mayo: {iv[F1]:.1%} a {iv[F2]:.1%}")
por_pata = griegas(F2)["valor"] - base["valor"]
por_pata.round(0)ORX: -10.60% KRN: -2.25%
P&L real del libro: -189,424 USD (-37.9% del capital)
IV de la put 5300 de mayo: 20.8% a 28.0%
pata
ORX 17-04 P5000 -247251.0
ORX 17-04 C5500 5494.0
ORX 16-05 P5300 -90906.0
ORX 16-05 C5300 19687.0
ORX 16-05 P4600 124845.0
KRN 17-04 P205 -1293.0
Name: valor, dtype: float64El libro perdió 189.424 USD en dos sesiones, un 37,9 % del capital, con ORX −10,6 %, KRN −2,25 % y la volatilidad implícita de la put 5300 de mayo de un 20,8 % a un 28,0 %. Casi toda la pérdida es de la put vendida de abril (−247.251) y de la de mayo (−90.906); el seguro de cola ganó 124.845. La pérdida real es menor que las dos revaloraciones (−226.400 con la volatilidad constante y −209.292 con 10 puntos más) porque la volatilidad no se desplaza en paralelo (sube más en los strikes bajos, lección 3.4.5, y las puts 4600 compradas ganan más que en el escenario) y porque pasan dos días de theta.
Margen: la regla de la bolsa y el margen de cartera
El bróker no pide una garantía por las griegas sino por una regla. Dos familias:
- Por estrategia (la de la ficha de las opciones SPX de Cboe, que ya usó la lección 3.6.1): cada opción vendida sin cubrir exige el 100 % de la prima más el 15 % del valor del índice por el multiplicador, menos lo que esté fuera del dinero, con un mínimo de la prima más el 10 % del índice (calls) o del strike (puts). Las opciones compradas se pagan enteras. Los diferenciales y las combinaciones tienen reglas propias, más favorables, que la lección no modela: la cifra que sale es una cota alta.
- De cartera (portfolio margining): la pérdida teórica más grande del conjunto de posiciones en una rejilla de movimientos del subyacente. La norma 4210(g) de FINRA fija para un índice amplio de gran capitalización movimientos de −8 % a +6 % en diez intervalos equidistantes y un mínimo de 0,375 USD por contrato y unidad del multiplicador (37,50 por contrato de ORX). Los precios teóricos los da en la realidad un modelo de la cámara; la lección usa el Black-Scholes-Merton de antes con la volatilidad de cada día.
def margen_cboe(f):
"""Ficha de las SPX: vendida sin cubrir = 100 % de la prima + 15 %
del índice - lo que esté fuera del dinero; mínimo: prima + 10 % del
índice (calls) o del strike (puts). Las compradas se pagan."""
S, _ = subyacente("ORX", f)
total = 0.0
for p in libro[libro["sub"] == "ORX"].itertuples():
clave = (f, p.venc, p.tipo, float(p.strike))
if p.n > 0 or clave not in cadenas["ORX"].index:
continue
o = cadenas["ORX"].loc[clave]
prima = (o["bid"] + o["ask"]) / 2
fuera = max(S - p.strike, 0) if p.tipo == "P" else max(
p.strike - S, 0)
minimo = prima + 0.10 * (p.strike if p.tipo == "P" else S)
total += -p.n * MULT * max(prima + 0.15 * S - fuera, minimo)
return total
def margen_cartera(f, rejilla=np.linspace(-0.08, 0.06, 11)):
"""Peor pérdida de la parte ORX en 11 puntos de -8 % a +6 %
(FINRA 4210(g) para índices amplios), con la IV de hoy."""
orx = libro["sub"].eq("ORX").to_numpy()
v0 = griegas(f)["valor"].to_numpy()[orx].sum()
perdidas = [griegas(f, m)["valor"].to_numpy()[orx].sum() - v0
for m in rejilla]
minimo = 0.375 * MULT * libro.loc[orx, "n"].abs().sum()
return max(-min(perdidas), minimo)
filas = {}
for f in ["2025-03-24", "2025-04-02", "2025-04-04", "2025-04-08",
"2025-04-16"]:
f = pd.Timestamp(f)
filas[f.date()] = {"capital": CAPITAL + serie.at[f, "pyg"],
"cboe_sin_cubrir": margen_cboe(f),
"cartera": margen_cartera(f)}
m = pd.DataFrame(filas).T
m["uso_cartera"] = m["cartera"] / m["capital"]
m.round(2) capital cboe_sin_cubrir cartera uso_cartera
2025-03-24 500000.00 1296390.0 114516.63 0.23
2025-04-02 520555.12 1272542.8 126028.31 0.24
2025-04-04 331131.25 1581945.0 184350.02 0.56
2025-04-08 426444.50 1486100.0 202485.31 0.47
2025-04-16 561732.91 1307566.2 205251.86 0.37Las dos reglas dan números de órdenes de magnitud distintos. Por estrategia, la parte de índice del libro pide 1,30 millones de USD, más del doble del capital: con esa regla el libro no se podría abrir sin las compensaciones de los diferenciales. Con margen de cartera pide 114.517 USD, un 23 % del capital. El 4 de abril el margen de cartera sube a 184.350 (un 56 % de un capital que ha bajado a 331.131) y sigue subiendo después, aunque el libro se recupera: 205.252 el 16 de abril. Es la prociclicidad que la lección 3.1.3 vio en los futuros.
El margen de cartera de la víspera, 126.028 USD, cubría una caída del 8 % con la volatilidad constante. La caída fue del 10,6 % con la volatilidad al alza, y la pérdida de dos sesiones lo superó en un 50 %. El margen es una regla de garantía calibrada para unos días normales, no una medida de riesgo; un margen bajo permite más apalancamiento, que es justo lo que hay que vigilar.
Liquidaciones en cripto: margen aislado y cruzado
En los perpetuos (lección 3.7.1) no hay llamada de margen con días para aportar: cuando el colateral baja del margen de mantenimiento, el motor del exchange liquida la posición. La lección 3.7.1 derivó el precio de liquidación de una posición aislada y advirtió que con margen cruzado todo el saldo responde de todas las posiciones. Aquí se mide lo que eso supone para una posición cubierta.
Los datos son las barras de 1 hora de los perpetuos de XBQ y XEQ en alfa (el exchange que imita a Binance), con el
mark de cada hora, de 2020 a 2025. Cada lunes a las 00:00 UTC se abre una posición y se sigue 28 días con tres
variantes del mismo apalancamiento (nominal bruto entre colateral) y un mantenimiento del 0,5 %:
- Largo de XBQ aislado: se liquida si el mínimo horario del mark toca .
- Par aislado: largo de XBQ y corto de XEQ con el mismo nominal, cada pata con la mitad del colateral y el mismo apalancamiento . Se cuenta como liquidado si cualquiera de las dos patas lo está; la otra queda sin cobertura.
- Par cruzado: el mismo par con un colateral común. Se liquida cuando el valor de la cuenta (colateral más el P&L de las dos patas al mark de cierre de cada hora) baja del mantenimiento de las dos.
h = pd.read_parquet("cripto3_1h.parquet")
mark = h.pivot(index="ts", columns="activo", values="mark_cierre")
mini = h.pivot(index="ts", columns="activo", values="mark_minimo")
maxi = h.pivot(index="ts", columns="activo", values="mark_maximo")
MMR, H = 0.005, 24 * 28 # mantenimiento; 28 días
lunes = mark.index[(mark.index.dayofweek == 0)
& (mark.index.hour == 0)]
lunes = lunes[lunes <= mark.index[-1] - pd.Timedelta(hours=H)]
pos = mark.index.get_indexer(lunes)
X, LO, HI = mark.to_numpy(), mini.to_numpy(), maxi.to_numpy()
def liquidaciones(L):
n = {"largo_XBQ": 0, "par_aislado": 0, "par_cruzado": 0}
for i in pos:
s = slice(i + 1, i + 1 + H)
e1, e2 = X[i] # XBQ, XEQ
liq_l = e1 * (1 - 1 / L) / (1 - MMR) # lección 3.7.1
liq_c = e2 * (1 + 1 / L) / (1 + MMR)
largo = (LO[s, 0] <= liq_l).any()
n["largo_XBQ"] += largo
n["par_aislado"] += largo or (HI[s, 1] >= liq_c).any()
q1, q2 = L / 2 / e1, L / 2 / e2 # colateral 1, cruzado
valor = 1 + q1 * (X[s, 0] - e1) - q2 * (X[s, 1] - e2)
mant = MMR * (q1 * X[s, 0] + q2 * X[s, 1])
n["par_cruzado"] += (valor <= mant).any()
return {k: v / len(pos) for k, v in n.items()}
print(f"{len(pos)} entradas, correlación horaria XBQ-XEQ: "
f"{np.log(mark).diff().corr().iloc[0, 1]:.2f}")
liq = pd.DataFrame({L: liquidaciones(L) for L in (2, 3, 5, 10, 20)}).T
liq.rename_axis("apalancamiento").round(3)309 entradas, correlación horaria XBQ-XEQ: 0.77
largo_XBQ par_aislado par_cruzado
apalancamiento
2 0.010 0.087 0.000
3 0.039 0.256 0.003
5 0.175 0.595 0.032
10 0.359 0.909 0.184
20 0.602 1.000 0.485XBQ y XEQ tienen una correlación horaria de 0,77, así que el par cubre la mayor parte del riesgo de cada pata. Con margen cruzado se nota: a 5x se liquida el 3,2 % de las entradas frente al 17,5 % del largo solo. Con margen aislado, el mismo par es peor que el largo solo: un 59,5 % a 5x y un 90,9 % a 10x. Cada pata tiene el apalancamiento y se liquida con su propio movimiento, aunque la otra gane lo mismo. A 5x, el corto de XEQ se liquida en el 44 % de las entradas (el 77 % de las de 2020 y el 60 % de las de 2021, años de subidas fuertes). Una cobertura con margen aislado no es una cobertura.
El margen cruzado tiene su propio riesgo: una posición mala puede consumir el colateral de las demás. A 20x, el par cruzado se liquida en casi la mitad de las entradas (48,5 %): el diferencial entre XBQ y XEQ se mueve lo bastante para llevarse un colateral del 5 %. Como límite operativo sirve la distancia a la liquidación medida en desviaciones típicas del horizonte. Para un largo aislado, con la volatilidad horaria de los 30 días anteriores y la aproximación normal sin deriva (principio de reflexión), la probabilidad de tocar el precio de liquidación en horas es , con la distancia logarítmica:
from scipy.stats import norm
lr = np.log(mark["XBQ"]).diff()
sig = lr.rolling(24 * 30).std().to_numpy() # 30 días, horaria
filas = {}
for L in (2, 3, 5, 10, 20):
prev, real = [], []
for i in pos[5:]:
e1 = X[i, 0]
d = np.log((1 - MMR) / (1 - 1 / L)) # ln(entrada/liq.)
z = d / (sig[i] * np.sqrt(H))
prev.append(2 * norm.cdf(-z)) # reflexión, normal
real.append((LO[i + 1:i + 1 + H, 0]
<= e1 * (1 - 1 / L) / (1 - MMR)).any())
filas[L] = {"distancia_pct": 100 * (1 - (1 - 1 / L) / (1 - MMR)),
"sigmas_28d_mediana": np.median(
d / (sig[pos[5:]] * np.sqrt(H))),
"prevista": np.mean(prev), "real": np.mean(real)}
pd.DataFrame(filas).T.rename_axis("apalancamiento").round(3) distancia_pct sigmas_28d_mediana prevista real
apalancamiento
2 49.749 4.596 0.000 0.010
3 32.998 2.674 0.018 0.039
5 19.598 1.457 0.153 0.178
10 9.548 0.670 0.489 0.365
20 4.523 0.309 0.746 0.612A 2x la liquidación está a un 49,7 % del precio, 4,6 desviaciones típicas de 28 días: la normal no prevé ninguna liquidación y en los datos hay un 1,0 %. A 3x, 2,7 desviaciones, prevé un 1,8 % y son un 3,9 %. Donde la liquidación está lejos, la normal infravalora mucho, en proporción, por las colas gruesas y los saltos (las mechas de la lección 3.7.1). Donde está cerca, a 10x y 20x, sobrestima (48,9 % frente a 36,5 %), porque la muestra tiene una deriva alcista que la fórmula no lleva. Un límite razonable no es un apalancamiento máximo fijo, sino una distancia mínima a la liquidación en desviaciones típicas de la volatilidad actual, con margen para la cola: cuando la volatilidad sube, la misma posición está más cerca de la liquidación y hay que reducirla antes de que lo haga el motor.
Un control de griegas antes de cada orden
Los límites solo sirven si se comprueban antes de enviar cada orden, con el libro que resultaría de ejecutarla. El control de la lección suma las griegas del libro con la pata nueva y añade un límite de escenario: la pérdida con una caída del 10 % y 10 puntos más de volatilidad no puede pasar del 20 % del capital (100.000 USD).
Fuente Mermaid
flowchart TB
O[Orden propuesta] --> L[Libro + orden]
L --> G{Griegas en límite?}
G -- no --> R[Rechazo]
G -- sí --> E{Estrés en límite?}
E -- no --> R
E -- sí --> B[Al bróker]def revisar(patas, f, lim=LIM, perdida_max=0.20 * CAPITAL):
"""Límites de griegas y un escenario de -10 % con +10 puntos de
volatilidad. Devuelve la lista de límites rotos."""
tot = griegas(f, patas=patas).sum()
estres = (griegas(f, -0.10, 0.10, patas=patas)["valor"].sum()
- tot["valor"])
rotos = []
if abs(tot["delta_1pct"]) > lim["delta_1pct"]:
rotos.append(f"delta {tot['delta_1pct']:,.0f}")
if tot["gamma_1pct"] < lim["gamma_1pct"]:
rotos.append(f"gamma {tot['gamma_1pct']:,.0f}")
if tot["vega_punto"] < lim["vega_punto"]:
rotos.append(f"vega {tot['vega_punto']:,.0f}")
if estres < -perdida_max:
rotos.append(f"escenario {estres:,.0f}")
return rotos
def cabe(nueva, f):
"""Control previo a una orden: el libro con la pata nueva."""
prueba = pd.concat([libro, pd.DataFrame([nueva])],
ignore_index=True)
rotos = revisar(prueba, f)
return ("acepta", []) if not rotos else ("rechaza", rotos)
print("Libro el 24-03:", revisar(libro, F0))
print("Libro el 02-04:", revisar(libro, F1))
venta = {"sub": "ORX", "venc": pd.Timestamp("2025-04-17"),
"tipo": "P", "strike": 5000, "n": -4}
cobertura = {"sub": "ORX", "venc": pd.Timestamp("2025-05-16"),
"tipo": "P", "strike": 4600, "n": 8}
print("Vender 4 puts más:", cabe(venta, F1))
print("Comprar 8 puts 4600:", cabe(cobertura, F1))Libro el 24-03: ['escenario -169,983']
Libro el 02-04: ['escenario -193,086']
Vender 4 puts más: ('rechaza', ['delta 8,450', 'escenario -316,525'])
Comprar 8 puts 4600: ('acepta', [])El control dice algo incómodo: el libro de la lección no pasaba el límite de escenario el día que se montó (−169.983 USD frente a un tope de −100.000), aunque cumplía los tres límites de griegas. El 2 de abril, un día antes de la caída, el escenario daba −193.086. Vender 4 puts más de abril se rechaza por la delta y por el escenario (−316.525); comprar 8 puts 4600 más se acepta, porque las coberturas reducen el escenario. Un control así no impide perder, pero hace que el tamaño del libro lo decida un escenario fijado de antemano y no la prima que se cobra.
Errores típicos
- Sumar deltas de subyacentes distintos sin beta, o vegas de plazos distintos como si fueran la misma volatilidad.
- Límites de griegas sin límite de escenario. Las griegas son locales; el riesgo de un libro de opciones está en los movimientos grandes.
- Escenarios con la volatilidad fija. En una caída la volatilidad sube, más en los plazos cortos y en los strikes bajos; el signo del efecto depende de la composición del libro.
- Confundir margen con riesgo. El margen de cartera cubre una rejilla fijada por la norma, no la peor caída posible, y sube justo cuando el capital baja.
- Cubrir con margen aislado. Cada pata se liquida por su cuenta; la cobertura desaparece en el peor momento.
- Apalancamiento fijo en cripto. La misma posición está más cerca de la liquidación cuando la volatilidad sube.
Ejercicios
- El 4 de abril, la delta del libro es de +24.871 USD por 1 %. Calcula cuántos futuros sobre ORX con un multiplicador de 50 USD habría que vender para dejarla en cero y qué habría ganado o perdido esa cobertura hasta el cierre del 9 de abril, cuando ORX sube un 9,5 %. Compáralo con el P&L del libro en esos días.
Solución
F3 = pd.Timestamp("2025-04-09")
S2, S3 = idx.at[F2, "cierre"], idx.at[F3, "cierre"]
delta_usd_punto = serie.at[F2, "delta_1pct"] / (0.01 * S2)
futuros = -round(delta_usd_punto / 50)
cobertura = futuros * 50 * (S3 - S2)
libro_pyg = serie.at[F3, "valor"] - serie.at[F2, "valor"]
print(f"Delta: {delta_usd_punto:,.0f} USD por punto de ORX;"
f" futuros: {futuros}")
print(f"P&L del libro del 4 al 9 de abril: {libro_pyg:+,.0f}")
print(f"P&L de la cobertura: {cobertura:+,.0f}")
print(f"Libro cubierto: "
f"{libro_pyg + cobertura:+,.0f}")Delta: 534 USD por punto de ORX; futuros: -11
P&L del libro del 4 al 9 de abril: +160,510
P&L de la cobertura: -341,528
Libro cubierto: -181,018La delta equivale a 534 USD por punto de ORX: 11 futuros vendidos. Del 4 al 9 de abril el libro recupera 160.510 USD y la cobertura pierde 341.528: el libro cubierto pierde 181.018 más. La cobertura cumple el límite y en este caso cuesta caro: vender futuros en el fondo y que el mercado rebote un 9,5 % en un día se lleva más de lo que recupera el libro. No es un argumento contra cubrir, porque la caída podía haber seguido: es el coste de un libro corto de gamma, que obliga a vender después de caer y a comprar después de subir (lección 3.6.2). La forma barata de cumplir los límites es no montar un libro que los rompa en el primer movimiento grande.
- Repite la tabla de liquidaciones con un margen de mantenimiento del 1 % y del 2,5 % (los exchanges suben la tasa de mantenimiento con el tamaño de la posición, por tramos). ¿Cómo cambia la ventaja del margen cruzado?
Solución
filas = {}
for mmr in (0.005, 0.01, 0.025):
MMR = mmr
for L in (5, 10):
filas[(mmr, L)] = liquidaciones(L)
MMR = 0.005
pd.DataFrame(filas).T.rename_axis(["mantenimiento", "apal"]).round(3) largo_XBQ par_aislado par_cruzado
mantenimiento apal
0.005 5 0.175 0.595 0.032
10 0.359 0.909 0.184
0.010 5 0.181 0.608 0.036
10 0.379 0.932 0.214
0.025 5 0.191 0.634 0.052
10 0.430 0.958 0.285Un mantenimiento más alto acerca la liquidación al precio de entrada en las tres variantes: el colchón que queda es aproximadamente. A 10x, con un 2,5 % de mantenimiento el colchón baja del 9,5 % al 7,7 % y las liquidaciones suben en las tres: el largo del 35,9 % al 43,0 %, el par aislado del 90,9 % al 95,8 % y el cruzado del 18,4 % al 28,5 %. El margen cruzado sigue siendo con diferencia la variante que menos se liquida, pero es la que más empeora en proporción: su colchón era el diferencial entre las dos patas, que se mueve poco, y cada punto de mantenimiento se come una parte mayor de él.
- En la tabla de revaloración del 2 de abril, 10 puntos más de volatilidad empeoran el P&L de una caída del 2 % y mejoran el de una caída del 10,6 %. Explica por qué con la composición del libro.
Solución
Con el índice cerca de 5.200, las opciones con más vega son las cercanas al dinero: el straddle 5300 vendido de mayo y la put 5000 vendida de abril. Una subida de la volatilidad sin apenas movimiento les hace perder: el libro está corto de vega cerca del dinero. Tras una caída del 10,6 % el índice está en unos 4.660 y las opciones cercanas al dinero son las puts 4600 compradas de mayo, con 8 contratos y un mes de plazo: ahora la vega del libro es positiva (+1.844 USD por punto el 4 de abril en la tabla de la lección). La vega no es una propiedad fija del libro: depende de dónde esté el subyacente, y un escenario tiene que mover el precio y la volatilidad a la vez.
Respuesta plegada en cada pregunta
Autoevaluación
Un libro tiene una delta de −2.500 USD por 1 % y una gamma de −2.500 USD por un movimiento del 1 %. ¿Qué pierde, según sus griegas, si el subyacente cae un 10 %?
- A25.000 USD: la delta por 10
- B2.500 USD
- CUnos 225.000 USD: −2.500 × (−10) − 2.500 × 10² = 25.000 − 250.000
- DNada: la delta es pequeña
Ver respuesta
Respuesta C. Unos 225.000 USD: −2.500 × (−10) − 2.500 × 10² = 25.000 − 250.000
El término de gamma crece con el cuadrado del movimiento: 100 veces el valor por 1 % en un movimiento del 10 %. Y aun así es una aproximación: en la lección, la revaloración completa da −226.400 USD frente a −285.600 de delta y gamma en una caída del 10,6 %.
¿Por qué el libro de la lección no rompe su límite de gamma en la caída de abril y aun así pierde un 37,9 % del capital en dos sesiones?
- APorque las griegas miden la sensibilidad en el punto de hoy: con la caída, las puts vendidas pasan a estar dentro del dinero, su gamma baja y su delta se dispara; el riesgo de un movimiento grande solo lo mide un escenario
- BPorque la gamma estaba mal calculada
- CPorque el límite de gamma era demasiado estricto
- DPorque las opciones de KRN perdieron mucho
Ver respuesta
Respuesta A. Porque las griegas miden la sensibilidad en el punto de hoy: con la caída, las puts vendidas pasan a estar dentro del dinero, su gamma baja y su delta se dispara; el riesgo de un movimiento grande solo lo mide un escenario
La gamma del libro pasa de −2.210 a −467 USD el 4 de abril mientras la delta sube de +3.521 a +24.871. Un límite de escenario (−10 % con +10 puntos de volatilidad) habría rechazado el libro el mismo día que se montó: −169.983 USD frente a un tope de −100.000.
Con la regla de Cboe para opciones vendidas sin cubrir, la parte de índice del libro exige 1,30 millones de USD; con un margen de cartera por escenarios, 114.500. ¿Qué número mide mejor su riesgo?
- AEl de Cboe, porque es más alto
- BEl de cartera, porque es el que pide el bróker
- CLos dos son iguales en riesgo
- DNinguno: el de Cboe ignora las coberturas y el de cartera cubre una caída del 8 % con la volatilidad de hoy; dos días después, el libro había perdido 189.400 USD
Ver respuesta
Respuesta D. Ninguno: el de Cboe ignora las coberturas y el de cartera cubre una caída del 8 % con la volatilidad de hoy; dos días después, el libro había perdido 189.400 USD
El margen es una regla de garantía, no una medida de riesgo. El de cartera de la lección cubre −8 % a +6 % con la volatilidad constante; la caída fue del 10,6 % con la volatilidad al alza, y la pérdida superó en un 50 % el margen de la víspera.
Un par largo XBQ y corto XEQ con el mismo nominal y apalancamiento 5. ¿Por qué se liquida en el 59,5 % de las entradas con margen aislado y en el 3,2 % con margen cruzado?
- APorque el margen cruzado no tiene mantenimiento
- BPorque con margen aislado cada pata tiene su propio colateral y se liquida con su propio movimiento; con margen cruzado responde todo el saldo y las dos patas se compensan (correlación 0,77)
- CPorque XEQ es más volátil
- DPor la comisión de liquidación
Ver respuesta
Respuesta B. Porque con margen aislado cada pata tiene su propio colateral y se liquida con su propio movimiento; con margen cruzado responde todo el saldo y las dos patas se compensan (correlación 0,77)
En aislado, una pata puede liquidarse aunque la otra gane lo mismo, y queda la otra sin cobertura. En cruzado, lo que importa es el valor del par, que se mueve mucho menos que cada pata.
Para un largo de XBQ a 10x, la aproximación normal (principio de reflexión) prevé un 48,9 % de liquidaciones en 28 días y en los datos son un 36,5 %; a 2x prevé un 0,0 % y son un 1,0 %. ¿Qué indica?
- AQue la fórmula está mal
- BQue la normal infravalora la cola: falla poco donde el umbral está cerca y mucho, en proporción, donde el umbral está a varias desviaciones
- CQue el apalancamiento no importa
- DQue hay que usar el último precio en vez del mark
Ver respuesta
Respuesta B. Que la normal infravalora la cola: falla poco donde el umbral está cerca y mucho, en proporción, donde el umbral está a varias desviaciones
A 2x la liquidación está a 4,6 desviaciones típicas de 28 días: con una normal no pasa nunca, con colas gruesas pasa una vez de cada cien. A 10x, la deriva alcista de la muestra y la reversión de los movimientos reducen las liquidaciones frente a la fórmula sin deriva.
Ideas clave
- Las griegas de un libro se suman en dinero: delta y gamma en USD por 1 % del subyacente, vega en USD por punto y theta en USD por día. La delta de subyacentes distintos se pondera por beta y la vega de plazos distintos no se suma sin más.
- Un límite de griegas mide la sensibilidad en el punto de hoy. En la caída de abril de 2025 de los datos, la gamma del libro cayó y la delta se multiplicó por siete: el libro perdió un 37,9 % del capital en dos sesiones sin romper su límite de gamma.
- La aproximación de Taylor falla en los movimientos grandes en las dos direcciones: con −15 % sobrestima la pérdida (−550.000 frente a −373.000 USD) y con +10 % la subestima. Los límites de riesgo de un libro de opciones se fijan con escenarios revalorando cada opción.
- El margen no mide el riesgo: la regla de Cboe sin cubrir ignora las coberturas (1,30 millones) y el margen de cartera cubre −8 % con la volatilidad de hoy (114.500). La pérdida de dos sesiones superó el margen de cartera de la víspera.
- Con margen aislado, un par cubierto se liquida por la pata que pierde aunque la otra gane: a 5x, un 59,5 % de las entradas frente al 3,2 % con margen cruzado.
- La distancia a la liquidación en desviaciones típicas es un buen límite, pero con colas gruesas la normal infravalora las liquidaciones lejanas.