Skip to content

Commit fda151b

Browse files
tdobrowolski1claude
andcommitted
Add max pain analysis example notebook and update README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bb7d413 commit fda151b

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ short volatility screener, vol selling scanner.
3232
| `notebooks/08_advanced_volatility.py` | Advanced Volatility: SVI and Variance Surface | SVI parameters, variance surface, calendar/butterfly arbitrage detection, greeks surfaces, variance swap rates |
3333
| `notebooks/09_volatility_analysis.py` | Comprehensive Volatility Analysis | IV rank, term structure, skew, vol risk premium, forward vol, vol regime for TSLA |
3434
| `notebooks/10_live_options_screener.py` | **Live Options Screener** | **Filter and rank symbols by GEX, VRP, IV, greeks, harvest scores, and custom formulas. Includes harvestable VRP screen, vol scanner, cascading strike/contract filters, and risk-adjusted rankings** |
35+
| `notebooks/11_max_pain_analysis.py` | **Max Pain Analysis** | **Max pain strike, pain curve, OI distribution, dealer alignment overlay (gamma flip + walls), expected move context, pin probability, and multi-expiry calendar** |
3536

3637
---
3738

@@ -76,6 +77,7 @@ pytest tests/test_examples.py -m integration -v
7677
| Method | Description | Tier |
7778
|--------|-------------|------|
7879
| `fa.screener(...)` | Live options screener — filter/rank by GEX, VRP, IV, harvest score, formulas | Growth+ |
80+
| `fa.max_pain(symbol)` | Max pain analysis with dealer alignment, pain curve, pin probability | Growth+ |
7981
| `fa.gex(symbol)` | Gamma exposure by strike | Public |
8082
| `fa.dex(symbol)` | Delta exposure | Public |
8183
| `fa.vex(symbol)` | Vanna exposure | Public |

notebooks/11_max_pain_analysis.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# # Max Pain Analysis — dealer alignment, pain curve, and pin probability
2+
#
3+
# The Max Pain endpoint returns the strike where total option holder payout is
4+
# minimized. This analysis overlays dealer positioning (gamma flip, call/put
5+
# walls) to identify whether max pain acts as a convergence magnet or is
6+
# overridden by directional gamma flow.
7+
#
8+
# Docs: https://flashalpha.com/docs
9+
# Install: pip install "flashalpha>=0.3.2"
10+
#
11+
# Tier: Growth+ required
12+
13+
import os
14+
from flashalpha import FlashAlpha
15+
16+
fa = FlashAlpha(os.environ["FLASHALPHA_API_KEY"])
17+
18+
# ---------------------------------------------------------------------------
19+
# 1) Full-chain max pain (all expirations)
20+
# ---------------------------------------------------------------------------
21+
result = fa.max_pain("SPY")
22+
print(f"Max pain strike: {result['max_pain_strike']}")
23+
print(f"Spot: {result['underlying_price']}")
24+
print(f"Distance: {result['distance']['absolute']:.2f} ({result['distance']['percent']:.2f}%) {result['distance']['direction']}")
25+
print(f"Signal: {result['signal']}")
26+
print(f"Pin probability: {result['pin_probability']}/100")
27+
28+
# ---------------------------------------------------------------------------
29+
# 2) Dealer alignment overlay
30+
# ---------------------------------------------------------------------------
31+
da = result["dealer_alignment"]
32+
print(f"\nDealer alignment: {da['alignment']}")
33+
print(f" {da['description']}")
34+
print(f" Gamma flip: {da['gamma_flip']}")
35+
print(f" Call wall: {da['call_wall']}")
36+
print(f" Put wall: {da['put_wall']}")
37+
print(f" Regime: {result['regime']}")
38+
39+
# ---------------------------------------------------------------------------
40+
# 3) Expected move context
41+
# ---------------------------------------------------------------------------
42+
em = result["expected_move"]
43+
print(f"\nATM straddle: ${em['straddle_price']:.2f}")
44+
print(f"ATM IV: {em['atm_iv']}%")
45+
print(f"Max pain within expected range: {em['max_pain_within_expected_range']}")
46+
47+
# ---------------------------------------------------------------------------
48+
# 4) Pain curve — top 5 strikes by lowest total pain
49+
# ---------------------------------------------------------------------------
50+
curve = sorted(result["pain_curve"], key=lambda x: x["total_pain"])
51+
print("\nPain curve (lowest pain strikes):")
52+
for row in curve[:5]:
53+
print(f" {row['strike']:>8} call_pain=${row['call_pain']:>12,.0f} put_pain=${row['put_pain']:>12,.0f} total=${row['total_pain']:>12,.0f}")
54+
55+
# ---------------------------------------------------------------------------
56+
# 5) OI distribution around max pain
57+
# ---------------------------------------------------------------------------
58+
mp = result["max_pain_strike"]
59+
oi_near = [s for s in result["oi_by_strike"] if abs(s["strike"] - mp) <= 10]
60+
print(f"\nOI near max pain ({mp}):")
61+
for s in oi_near[:5]:
62+
print(f" {s['strike']:>8} call_oi={s['call_oi']:>8,} put_oi={s['put_oi']:>8,} total={s['total_oi']:>8,}")
63+
64+
print(f"\nPut/call OI ratio: {result['put_call_oi_ratio']:.3f}")
65+
66+
# ---------------------------------------------------------------------------
67+
# 6) Multi-expiry calendar
68+
# ---------------------------------------------------------------------------
69+
if result.get("max_pain_by_expiration"):
70+
print("\nMax pain by expiry:")
71+
for entry in result["max_pain_by_expiration"][:5]:
72+
print(f" {entry['expiration']} strike={entry['max_pain_strike']} DTE={entry['dte']} OI={entry['total_oi']:,}")
73+
74+
# ---------------------------------------------------------------------------
75+
# 7) Single-expiry max pain
76+
# ---------------------------------------------------------------------------
77+
# Get first available expiration
78+
opts = fa.options("SPY")
79+
if opts.get("expirations"):
80+
exp = opts["expirations"][0]["expiration"]
81+
single = fa.max_pain("SPY", expiration=exp)
82+
print(f"\nMax pain for {exp}: {single['max_pain_strike']}")
83+
print(f" Pin probability: {single['pin_probability']}/100")
84+
print(f" Dealer alignment: {single['dealer_alignment']['alignment']}")

0 commit comments

Comments
 (0)