TL;DR: Every GEX tool signs gamma the same way: calls positive, puts negative. That answers "what is the market's structural gamma?" It does not answer "are dealers actually long or short gamma from today's flow?" FlashAlpha's new ?polarity=flow mode answers the second question by signing each strike from the measured dealer position on today's classified tape. Untraded strikes contribute exactly zero. Defaults unchanged.
Originally published on FlashAlpha Research.
Two ways to sign dealer gamma
Every gamma exposure number rests on one hidden choice: what sign to attach to each strike's gamma. Almost every tool makes the same choice, and it smuggles in an assumption: that dealers sit on the opposite side of all customer positioning in one fixed way, forever.
| Mode | How each strike is signed | What it answers |
|---|---|---|
convention (default) |
Calls positive, puts negative | What is the market's structural gamma? |
flow |
Measured dealer position from today's classified session trades | Are dealers actually long or short gamma today? |
The first is a property of open contracts. The second is a property of the live tape. You want both, and now the same endpoints serve either, selected with one parameter. Omit it (or pass ?polarity=convention) and you get the existing behaviour, byte for byte. Anything else returns 400 {"error":"invalid_polarity"}.
How flow polarity is computed
The computation is deliberately simple and fully mechanical. For each strike, on both the call and put side:
dealer_pos = -(customer_buys - customer_sells)
= customer_sells - customer_buys
GEX_strike = dealer_pos * gamma * multiplier * spot^2 * 0.01
where the multiplier is 100 for equity options and the 0.01 scales to a 1% move.
The logic is the market-maker's mirror. When customers buy options, dealers end up short those options, and short gamma. When customers sell, dealers end up long, and long gamma. So dealer_pos is simply the negative of net customer buying, accumulated from the 09:30 ET open, starting at zero.
How trades are classified
Buy/sell tagging is a quote-rule aggressor classification against the concurrent NBBO, a Lee-Ready-style method:
- A print near the ask is buyer-initiated
- A print near the bid is seller-initiated
- A print in the middle of the spread is indeterminate and excluded
There is no tick-rule fallback and no midpoint imputation. If the aggressor is ambiguous, the trade simply does not contribute.
What it explicitly does NOT use
| Not used | Why it matters |
|---|---|
| Open interest | The surface is built from classified volume alone. No settled OI, no effective-OI simulator, no open/close confidence weights. |
| Opening-position estimates | Dealer position starts from zero at the open and accumulates only measured trades. Nothing is assumed about yesterday's book. |
| Decay heuristics | Nothing ages or re-weights past trades within the session. It is a straight running sum. |
Three clean properties fall out:
-
Untraded strikes contribute exactly 0. No classified session trades means
dealer_pos = 0and zero gamma contribution. - Midpoint trades are dropped, not guessed.
- Position is session-cumulative from the open, so it measures the day's dealer inventory change, not the absolute book.
That is the whole point: the signal moves when dealers actually trade, not when a model decides positions should have changed. One practical consequence worth knowing: because only traded strikes count, the flow-signed surface can look sparser than the convention surface on quiet names or early in the session. That sparseness is information, not a bug.
Reading net_gex
Once each strike is signed by dealer position, the sign of the aggregate live_net_gex is the whole message.
net_gex > 0, dealers net LONG gamma. Dealers hedge against the move: sell rallies, buy dips. Volatility compresses and price tends to pin around the gamma flip. The classic mean-reverting, low-realised-vol regime.
net_gex < 0, dealers net SHORT gamma. Dealers hedge with the move: chase price up, sell it down, amplifying the trend. Volatility expands and larger directional moves become more likely.
The label is echoed as live_net_gex_label ("positive" or "negative") so you never re-derive the sign in client code. The same signing flows through the levels endpoint, re-signing the gamma flip and the call and put walls.
The endpoints, precisely scoped
| Endpoint | What flow mode changes | Tier |
|---|---|---|
GET /v1/flow/gex/{symbol} |
Full re-signed surface, aggregate as live_net_gex, plus per-strike dealer diagnostics |
Growth and above |
GET /v1/flow/levels/{symbol} |
Re-signs live_gamma_flip, live_call_wall, live_put_wall. live_max_pain intentionally stays OI-based |
Growth and above |
GET /v1/flow/live/{symbol} |
Re-signed aggregate gamma as live_gex. DEX and dealer-risk in the bundle stay convention-signed |
Alpha and above |
Scope notes that will save you a support ticket: the five other simulation-aware flow endpoints (summary, pin-risk, dex, dealer-risk, oi) do not accept polarity. And flow polarity is independent of the effective-OI simulator the default flow surface uses; that is a separate mechanism.
Coverage is 6,000+ US equities and ETFs, plus ES and NQ index futures. Because the default is unchanged, every existing integration keeps working exactly as before. Flow mode is strictly additive.
Calling it
import requests
BASE = "https://lab.flashalpha.com"
HEADERS = {"X-Api-Key": "YOUR_KEY"}
# Flow-signed GEX: sign each strike by measured dealer position
r = requests.get(f"{BASE}/v1/flow/gex/SPY",
params={"polarity": "flow"}, headers=HEADERS).json()
print(f"Polarity: {r['polarity']}")
print(f"Net dealer GEX: {r['live_net_gex']:,} ({r['live_net_gex_label']})")
print(f"Gamma flip: {r['live_gamma_flip']}")
if r["live_net_gex"] < 0:
print("Dealers net SHORT gamma - moves amplify, vol expands")
else:
print("Dealers net LONG gamma - moves dampen, price pins")
# Inspect where the position came from, strike by strike
for s in r["strikes"][:3]:
print(f" {s['strike']}: dealer calls {s['call_dealer_pos']:+}, "
f"dealer puts {s['put_dealer_pos']:+}, net_gex {s['net_gex']:,}")
In flow mode the response adds a top-level polarity: "flow" marker plus four per-strike diagnostics: call_net_customer and put_net_customer (customer buys minus sells, in contracts) and call_dealer_pos and put_dealer_pos (their negatives). Settled call_oi / put_oi are still returned per strike for reference, but play no part in the signed gamma.
An illustrative API-shape sample (not live data):
{
"symbol": "SPY",
"polarity": "flow",
"live_net_gex": -4200000000,
"live_net_gex_label": "negative",
"live_gamma_flip": 596.00,
"strikes": [
{
"strike": 595.0,
"call_gex": -145000000,
"put_gex": 62000000,
"net_gex": -83000000,
"call_net_customer": 2150,
"put_net_customer": -1890,
"call_dealer_pos": -2150,
"put_dealer_pos": 1890,
"call_oi": 15820,
"put_oi": 12340
}
]
}
Read the strike: customers net bought 2,150 calls, so dealers are short 2,150 calls and short gamma on that side. Customers net sold 1,890 puts, so dealers are long those puts and long gamma there. Net across the surface is negative: dealers are net short gamma and moves are likely to amplify.
The divergence trade
Convention and flow polarity are complementary, not competing. Use convention GEX for the structural backdrop and the flip level, then watch flow polarity intraday to catch when the day's order flow pushes dealers into a genuinely different gamma posture than the OI-based picture implies. When the two disagree, the disagreement is the signal: structural gamma says pin, real positioning says amplify.
import requests
BASE = "https://lab.flashalpha.com"
H = {"X-Api-Key": "YOUR_KEY"}
# Same endpoint, two signings
conv = requests.get(f"{BASE}/v1/flow/gex/SPY", headers=H).json()
flow = requests.get(f"{BASE}/v1/flow/gex/SPY",
params={"polarity": "flow"}, headers=H).json()
structural_long = conv["live_net_gex"] > 0
dealer_long = flow["live_net_gex"] > 0
if structural_long and not dealer_long:
print("Structural gamma long, but today's flow has dealers SHORT - expect amplification")
elif dealer_long:
print("Dealers genuinely long gamma from the tape - fade extremes toward the flip")
else:
print("Both agree: dealers short gamma - respect trends")
Pair it with net dealer premium, the premium-sign counterpart, for a fuller read of dealer positioning.
Backtesting with no lookahead bias
Flow polarity works on the point-in-time historical API too. Add ?at=<timestamp>&polarity=flow to reconstruct the flow-signed surface as it stood at any minute, using only the classified tape up to that instant, with greeks repriced at that moment's spot. Historical replay covers /v1/flow/gex and /v1/flow/levels on the Alpha-tier historical API (the historical live bundle does not take the parameter).
import requests, pandas as pd
BASE = "https://historical.flashalpha.com"
HEADERS = {"X-Api-Key": "YOUR_KEY"}
rows = []
for day in pd.bdate_range("2026-05-01", "2026-05-31"):
at = f"{day:%Y-%m-%d}T18:00:00Z" # 14:00 ET snapshot
r = requests.get(f"{BASE}/v1/flow/gex/SPY",
params={"at": at, "polarity": "flow"},
headers=HEADERS).json()
rows.append({"date": day.date(),
"net_gex": r["live_net_gex"],
"label": r["live_net_gex_label"]})
df = pd.DataFrame(rows)
print(df) # daily flow-signed dealer gamma regime, no lookahead
Because each snapshot only consumes the tape up to at, the series is usable for genuine signal research on dealer-positioning regimes.
What flow polarity is not
- Not the effective-OI simulator. The default flow surface estimates intraday OI change with a confidence weight and signs by convention. Flow polarity ignores that machinery and builds gamma from classified volume alone.
-
Not an absolute dealer book.
dealer_posis the net position accumulated today, from zero at the open. It is what dealers traded into during the session, not their full inventory including prior days. - Not a modification of anything else. Convention output, settled exposure endpoints, DEX and dealer-risk are untouched. Only the three endpoints above change, and only when you ask.
One honest caveat: flow polarity is a derived signal off the classified options tape, not exchange-audited dealer accounting. The dealer side is inferred from quote-rule NBBO aggressor classification. Read the sign and its intraday shifts as a relative signal.
Try it
The full reference, including response schemas, an FAQ, and the same call in Python, JavaScript, C#, Go and cURL, is in the original article. The interactive playground has the endpoints live if you want to poke at them without writing code.
Questions on the classification method or the maths welcome in the comments.
Top comments (0)