Del gamma de una sola opción a toda la cadena de opciones del SPX: gamma neto, el gamma flip y los call y put walls, calculados sobre 19.414 contratos reales, y luego la única regla de trading que ejecuta un bot de GEX sobre la API de Interactive Brokers.
Los dealers de opciones cubren las opciones que venden. Cuando el índice se mueve, su delta cambia según el gamma, y operan el subyacente para mantenerse neutrales. El gamma exposure suma eso en todos los contratos: cuántos dólares del S&P 500 tienen que comprar o vender los dealers por cada 1% de movimiento del índice.
Lo que importa es su signo. Cuando los dealers están largos en gamma, venden en las subidas y compran en las caídas, lo que amortigua el mercado. Cuando están cortos en gamma, compran en las subidas y venden en las caídas, lo que amplifica los movimientos. El GEX se volvió popular con el auge de las opciones de cero días: en julio de 2026, un récord de dos tercios del volumen de opciones del SPX venció el mismo día en que se negoció, así que este flujo de cobertura ya es una fuerza real sobre el índice.
Paso a paso, la página construye:
Los números de abajo salen de la cadena del 18 de septiembre de 2026 a las 13:10, hora de Nueva York, un vencimiento mensual. Solo se necesitan numpy, pandas y matplotlib. Lee las limitaciones antes de citar un número de aquí. El lado del dealer es un supuesto, no un dato.
El video recorre cada paso en un dashboard en vivo y luego ejecuta el motor de trading en una cuenta de paper trading de Interactive Brokers: régimen, decisión, orden, ejecución y resultado.
Publico los modelos a medida que los construyo, los cambios que les hago y los resultados que no funcionaron. Más cortos que estos artículos, y son muchos más.
Sígueme en XLas secciones de abajo calculan el gamma exposure paso a paso, con las fórmulas, los gráficos y el código. Las acompañan dos repositorios: el motor de trading, con el régimen, la decisión y la ejecución en Interactive Brokers, y los gráficos y modelos, incluido el análisis del vencimiento del mismo día.
Todo lo de esta página, funcionando a la vez: el tape del SPX, la profundidad por strike con gamma y GEX de calls y puts, el gamma neto según el spot con el flip y ambos walls, y el P&L de una estrategia con su matriz de correlación. Un modo de sensibilidad mueve el spot, la volatilidad implícita, el tiempo al cierre y el open interest del mismo día, y muestra cómo se desplazan el flip y los walls. Corre sobre una cadena de Cboe almacenada con un tape simulado.
El gamma mide qué tan rápido cambia el delta de una opción cuando se mueve el índice. Black-Scholes lo da en forma cerrada:
Γ = φ(d1) / (S σ √T) con d1 = [ ln(S/K) + (r + σ²/2) T ] / (σ √T)Multiplícalo por el open interest y por 100 acciones por contrato y obtienes cuánto cambia el delta del libro de los dealers por punto. Multiplícalo por S² × 1% y se convierte en dólares de índice a operar por cada 1% de movimiento. Los calls entran con signo positivo y los puts con signo negativo, bajo el supuesto estándar de que los dealers están largos en los calls y cortos en los puts que operan los clientes:
GEX = Γ × OI × 100 × S² × 0,01 × (+1 para calls, −1 para puts)gex.py
R = 0.04 # flat rate for Black-Scholes
MULTIPLIER = 100 # shares per SPX contract
MIN_T = 4 / (365 * 24) # floor of four hours, so a contract expiring today stays finite
def bs_gamma(S, K, T, sigma):
"""Black-Scholes gamma, vectorised over K, T and sigma."""
T = np.maximum(np.asarray(T, float), MIN_T)
sigma = np.maximum(np.asarray(sigma, float), 1e-4)
d1 = (np.log(S / np.asarray(K, float)) + (R + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
return np.exp(-0.5 * d1 ** 2) / (np.sqrt(2 * np.pi) * S * sigma * np.sqrt(T))
def gex(S, K, T, sigma, oi, is_call):
"""Dollars dealers trade per 1% move: calls positive, puts negative (dealers long calls, short puts)."""
sign = np.where(is_call, 1.0, -1.0)
return sign * bs_gamma(S, K, T, sigma) * oi * MULTIPLIER * S ** 2 * 0.01Un contrato real, el call más grande en el call wall: el call 8.100 con vencimiento el 2026-12-31, open interest de 41.378, volatilidad implícita de 12,1%, con el spot en 7.637,76.
0,000632 × 41.378 × 100 × 7.637,76² × 0,01 = $1,53 mil millonesEsa sola línea de la cadena significa que los dealers operan cerca de $1,5 mil millones del S&P por cada 1% que se mueve el índice.
Cboe publica la cadena completa del SPX como un único archivo JSON gratuito, con cada strike y vencimiento, su interés abierto y su volatilidad implícita, con unos 15 minutos de retraso. Los nombres de los contratos siguen el formato OCC, así que una expresión regular recupera vencimiento, tipo y strike. Descarto los contratos sin interés abierto, sin volatilidad o con strikes a más del 50% del spot.
fetch_chain.py
CHAIN = "https://cdn.cboe.com/api/global/delayed_quotes/options/_SPX.json"
OCC = re.compile(r"^(?P<root>[A-Z]+)(?P<ymd>\d{6})(?P<side>[CP])(?P<strike>\d{8})$")
def fetch_chain():
raw = json.loads(urllib.request.urlopen(
urllib.request.Request(CHAIN, headers={"User-Agent": "gex-research"}), timeout=90).read())
rows = []
for o in raw["data"]["options"]:
m = OCC.match(o["option"])
if m:
rows.append(dict(expiry=date(2000 + int(m["ymd"][:2]), int(m["ymd"][2:4]), int(m["ymd"][4:])),
is_call=m["side"] == "C", strike=int(m["strike"]) / 1000,
oi=float(o["open_interest"]), iv=float(o["iv"])))
df = pd.DataFrame(rows)
spot = float(raw["data"]["current_price"])
df["T"] = [(e - date.today()).days / 365 for e in df.expiry]
keep = (df["T"] >= 0) & (df.oi > 0) & (df.iv > 0.01) & (np.abs(np.log(df.strike / spot)) < 0.5)
return df[keep].reset_index(drop=True), spot, raw.get("timestamp", datetime.now().isoformat(timespec="minutes"))El 18 de septiembre de 2026 a las 13:10 eso devuelve 19.414 contratos en 56 vencimientos, con el SPX en 7.637,76.
La gamma neta es la suma del GEX de todos los contratos:
Net GEX(S) = Σi signoi × Γi(S) × OIi × 100 × S² × 0.01net_gamma.py
def net_gamma(df, S):
x = gex(S, df.strike, df["T"], df.iv, df.oi, df.is_call)
return x.sum(), x| Libro | GEX |
|---|---|
| Todas las calls | +242.3 |
| Todos los puts | −233.0 |
| Gamma neta | +9.4 |
Dos libros de unos 240 mil millones cada uno se cancelan casi por completo, y lo que queda, +9,4 mil millones, es positivo: los dealers están largos en gamma a este spot. Aunque parezca pequeño frente a cualquiera de los dos lados, el signo de ese residuo es lo que decide si la cobertura amortigua o amplifica el próximo movimiento.
Agrupa los mismos números por strike. Por encima del spot, el strike con el mayor GEX positivo es el call wall. Por debajo del spot, el strike con el GEX más negativo es el put wall. Marcan dónde se concentra la mayor parte de la cobertura de los dealers.
walls.py
def walls(df, per_contract, spot):
by_strike = pd.Series(per_contract).groupby(df.strike).sum()
above, below = by_strike[by_strike.index > spot], by_strike[by_strike.index < spot]
return by_strike, float(above.idxmax()), float(below.idxmin())
Por debajo del spot el libro está casi todo en rojo: domina el lado de los puts. Por encima, mandan las calls. Aquí el call wall está en 8.100 y el put wall en 7.500.
La gamma neta solo vale al precio de hoy. Revalúa cada contrato como si el índice estuviera en otro nivel, vuelve a sumar y repite en una grilla de niveles: así se dibuja la gamma neta a lo largo del spot. Donde cruza cero está el gamma flip:
S* tal que Net GEX(S*) = 0gamma_flip.py
def profile(df, spot, lo=0.90, hi=1.10, n=161):
"""Net gamma at every hypothetical spot: re-price every contract, add again."""
grid = np.linspace(spot * lo, spot * hi, n)
total = np.array([net_gamma(df, S)[0] for S in grid])
return grid, total
def flip_level(grid, total, spot):
"""Zero crossing of the profile closest to spot, by linear interpolation."""
best = None
for i in range(len(grid) - 1):
a, b = total[i], total[i + 1]
if (a < 0 <= b) or (a > 0 >= b):
x = grid[i] + (grid[i + 1] - grid[i]) * (-a) / (b - a)
if best is None or abs(x - spot) < abs(best - spot):
best = x
return best
Aquí el flip está en 7.631, y el SPX en 7.638 está solo 7 puntos por encima. Justo por encima del flip, los dealers están largos en gamma, pero apenas: una caída de 0,1% los dejaría cortos, y la misma cobertura que hoy amortigua los movimientos empezaría a amplificarlos.
La mayoría de las reglas de GEX en internet tratan el flip y los walls como soporte y resistencia. Probadas día a día, no se sostuvieron. Una regla sí, y viene de la investigación: Baltussen, Da, Lammers y Martens, Hedging demand and market intraday momentum, Journal of Financial Economics, 2021. La última media hora de negociación continúa la dirección del día, y en el S&P 500 solo cuando los dealers están cortos en gamma. Barbon y Buraschi, Gamma Fragility (2021), encuentran que los movimientos tienden a revertirse cuando los dealers están largos en gamma.
| Gamma de los dealers al cierre anterior | Mercado al alza | Mercado a la baja |
|---|---|---|
| Corto en gamma | comprar | vender |
| Largo en gamma | vender | comprar |
Siempre del mismo lado que la cobertura del dealer. Un día plano significa no operar.
| Prueba | Sharpe |
|---|---|
| Regla anterior | 1.37 |
| Siempre a favor del día, ignorando la gamma | −2.00 |
| Misma regla en QQQ | 0.88 |
La gamma hace todo el trabajo: la misma operación sin ella pierde dinero. La ventaja es pequeña, eso sí: unos 3 puntos básicos por día antes de costos, que desaparecen con 3 puntos básicos de costos, y solo 26 de 723 sesiones abrieron cortas en gamma. Para el régimen, el motor usa la serie diaria gratuita de SqueezeMetrics, la misma sobre la que se construyó la investigación, y muestra el número de Cboe de arriba como verificación cruzada, ya que los dos pueden diferir en signo. El código del motor, con la ejecución en Interactive Brokers, está en el repositorio.
Inferir la gamma de los dealers a partir de gamma × interés abierto es discutible, y el punto débil es el signo del dealer. El interés abierto cuenta cuántos contratos están abiertos, no quién tiene cada lado. Todo contrato tiene un comprador y un vendedor, y la fórmula simplemente supone que los dealers están del lado opuesto a los clientes, que por convención venden calls (covered calls, collars) y compran puts (protección). Esa suposición es el +1 para calls, −1 para puts de la fórmula:
GEXi = signo del dealeri × Γi × OIi × 100 × S² × 0.01 con signo del dealer = +1 (call), −1 (put)Cuando los clientes compran calls, como en un rally especulativo o en opciones de cero días, o venden puts para generar ingresos, los dealers tienen el lado opuesto y el signo real de ese contrato se invierte. Nada en el interés abierto lo muestra. También faltan las operaciones del mismo día, ya que el interés abierto solo se actualiza de un día para otro.
Por eso el dashboard y los pasos 1 a 5 usan esta estimación para explicar la mecánica: es simple y se puede ver strike por strike. Nuestro motor de trading no la usa. Nunca calcula el GEX a partir de la cadena. Lee un único número publicado por día, la gamma neta del SPX de SqueezeMetrics al cierre anterior, y se queda solo con su signo:
régiment = signo( GEX de SqueezeMetrics al cierre del día t−1 ) rt = MES a las 15:30 / MES a las 09:30 − 1 posiciónt = +signo(rt) si corto en gamma (seguir) −signo(rt) si largo en gamma (ir en contra) contratos = floor( capital / (5 × precio MES) )Para ser claro, el propio white paper de SqueezeMetrics parte de los mismos supuestos sobre los dealers, así que el motor no escapa del debate. Cambian tres cosas. Es una sola serie pública, fijada antes de que abra la sesión, así que no hay look ahead. Usa solo el signo, nunca niveles como el flip o los walls, que es donde un signo equivocado en unos pocos strikes grandes hace más daño. Y la regla se sometió a backtest sobre esa misma serie, así que cualquier error que arrastre el supuesto ya está dentro del resultado de arriba en lugar de quedar oculto. Nuestro número de Cboe se muestra al lado solo como verificación cruzada; el día del video los dos diferían en signo.
Todos los números hasta ahora dejan el reloj quieto. Las opciones de cero días hacen lo contrario: un contrato del mismo día pierde horas, y su gamma, que solo existe alrededor de su strike, se vuelve más angosta y más alta a medida que se acerca el cierre. Este paso mantiene fijos el spot, las volatilidades implícitas y todos los demás vencimientos, envejece solo el libro del mismo día desde 6,5 horas hasta 0,1 horas para la expiración, y revalúa toda la cadena en cada nivel del SPX cada vez.
De aquí en adelante, los números vienen de una segunda corrida, el 1 de octubre de 2026, 13:38 Nueva York, con un vencimiento del mismo día activo: 20.034 contratos en la cadena, 363 de ellos con vencimiento ese día y 402.723 contratos de interés abierto, con el SPX en 7.662. Las cotizaciones del mismo día sin volatilidad utilizable toman prestada la volatilidad del siguiente vencimiento en el mismo strike. El código y los gráficos están en el repositorio de gráficos y modelos.
dealer_gamma_aged.py
def gamma(S, K, T, sigma):
"""Black-Scholes gamma (rate set to zero, it barely moves gamma over hours)."""
srt = np.maximum(sigma, 1e-4) * np.sqrt(np.maximum(T, 0.05 / YEAR_H))
d1 = np.log(S / K) / srt + 0.5 * srt
return np.exp(-0.5 * d1 ** 2) / (np.sqrt(2 * np.pi) * S * srt)
def dealer_gex(df, iv, S, hours, sign=None):
"""$ dealers trade per 1% SPX move, per contract. Default sign: dealers long calls, short puts.
Same day contracts get `hours` to expiration, every other expiry keeps its days."""
T = np.where(df.same_day, hours / YEAR_H, df.days / 365)
sign = np.where(df.is_call, 1.0, -1.0) if sign is None else sign
return sign * gamma(S, df.strike.to_numpy(), T, iv) * df.oi.to_numpy() * MULTIPLIER * S * S * 0.01
def profile(df, iv, grid, hours, sign=None):
"""Net dealer gamma at every hypothetical SPX level: re-price the whole book, add it up."""
return np.array([dealer_gex(df, iv, S, hours, sign).sum() for S in grid])
def flip(grid, total, spot):
"""Zero crossing of the profile closest to spot."""
best = None
for i in range(len(grid) - 1):
a, b = total[i], total[i + 1]
if (a < 0 <= b) or (a > 0 >= b):
x = grid[i] + (grid[i + 1] - grid[i]) * (-a) / (b - a)
best = x if best is None or abs(x - spot) < abs(best - spot) else best
return best
| Horas al vencimiento | Gamma flip | Gamma de los dealers en el spot |
|---|---|---|
| 6.5 | 7,680 | −23.2 |
| 3 | 7,680 | −24.2 |
| 1 | 7,682 | −24.2 |
| 0.25 | 7,684 | −24.0 |
| 0.1 | 7,686 | −23.7 |
Este día el flip apenas se desplaza de 7.680 a 7.686, y la gamma en el spot casi no cambia, porque la mayor parte del libro está en otros vencimientos. Donde sí se nota el vencimiento del mismo día es en la forma: en los últimos minutos la curva se vuelve irregular, y cada ondulación es un strike del mismo día cuya gamma se comprimió en un pico de pocos puntos de ancho.
Si separo solo el vencimiento del mismo día por strike, se ve dónde está su gamma. Con horas por delante se reparte en más de cien puntos de strikes. Cerca del cierre solo conservan gamma los strikes a pocos puntos del spot: la gamma de una opción en el spot se desvanece cuando el spot está a más de una desviación estándar del movimiento restante de su strike, y con seis minutos por delante eso son unos pocos puntos.
same_day_by_strike.py
def same_day_by_strike(df, iv, spot, hours):
sd = df.same_day.to_numpy()
x = pd.Series(dealer_gex(df, iv, spot, hours)[sd]).groupby(df.strike[sd].to_numpy()).sum()
return x / 1e9
Bajo la convención de dealers, dominan los puts del mismo día alrededor del spot, por eso la mayoría de las barras apuntan hacia abajo.
El GEX se expresa por movimiento de 1%: gamma por S² por 1%, una línea recta trazada a partir de la gamma. A semanas del vencimiento esa línea funciona. Cerca del vencimiento se rompe. En el mayor strike del mismo día, 7.650 con 6.177 contratos, la cifra de 1% sube de $3,4 mil millones con 6,5 horas por delante a $27,4 mil millones con 0,1 horas. No puede existir una cobertura de ese tamaño: el strike completo, con cada contrato a delta plena, equivale a 6.177 × 100 × 7.650 = $4,73 mil millones de SPX.
La cifra exacta es el cambio de delta ante un movimiento realista. Para un movimiento de 0,25% desde el strike, más o menos lo que hace el SPX en una última hora:
cobertura = Σ OI × 100 × K × [ N(d1 en K(1 + 0,25%)) − N(d1 en K) ]hedge_for_move.py
MOVE = 0.0025 # a 0.25% move, about what SPX does in a last hour
def hedge_for_move(df, iv, K, hours, move=MOVE):
"""$ of SPX dealers must trade at strike K if SPX moves `move` off it: the exact change in delta.
It can never pass the strike's whole delta, contracts x 100 x K."""
m = (df.same_day & (df.strike == K)).to_numpy()
srt = iv[m] * np.sqrt(hours / YEAR_H)
d_on, d_off = 0.5 * srt, np.log(1 + move) / srt + 0.5 * srt
return float(((norm.cdf(d_off) - norm.cdf(d_on)) * df.oi.to_numpy()[m] * MULTIPLIER * K).sum())
def linear_one_pct(df, iv, K, hours):
"""The usual GEX figure at the strike: gamma x S squared x 1%. Fine far from expiry, it explodes close to it."""
m = (df.same_day & (df.strike == K)).to_numpy()
return float((gamma(K, K, hours / YEAR_H, iv[m]) * df.oi.to_numpy()[m] * MULTIPLIER * K * K * 0.01).sum())
| Medida | A 6,5 horas | A 0,1 horas |
|---|---|---|
| Cifra habitual, gamma × movimiento de 1% | 3.4 | 27.4 |
| Cobertura exacta para un movimiento de 0,25% | 0.82 | 2.36 |
| Strike completo, contratos × 100 × strike | 4.73 | 4.73 |
La cobertura real sube a medida que se acerca el cierre y luego se aplana, porque un movimiento de 0,25% puede llevar como máximo una opción at the money del mismo día de delta 0,5 a casi 1. Cerca del vencimiento, lea cualquier cifra de GEX por 1% como un límite superior que la cobertura real nunca alcanza.
SqueezeMetrics publica gratis su propia estimación diaria de GEX desde 2011. Cada punto abajo es un día: el GEX de ese día frente al movimiento del SPX del día siguiente, de cierre a cierre. Su estimación tiene su propio método y escala, así que sus números no son comparables con las cifras de la cadena de opciones anteriores.
gex_next_day.py
SQUEEZE = "https://squeezemetrics.com/monitor/static/DIX.csv"
def squeeze_history():
"""SqueezeMetrics GEX (their own positioning estimate, their own scale) and the next close to close SPX move."""
raw = urllib.request.urlopen(urllib.request.Request(SQUEEZE, headers={"User-Agent": "gex-research"}),
timeout=60).read().decode()
sq = pd.read_csv(io.StringIO(raw), parse_dates=["date"])
sq["next"] = sq.price.pct_change().shift(-1) * 100
sq["gex_bn"] = sq.gex / 1e9
return sq.dropna(subset=["next"]).reset_index(drop=True)
def spreads(sq):
short, long_ = sq[sq.gex < 0], sq[sq.gex > 0]
calm = short[~short.date.dt.year.isin([2020, 2022])]
return dict(days=len(sq), short_days=len(short), short=float(short.next.std()), long=float(long_.next.std()),
short_without_2020_2022=float(calm.next.std()))
| Días | Cantidad | Movimiento del día siguiente |
|---|---|---|
| GEX negativo (dealers cortos en gamma) | 347 | ±2.25% |
| GEX positivo (dealers largos en gamma) | 3,529 | ±0.89% |
| GEX negativo, sin 2020 ni 2022 | ±1.77% |
En los días de GEX negativo, el día siguiente se movió unas 2,5 veces más. Parte de eso se debe a años volátiles: sin 2020 ni 2022 la dispersión es de ±1,77%, todavía cerca del doble que en los días de GEX positivo. El GEX y la volatilidad se mueven juntos, y este gráfico muestra la asociación sin separar ambos.
Cada cifra de GEX en esta página supone que los dealers están largos en calls y cortos en puts. El libro del mismo día es donde ese supuesto es más débil: el flujo de cero días va en ambas direcciones, y muchos traders venden puts del mismo día. Cambiando el signo solo del libro del mismo día, a 6,5 horas del vencimiento, con todo lo demás igual:
sign_scenarios.py
def sign_scenarios(df, iv, spot, hours):
"""Dealer gamma at spot under three views of who holds the same day book."""
std = np.where(df.is_call, 1.0, -1.0)
sd = df.same_day.to_numpy()
netted = np.where(sd, 0.0, std) # same day flow two way, nets to zero
puts_sold = np.where(sd & ~df.is_call.to_numpy(), 1.0, std) # customers sold the same day puts
return {name: float(dealer_gex(df, iv, spot, hours, s).sum() / 1e9) for name, s in
(("convention", None), ("same_day_nets_to_zero", netted), ("same_day_puts_sold", puts_sold))}| Quién tiene el libro del mismo día | Gamma de los dealers |
|---|---|
| Convención: dealers largos en calls, cortos en puts | −23.2 |
| Flujo del mismo día en ambos sentidos, neto cero | −14.3 |
| Los clientes vendieron los puts del mismo día | +31.0 |
Misma cadena de opciones, mismos precios, y la gamma de los dealers en el spot queda en cualquier punto entre −23,2 y +31,0 mil millones según quién tenga el libro del mismo día. Lea el signo de cualquier cifra de GEX, incluida esta, como una estimación.