Skip to content

Commit e96b27f

Browse files
tdobrowolski1claude
andcommitted
Add GEX trading examples, dealer hedging, exposure analysis, and comprehensive tests
New code examples using FlashAlpha SDK: - gamma_exposure_by_strike.py: full GEX profile by strike (call/put/net GEX, OI, volume) - dealer_hedging_flow_analysis.py: hedging estimates at +/-1% moves with regime interpretation - gamma_flip_level_tracker.py: tracks gamma flip, call wall, put wall, max pain, 0DTE magnet - call_wall_put_wall_finder.py: multi-symbol wall scanner (SPY, QQQ, AAPL, TSLA, NVDA) - gex_trading_spy_tsla_qqq.py: comprehensive GEX trading analysis for three key symbols - exposure_narrative_analysis.py: AI narrative with regime, flow, vanna, charm, 0DTE, outlook - delta_vanna_charm_exposure.py: DEX, VEX, CHEX analysis beyond gamma Tests: - tests/test_unit.py: 44 new unit tests (mocked, no API key required) covering GEX response parsing, exposure levels, hedging estimates, narrative sections, DEX/VEX/CHEX, error handling - tests/test_integration.py: 18 new integration tests covering SDK client for GEX strikes, levels fields, hedging, narrative sections, DEX/VEX/CHEX, and stock_quote README updated with new file table, GEX trading guide link, and test instructions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fc9d5a0 commit e96b27f

10 files changed

Lines changed: 1514 additions & 3 deletions

README.md

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,33 @@ Beyond the gamma flip, GEX analysis identifies:
8484

8585
## Code in This Repo
8686

87+
### Core: from-scratch GEX computation
88+
8789
| File | What it does |
8890
|------|-------------|
8991
| [code/compute_gex.py](code/compute_gex.py) | Computes GEX from a raw CSV options chain |
9092
| [code/plot_gex.py](code/plot_gex.py) | Bar chart of GEX by strike with key levels marked |
9193
| [code/compare_with_api.py](code/compare_with_api.py) | Compares manual calculation against the FlashAlpha API |
9294
| [data/sample_chain.csv](data/sample_chain.csv) | Sample SPY options chain (~25 rows, realistic prices) |
93-
| [tests/test_compute_gex.py](tests/test_compute_gex.py) | Unit tests for the GEX computation logic |
95+
96+
### API examples: live GEX data via FlashAlpha
97+
98+
| File | What it does |
99+
|------|-------------|
100+
| [code/gamma_exposure_by_strike.py](code/gamma_exposure_by_strike.py) | Full GEX profile by strike — call GEX, put GEX, net GEX, OI, volume |
101+
| [code/dealer_hedging_flow_analysis.py](code/dealer_hedging_flow_analysis.py) | Dealer hedging estimates at ±1% moves — shares, direction, notional |
102+
| [code/gamma_flip_level_tracker.py](code/gamma_flip_level_tracker.py) | Track gamma flip, call wall, put wall, max pain, 0DTE magnet |
103+
| [code/call_wall_put_wall_finder.py](code/call_wall_put_wall_finder.py) | Scan multiple symbols (SPY, QQQ, AAPL, TSLA, NVDA) for wall levels |
104+
| [code/gex_trading_spy_tsla_qqq.py](code/gex_trading_spy_tsla_qqq.py) | Comprehensive GEX trading analysis for SPY, TSLA, and QQQ |
105+
| [code/exposure_narrative_analysis.py](code/exposure_narrative_analysis.py) | AI-powered narrative: regime, flow, vanna, charm, 0DTE, outlook |
106+
| [code/delta_vanna_charm_exposure.py](code/delta_vanna_charm_exposure.py) | DEX, VEX, CHEX — delta, vanna, and charm exposure beyond gamma |
107+
108+
### Tests
109+
110+
| File | What it does |
111+
|------|-------------|
112+
| [tests/test_compute_gex.py](tests/test_compute_gex.py) | Unit tests for the from-scratch GEX computation logic |
113+
| [tests/test_unit.py](tests/test_unit.py) | Unit tests for FlashAlpha API response parsing (mocked, no key required) |
94114
| [tests/test_integration.py](tests/test_integration.py) | Integration tests against the live FlashAlpha API |
95115

96116
Run the compute script:
@@ -100,12 +120,28 @@ pip install numpy scipy matplotlib requests
100120
python code/compute_gex.py
101121
```
102122

103-
Run tests:
123+
Run unit tests (no API key required):
104124

105125
```bash
106-
pytest tests/test_compute_gex.py
126+
pip install flashalpha pytest
127+
pytest tests/test_compute_gex.py tests/test_unit.py -v
107128
```
108129

130+
Run all tests including live API tests:
131+
132+
```bash
133+
export FLASHALPHA_API_KEY=your_key_here
134+
pytest tests/ -v -m integration
135+
```
136+
137+
---
138+
139+
## GEX Trading Guide
140+
141+
For a complete walkthrough of how to use GEX data to trade SPY, TSLA, and QQQ — including regime identification, level tracking, and dealer hedging flow analysis — see:
142+
143+
[GEX Trading Guide: Gamma Exposure API for SPY, TSLA, QQQ](https://flashalpha.com/articles/gex-trading-guide-gamma-exposure-api-spy-tsla)
144+
109145
---
110146

111147
## Skip the Math

code/call_wall_put_wall_finder.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""
2+
Find call wall and put wall levels for any stock using gamma exposure data
3+
4+
The call wall and put wall are derived from the gamma exposure (GEX) profile
5+
of an options chain. They represent the strikes where dealers have the highest
6+
concentration of long-gamma (call wall) or short-gamma (put wall) exposure.
7+
8+
These levels act as dynamic support and resistance because dealer hedging
9+
behavior intensifies as spot approaches them:
10+
11+
- Near the call wall: dealers must sell more as price climbs, creating
12+
friction that slows or caps rallies.
13+
14+
- Near the put wall: dealers are short gamma, meaning a break below triggers
15+
forced selling that can accelerate declines.
16+
17+
This script scans multiple symbols and prints a table of walls for each.
18+
19+
Usage:
20+
export FLASHALPHA_API_KEY=your_key_here
21+
python code/call_wall_put_wall_finder.py
22+
23+
Install:
24+
pip install flashalpha
25+
"""
26+
27+
import os
28+
29+
from flashalpha import FlashAlpha, FlashAlphaError
30+
31+
32+
SYMBOLS = ["SPY", "QQQ", "AAPL", "TSLA", "NVDA"]
33+
34+
35+
def main() -> None:
36+
api_key = os.environ.get("FLASHALPHA_API_KEY", "YOUR_API_KEY")
37+
fa = FlashAlpha(api_key)
38+
39+
print("Fetching call wall and put wall for multiple symbols...\n")
40+
41+
results = []
42+
for symbol in SYMBOLS:
43+
try:
44+
data = fa.exposure_levels(symbol)
45+
levels = data.get("levels", {})
46+
results.append({
47+
"symbol": symbol,
48+
"call_wall": levels.get("call_wall"),
49+
"put_wall": levels.get("put_wall"),
50+
"gamma_flip": levels.get("gamma_flip"),
51+
"max_pain": levels.get("max_pain"),
52+
})
53+
except FlashAlphaError as exc:
54+
print(f" {symbol}: error — {exc}")
55+
results.append({
56+
"symbol": symbol,
57+
"call_wall": None,
58+
"put_wall": None,
59+
"gamma_flip": None,
60+
"max_pain": None,
61+
})
62+
63+
# Print table
64+
header = f"{'Symbol':<8} {'Call Wall':>10} {'Put Wall':>10} {'Gamma Flip':>11} {'Max Pain':>10} {'Range Width':>12}"
65+
print(header)
66+
print("-" * len(header))
67+
68+
for r in results:
69+
call_wall = r["call_wall"]
70+
put_wall = r["put_wall"]
71+
gamma_flip = r["gamma_flip"]
72+
max_pain = r["max_pain"]
73+
74+
cw_str = f"{call_wall:.2f}" if call_wall is not None else "N/A"
75+
pw_str = f"{put_wall:.2f}" if put_wall is not None else "N/A"
76+
gf_str = f"{gamma_flip:.2f}" if gamma_flip is not None else "N/A"
77+
mp_str = f"{max_pain:.2f}" if max_pain is not None else "N/A"
78+
79+
if call_wall is not None and put_wall is not None:
80+
range_str = f"{call_wall - put_wall:.2f}"
81+
else:
82+
range_str = "N/A"
83+
84+
print(f"{r['symbol']:<8} {cw_str:>10} {pw_str:>10} {gf_str:>11} {mp_str:>10} {range_str:>12}")
85+
86+
print()
87+
print("--- How to Use These Levels ---")
88+
print()
89+
print(" Call wall (resistance):")
90+
print(" The strike where dealers have the highest concentration of long-gamma")
91+
print(" exposure from calls. As price climbs toward the call wall, dealer")
92+
print(" rehedging creates selling pressure that slows or caps the rally.")
93+
print(" A weekly close ABOVE the call wall with strong momentum is bullish —")
94+
print(" it forces dealers to cover their shorts and buy more.")
95+
print()
96+
print(" Put wall (support / trap door):")
97+
print(" The strike where dealers are most short-gamma from puts. The put wall")
98+
print(" often acts as a floor — but if breached decisively, the forced selling")
99+
print(" from dealer rehedging can accelerate the move lower sharply.")
100+
print()
101+
print(" Gamma flip (regime boundary):")
102+
print(" Above the gamma flip, dealers stabilize price. Below it, they amplify")
103+
print(" moves. The gamma flip is often the most actionable single level.")
104+
print()
105+
print(" Range width:")
106+
print(" The distance between put wall and call wall. Narrower ranges suggest")
107+
print(" tighter pinning and lower realized volatility. Wider ranges indicate")
108+
print(" that dealers are spread thin and cannot contain large moves.")
109+
print()
110+
print("Learn more: https://flashalpha.com/articles/gex-trading-guide-gamma-exposure-api-spy-tsla")
111+
112+
113+
if __name__ == "__main__":
114+
main()
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""
2+
Analyze dealer hedging flows from options gamma exposure in Python
3+
4+
When dealers sell options, they hedge their delta exposure by trading the
5+
underlying. As price moves, that hedge must be continuously adjusted — and
6+
the size and direction of those adjustments is what GEX measures.
7+
8+
This script uses the FlashAlpha exposure_summary endpoint to quantify exactly
9+
how many shares dealers must buy or sell if the market moves 1% up or down.
10+
That mechanical flow is predictable, repeatable, and tradeable.
11+
12+
Why it matters:
13+
- Positive gamma regime: dealers BUY dips and SELL rips (dampening)
14+
- Negative gamma regime: dealers SELL dips and BUY rips (amplifying)
15+
- The hedging flow is proportional to gamma * spot^2 * move
16+
17+
Usage:
18+
export FLASHALPHA_API_KEY=your_key_here
19+
python code/dealer_hedging_flow_analysis.py
20+
21+
Install:
22+
pip install flashalpha
23+
"""
24+
25+
import os
26+
27+
from flashalpha import FlashAlpha
28+
29+
30+
def format_shares(n: float) -> str:
31+
"""Format a share count with sign and thousands separator."""
32+
sign = "+" if n >= 0 else ""
33+
return f"{sign}{n:,.0f}"
34+
35+
36+
def format_notional(dollars: float) -> str:
37+
"""Format a dollar notional as $XB or $XM."""
38+
if abs(dollars) >= 1e9:
39+
return f"${dollars/1e9:+.2f}B"
40+
return f"${dollars/1e6:+.2f}M"
41+
42+
43+
def main() -> None:
44+
api_key = os.environ.get("FLASHALPHA_API_KEY", "YOUR_API_KEY")
45+
fa = FlashAlpha(api_key)
46+
47+
symbol = "SPY"
48+
print(f"Fetching dealer hedging flow analysis for {symbol}...")
49+
50+
data = fa.exposure_summary(symbol)
51+
52+
# --- Overall GEX regime ---
53+
gex = data.get("gex", {})
54+
net_gex = gex.get("net_gex", 0)
55+
regime = gex.get("net_gex_label", "unknown")
56+
print(f"\nSymbol : {symbol}")
57+
print(f"Net GEX : ${net_gex:,.0f}")
58+
print(f"Regime : {regime}")
59+
60+
# --- Hedging estimates ---
61+
hedging = data.get("hedging", {})
62+
63+
up_move = hedging.get("up_1pct", {})
64+
down_move = hedging.get("down_1pct", {})
65+
66+
print("\n--- Dealer Hedging at +1% Move ---")
67+
up_shares = up_move.get("shares_to_hedge", 0)
68+
up_direction = up_move.get("direction", "unknown")
69+
up_notional = up_move.get("notional_usd", 0)
70+
print(f" Shares to hedge : {format_shares(up_shares)}")
71+
print(f" Direction : {up_direction}")
72+
print(f" Notional : {format_notional(up_notional)}")
73+
74+
print("\n--- Dealer Hedging at -1% Move ---")
75+
dn_shares = down_move.get("shares_to_hedge", 0)
76+
dn_direction = down_move.get("direction", "unknown")
77+
dn_notional = down_move.get("notional_usd", 0)
78+
print(f" Shares to hedge : {format_shares(dn_shares)}")
79+
print(f" Direction : {dn_direction}")
80+
print(f" Notional : {format_notional(dn_notional)}")
81+
82+
# --- Explanation ---
83+
print("\n--- Why Dealer Hedging Creates Mechanical Flow ---")
84+
print()
85+
print(" When you buy a call, the dealer who sold it goes short gamma.")
86+
print(" To stay delta-neutral, the dealer buys the underlying as price rises")
87+
print(" and sells it as price falls. This is continuous, mechanical, and large.")
88+
print()
89+
if net_gex > 0:
90+
print(" Current regime: POSITIVE gamma.")
91+
print(" Dealer rehedging is COUNTER-CYCLICAL:")
92+
print(" - On up moves: dealers SELL to rehedge (suppresses rallies)")
93+
print(" - On down moves: dealers BUY to rehedge (cushions declines)")
94+
print(" Expect range-bound, mean-reverting price action.")
95+
else:
96+
print(" Current regime: NEGATIVE gamma.")
97+
print(" Dealer rehedging is PRO-CYCLICAL:")
98+
print(" - On up moves: dealers BUY to rehedge (accelerates rallies)")
99+
print(" - On down moves: dealers SELL to rehedge (accelerates declines)")
100+
print(" Expect trending, momentum-driven price action with elevated volatility.")
101+
102+
print()
103+
print("Learn more: https://flashalpha.com/articles/gex-trading-guide-gamma-exposure-api-spy-tsla")
104+
105+
106+
if __name__ == "__main__":
107+
main()

0 commit comments

Comments
 (0)