DEV Community

Cover image for Backtesting Futures Gamma (ES & NQ): Historical GEX Done Right
tomasz dobrowolski
tomasz dobrowolski

Posted on • Originally published at flashalpha.com

Backtesting Futures Gamma (ES & NQ): Historical GEX Done Right

"Can I backtest futures gamma?" is one of the most common questions we get from quant desks, and the honest answer has two parts.

First: FlashAlpha serves live gamma exposure on ES and NQ futures today, computed properly on the options-on-futures chains. Second: the historical replay engine does not yet cover the futures symbols, so /v1/exposure/gex/ES=F?at=... returns no data. That is a real limitation — and this article is about the fact that it is not the blocker it looks like.

The reason is structural. The dealer gamma that pins or unpins ES is the gamma of the entire S&P 500 options complex — SPX index options, SPY ETF options, and the ES options-on-futures — hedged by the same desks against the same index. That complex has a deep, clean, minute-resolution history. So the rigorous move is to backtest the regime on the cash-index history and trade it on the live future, rather than wait for a thin native futures tape.

TL;DR — the split:

Phase Host Symbols
Research / backtest historical.flashalpha.com SPY (for ES), QQQ (for NQ), since April 2018
Live execution lab.flashalpha.com ES=F, NQ=F

Same response shape on both hosts, so the same code path works for backtest and live.

Why There Is No Native Futures GEX History (Yet)

Computing historical gamma exposure needs a point-in-time options chain with open interest, strikes, and quotes at every past minute. For US equities and index options that history is deep and well-kept, which is why FlashAlpha can replay SPY, SPX, QQQ, and thousands of names back to 2018. Options-on-futures history is a different, sparser story across the industry, and FlashAlpha's replay engine does not yet ingest it. Concretely:

  • Live ES=F and NQ=F exposure works now — /v1/exposure/gex/ES%3DF returns the full live surface.
  • Historical ES=F / NQ=F does not — a ?at= replay on a futures symbol returns no_data.
  • The cash-index proxies — SPY, SPX, QQQ, NDX — replay cleanly at minute resolution since 2018-04-16.

Rather than pretend otherwise, the workflow below leans on what is actually true and testable.

The Cash-Index Proxy: ES Is the SPX Complex, NQ Is the NDX Complex

An ES future is a financed claim on the S&P 500; an NQ future is a financed claim on the Nasdaq-100. The options that hedge them belong to the same index complex, so the gamma regime — positive versus negative dealer gamma, where the flip sits, where the walls cluster — is shared. That is what makes the proxy sound: you are not substituting an unrelated instrument, you are reading the same dealer book from its most liquid, best-recorded venue.

  • ES ← SPY / SPX. The E-mini S&P 500 tracks the same index as SPY and SPX. Historical SPY (or SPX) gamma regime is the ES gamma regime, minus a basis on the price axis.
  • NQ ← QQQ / NDX. The E-mini Nasdaq-100 tracks the same index as QQQ and NDX. Historical QQQ (or NDX) gamma regime is the NQ gamma regime, minus a basis.

What transfers cleanly: gamma regime (positive/negative), the timing of flips, relative wall placement, dealer-hedging direction, and the shape of the exposure profile. These are properties of the index dealer book, not of one venue.

What needs a translation: the price of each level. Cash-index strikes are on the index; ES/NQ trade at a basis to cash. A level is correct in regime terms but must be shifted onto the futures price before you act on it.

Translating Levels Through the Basis

The only adjustment the proxy needs is on the price axis. The future trades at a basis to the cash index:

F = S + Basis        (Basis = F − S)
Enter fullscreen mode Exit fullscreen mode

where F is the ES (or NQ) future and S is the cash index (SPX or NDX). The basis reflects financing minus dividends to the contract's expiry and drifts toward zero into the quarterly roll. So a gamma flip your backtest found at an SPX level maps to an ES level by adding the current basis:

ES level = SPX level + Basis(now)
Enter fullscreen mode Exit fullscreen mode

If you backtest on SPY rather than SPX, first scale by roughly ten (SPY is about 1/10th of SPX) to reach index points, then apply the basis. The /futures/es page prints the live basis so you do not have to compute it by hand, and the ES fair value and basis explainer covers the mechanics in depth.

Why this is enough: dealer gamma is a statement about where hedging flips sign, which is a property of the index, not the wrapper. The basis moves the number on the x-axis; it does not change whether dealers are long or short gamma. Get the regime from the deep cash history, get the exact price from the live future and its basis.

How To Backtest It: Signal on the Cash History

The historical host mirrors the live exposure endpoints and adds an ?at= timestamp (ET). Pull SPY (for ES) or QQQ (for NQ) at any past minute and you get the same response shape as live — net_gex, the gamma flip, the walls, and the per-strike breakdown:

# Replay SPY dealer gamma at a past timestamp (ET) - the ES proxy
curl -H "X-Api-Key: YOUR_KEY" \
  "https://historical.flashalpha.com/v1/exposure/gex/SPY?at=2026-06-13T15:55:00"

# NQ backtests off QQQ
curl -H "X-Api-Key: YOUR_KEY" \
  "https://historical.flashalpha.com/v1/exposure/gex/QQQ?at=2026-06-13T15:55:00"
Enter fullscreen mode Exit fullscreen mode

To build a strategy signal, walk a date range and tag each day's dealer-gamma regime from the sign of net GEX. That regime series is your ES signal:

import requests

HOST = "https://historical.flashalpha.com/v1/exposure/gex/SPY"
HEADERS = {"X-Api-Key": "YOUR_KEY"}

def net_gex_at(day):
    r = requests.get(HOST, params={"at": f"{day}T15:55:00"}, headers=HEADERS)
    r.raise_for_status()
    return r.json()["net_gex"]

# Tag each session's regime - the signal you would have traded ES on
for day in ["2026-06-08", "2026-06-09", "2026-06-10"]:
    g = net_gex_at(day)
    regime = "positive-gamma (pin)" if g > 0 else "negative-gamma (trend)"
    print(day, regime, f"net_gex={g:,.0f}")
Enter fullscreen mode Exit fullscreen mode

From there it is an ordinary backtest: join the regime series to ES returns, test the rule (for example, "fade range in positive gamma, stand aside or follow trend in negative gamma"), and measure the edge. Because the historical and live responses are identical in shape, the same parsing code you write here runs unchanged against the live future in production. For the general historical-GEX backtesting workflow and field reference, see the historical GEX API guide.

The workflow in five steps:

  1. Pick the proxy. SPY (or SPX) for ES, QQQ (or NDX) for NQ.
  2. Replay the regime. Loop ?at= over your test window and record net GEX, the flip, and the walls at each point.
  3. Build the signal. Turn the exposure series into your rule — regime sign, distance to flip, proximity to a wall.
  4. Join to futures returns. Test the rule against ES/NQ price action; the regime is shared, so the signal transfers.
  5. Translate levels for execution. Shift any price level onto the future with the current basis.

Execute on the Live Futures Book

When the backtested rule fires today, read the actual dealer gamma off the live future — this is where the options-on-futures chain, Black-76 pricing, and the correct CME multiplier ($50/pt for ES, $20/pt for NQ) matter for the exact levels:

# Live ES gamma for the current session (URL-encode '=' as %3D)
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/ES%3DF"

# Live NQ
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/NQ%3DF"
Enter fullscreen mode Exit fullscreen mode

So the division of labour is clean: the cash history gives you the tested edge, the live future gives you the exact, execution-grade levels on the instrument you actually trade. The live ES and NQ surfaces are documented in GEX on ES & NQ futures, and the two books are compared side by side in ES futures vs SPY/SPX gamma.

What The Proxy Can and Cannot Tell You

Being explicit about the edges keeps the backtest honest:

  • It is not the ES options-on-futures tape. ES has its own open interest and its own 0DTE behavior. The regime transfers; the exact ES per-strike OI in the past does not. For strategies that hinge on ES-specific micro-positioning, treat the proxy as regime context, not ground truth.
  • The overnight session is only on the future. ES gamma is live through the near-24-hour Globex session; the cash proxy is a regular-hours book. Backtest the regime on RTH cash, but remember the live future is what is active at 4 a.m. ET.
  • The basis is time-varying. Use the basis as of each point in time, not a constant, especially across a quarterly roll.
  • Native futures replay is on the roadmap. When options-on-futures history lands in the replay engine, the same ?at= pattern will work directly on ES=F / NQ=F and this proxy step becomes optional.

Wrapping Up

Backtesting futures gamma is a solved problem if you frame it correctly: options-on-futures history is thin, but ES gamma is the S&P 500 complex's gamma and NQ gamma is the Nasdaq-100's, so you research the edge on the deep cash-index history (SPY/SPX for ES, QQQ/NDX for NQ, minute-resolution since April 2018), translate levels through the basis, and execute on the live ES=F / NQ=F book.

See live futures gamma on /futures/es and /futures/nq, and the API docs for the full field reference on both hosts.

Questions on the replay engine or the proxy approach? Drop a comment or find us on Discord.

Top comments (0)