Skip to content

GEX Math Specification — Canonical Reference


status: ACTIVE tier: 2 (design reference) last-verified: 2026-09-18 verified-against: 5c9bd087 canonical-implementation: Backtesting/scripts/gex_lib.py parity-port: TraderJan.Indicators/GexEngine.cs (v1.7.2)


Single source of truth

All GEX/VEX/CEX/DEX math lives in gex_lib.py; GexEngine.cs is a verified parity port (0.0 pt across all 10 key levels on the frozen CBOE fixture — ParityHarness + tests/test_gex_lib.py). manage_data.py delegates since Phase 5 (4th dialect eliminated). Any new consumer MUST import/call these — never re-implement.

Greeks (Black-Scholes, dividend-yield adjusted)

Inputs: ETF spot S, strike K, years T = max(dte/365, 1/(365·24)), r = 0.0435, q = 0.0055 (QQQ) / 0.0125 (SPY), IV σ (vendor or bisection-inverted from mid price).

  • gamma = e^{-qT}·φ(d1) / (S·σ·√T)
  • vanna = -e^{-qT}·φ(d1)·d2 / σ
  • charm = -e^{-qT}·( φ(d1)·(2(r-q)T - d2·σ·√T)/(2·T·σ·√T) + q·N(d1) ) ← the q·N(d1) term is mandatory (Phase 1 R2)
  • delta = ±N(d1) (call/put)

Exposures (per contract, dealer-position convention)

  • GEX = sign · gamma · OI · 100 · S² · 0.01 (calls +, puts −)
  • VEX = sign · vanna · OI · 100 · S · 0.01
  • CEX = sign · charm · OI · 100
  • DEX = delta · OI · 100 · S

Level construction (compute_strike_levels / ComputeScope)

  • Walls: proximity-weighted argmax of call/put GEX (Gaussian weight, band 3.5 % for 0DTE/Weekly, 8 % Monthly/Core) above/below bench ETF spot; tie-break by OI.
  • Gamma flip: cumulative net-GEX zero crossing restricted to ±5 % of spot; fallback = strike with min |cumulative| in band (Phase 3.1 adoption).
  • Max pain: min aggregate intrinsic payout over candidate strikes in the same ±5 % band.
  • Vol trigger: descending scan below flip where cumulative put GEX > 1.5× call GEX, sanity-floored at 0.5×spot with fallback scale(gf/ratio − offset) (offset 1.0 NQ / 0.2 ES).
  • Charm magnet / major pin: proximity-weighted (σ=0.75·EM) argmax of |CEX| / |GEX| over ≤5 DTE near-EM contracts.
  • EM bands: S · (IV_atm/√252) around bench spot.
  • Scaling: ETF strike × ratio → rounded to 25 pt (NQ) / 5 pt (ES); ratio from nq_qqq_ratios.json / es_spy_ratios.json (real daily closes, ratio_source: real).

Term-structure cascade (display/lookup, C# ExtractGexLevels)

  • Call/Put Wall: weekly → monthly → key_levels → dte0
  • Gamma Flip: dte0 → weekly → key_levels Master Parquet static columns use the same cascade (verified by verify_golden_days.py).

Divergence table (gex_lib vs legacy sources)

Feature Status
Gamma-flip ±5 % band + min|cum| fallback Resolved — adopted in GexEngine v1.6.0
VT sanity floor Resolved — adopted in GexEngine v1.6.0
Strike-profile pruning (Pareto 90 % + anchors, Python only) By design — C# keeps full ±1.5σ band
manage_data 4th dialect Eliminated Phase 5 (delegates to gex_lib)

Verification contract

  • tests/test_gex_lib.py — 5/5 incl. golden fixture values from temp/cs_key_levels.json (C# harness output; regenerate via ParityHarness after any math change).
  • verify_golden_days.py — 5 sessions/yr parity: Parquet == C# cascade == backtester, 0.0 pt.
  • Evidence mandate: GATE submissions paste raw command output only.