Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ summary = client.stock_summary("SPY")

# Or a focused exposure snapshot.
exp = client.exposure_summary("SPY")
print(exp["gamma_flip"], exp["regime"], exp["exposures"]["net_gex"])
# gamma_flip is None unless gamma_flip_status == "available".
print(exp["gamma_flip"], exp["gamma_flip_status"], exp["regime"])
```

## Typed responses
Expand All @@ -144,6 +145,18 @@ silent-null traps:
MAGNITUDE on this endpoint (the `direction` field carries the
sign). On `zero_dte` the same field is signed. Don't copy code
between the two without re-checking signs.
- `gamma_flip` (every exposure/flow endpoint): nullable, and `null`
for the MAJORITY of chains. The sibling `gamma_flip_status` says why
— `"available"` means a level was published, anything else is a
reason code (`no_boundary`, `insufficient_local_coverage`,
`insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`,
`stored_sign_mismatch`, `search_budget`, `quality_budget`) and
`regime` degrades to `"unknown"`. NEVER generate code that formats
`gamma_flip` as a number without a null check — `f"{flip:.2f}"`
raises `TypeError` on `NoneType.__format__`. Treat unrecognised
status values as unavailable. On the flow endpoints the value field
is `live_gamma_flip` but the status is still named plain
`gamma_flip_status`.
- `pricing/greeks` response: `additional.lambda` collides with the
Python `lambda` keyword — the typed model uses the functional
`TypedDict` constructor so the JSON name is preserved. Read it as
Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,25 @@ fa = FlashAlpha("YOUR_API_KEY") # Get a free key at flashalpha.com
# Gamma exposure by strike
gex = fa.gex("SPY")
print(f"Net GEX: ${gex['net_gex']:,.0f}")
print(f"Gamma flip: {gex['gamma_flip']}")
# gamma_flip is None unless gamma_flip_status == "available" -- never format it
# as a number without checking first.
print(f"Gamma flip: {gex['gamma_flip']} ({gex['gamma_flip_status']})")

for strike in gex["strikes"][:5]:
print(f" {strike['strike']}: net ${strike['net_gex']:,.0f}")
```

Get your free API key at [flashalpha.com](https://flashalpha.com) — no credit card required.

> **`gamma_flip` is nullable.** A dealer gamma flip is only published when the
> level is well-determined, which is a minority of chains. When it is withheld,
> `gamma_flip` is `null`, `regime` is `"unknown"`, and `gamma_flip_status` carries
> a reason code (`no_boundary`, `insufficient_local_coverage`,
> `insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`,
> `stored_sign_mismatch`, `search_budget`, `quality_budget`). Only
> `gamma_flip_status == "available"` guarantees a number; treat any other value -
> including codes added in future - as "no level published".

## Data provenance: `data_as_of`

Every successful JSON-object response carries `data_as_of`, reporting when each upstream
Expand Down Expand Up @@ -148,7 +159,8 @@ chex = fa.chex("NVDA") # Charm exposure
levels = fa.exposure_levels("SPY") # Key levels
print(f"Call wall: {levels['levels']['call_wall']}")
print(f"Put wall: {levels['levels']['put_wall']}")
print(f"Gamma flip: {levels['levels']['gamma_flip']}")
print(f"Gamma flip: {levels['levels']['gamma_flip']} "
f"({levels['levels']['gamma_flip_status']})")

summary = fa.exposure_summary("SPY") # Full summary (Growth+)
narrative = fa.narrative("SPY") # AI narrative (Growth+)
Expand Down
43 changes: 35 additions & 8 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Real-time options exposure analytics. Live gamma (GEX), delta (DEX), vanna (VEX)

> 📖 **Canonical, always-current docs:** https://flashalpha.com/docs · 🔑 [Get a free API key](https://flashalpha.com) · 🧪 [Interactive playground](https://lab.flashalpha.com/swagger)

> ⚠️ **`gamma_flip` is nullable.** A dealer gamma flip is only published when the level
> is well-determined, which is a minority of chains. When it is withheld, `gamma_flip`
> (and `live_gamma_flip` on the flow endpoints) is `null`, `regime` is `"unknown"`, and
> the sibling `gamma_flip_status` string carries the reason code. Only
> `gamma_flip_status == "available"` guarantees a number — treat every other value,
> including codes added in future, as "no level published". Note that the flow endpoints
> pair `live_gamma_flip` with a status field named plainly `gamma_flip_status`, not
> `live_gamma_flip_status`. The examples below all show the `"available"` case.

---

## Playground
Expand Down Expand Up @@ -429,6 +438,7 @@ curl "https://lab.flashalpha.com/v1/stock/SPY/summary"
"net_vex": 1200000000,
"net_chex": 850000000,
"gamma_flip": 575.25,
"gamma_flip_status": "available",
"call_wall": 585.0,
"put_wall": 570.0,
"max_pain": 578.0,
Expand Down Expand Up @@ -489,7 +499,8 @@ curl "https://lab.flashalpha.com/v1/stock/SPY/summary"
| `volatility.iv_term_structure` | IV at ATM for each active expiration (filtered to 5–200% to exclude bad SVI fits) |
| `options_flow` | Aggregate OI, volume, and put/call ratios across all active expirations |
| `exposure.net_gex/dex/vex/chex` | Net gamma/delta/vanna/charm exposure |
| `exposure.gamma_flip` | Strike where net GEX crosses zero |
| `exposure.gamma_flip` | Strike where net GEX crosses zero; `null` when no level is published |
| `exposure.gamma_flip_status` | `"available"` when a level is published, otherwise the reason code it was withheld (`no_boundary`, `insufficient_local_coverage`, `insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`, `stored_sign_mismatch`, `search_budget`, `quality_budget`). Treat anything other than `"available"` as no level published |
| `exposure.call_wall` / `put_wall` | Strikes with highest call/put GEX concentration |
| `exposure.max_pain` | Strike where total option holder loss is maximized |
| `exposure.highest_oi_strike` | Strike with highest total open interest |
Expand Down Expand Up @@ -553,6 +564,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"underlying_price": 597.505,
"as_of": "2026-02-28T16:30:45Z",
"gamma_flip": 595.25,
"gamma_flip_status": "available",
"net_gex": 2850000000,
"net_gex_label": "positive",
"strikes": [
Expand Down Expand Up @@ -734,6 +746,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"underlying_price": 597.505,
"as_of": "2026-02-28T16:30:45Z",
"gamma_flip": 595.25,
"gamma_flip_status": "available",
"regime": "positive_gamma",
"exposures": {
"net_gex": 2850000000,
Expand Down Expand Up @@ -802,6 +815,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"as_of": "2026-02-28T16:30:45Z",
"levels": {
"gamma_flip": 595.25,
"gamma_flip_status": "available",
"max_positive_gamma": 600.0,
"max_negative_gamma": 585.0,
"call_wall": 600.0,
Expand All @@ -816,7 +830,8 @@ curl -H "X-Api-Key: YOUR_API_KEY" \

| Field | Description |
|-------|-------------|
| `gamma_flip` | Price where net GEX crosses zero — above = positive gamma, below = negative |
| `gamma_flip` | Price where net GEX crosses zero — above = positive gamma, below = negative; `null` when no level is published |
| `gamma_flip_status` | `"available"` when a level is published, otherwise the reason code it was withheld (`no_boundary`, `insufficient_local_coverage`, `insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`, `stored_sign_mismatch`, `search_budget`, `quality_budget`). Treat anything other than `"available"` as no level published |
| `call_wall` | Strike with highest call GEX — acts as resistance |
| `put_wall` | Strike with highest put GEX — acts as support |
| `max_positive_gamma` | Strike with highest positive net GEX |
Expand Down Expand Up @@ -867,6 +882,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"net_gex_change_pct": 9.6,
"vix": 18.5,
"gamma_flip": 595.25,
"gamma_flip_status": "available",
"call_wall": 600.0,
"put_wall": 595.0,
"regime": "positive_gamma",
Expand Down Expand Up @@ -936,6 +952,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"label": "positive_gamma",
"description": "Dealers long gamma — moves dampened, mean reversion likely",
"gamma_flip": 588.50,
"gamma_flip_status": "available",
"spot_vs_flip": "above",
"spot_to_flip_pct": 0.33,
"distance_to_flip_dollars": 1.92,
Expand Down Expand Up @@ -1968,6 +1985,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"underlying_price": 597.50,
"expiry": "2026-05-15",
"live_gamma_flip": 595.50,
"gamma_flip_status": "available",
"live_call_wall": 600,
"live_put_wall": 590,
"live_max_pain": 595
Expand Down Expand Up @@ -2125,6 +2143,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"live_net_gex": 12500000000,
"live_net_gex_label": "positive",
"live_gamma_flip": 595.50,
"gamma_flip_status": "available",
"strikes": [
{
"strike": 595.0,
Expand Down Expand Up @@ -2355,6 +2374,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"live_gex": 12500000000,
"live_gex_delta": -450000000,
"live_gamma_flip": 595.50,
"gamma_flip_status": "available",
"live_call_wall": 600,
"live_put_wall": 590,
"live_max_pain": 595,
Expand Down Expand Up @@ -2430,7 +2450,8 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"call_wall": 950.0,
"put_wall": 850.0,
"max_pain": 900.0,
"gamma_flip": 905.0
"gamma_flip": 905.0,
"gamma_flip_status": "available"
},
"count": 1,
"signals": [
Expand Down Expand Up @@ -2648,6 +2669,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"net_gex": 1842000000,
"net_dex": 48200000000,
"gamma_flip": 588.50, // nullable
"gamma_flip_status": "available", // null flip -> reason code
"call_wall": 595.0, // nullable
"put_wall": 585.0, // nullable
"magnet": 590.0, // nullable
Expand Down Expand Up @@ -3374,6 +3396,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"alignment": "converging",
"description": "Max pain (545) near gamma flip (546) between walls (538–555) — strong converging magnet.",
"gamma_flip": 546,
"gamma_flip_status": "available",
"call_wall": 555,
"put_wall": 538
},
Expand Down Expand Up @@ -3401,7 +3424,8 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
| `oi_by_strike` | Per-strike OI and volume for calls and puts |
| `max_pain_by_expiration` | Per-expiry max pain with DTE and total OI. Only present when no `?expiration=` filter. |
| `dealer_alignment.alignment` | `converging` (max pain near gamma flip, between walls), `moderate` (between walls, far from flip), `diverging` (outside walls), `unknown` (insufficient data) |
| `dealer_alignment.gamma_flip` | Strike where net GEX crosses zero |
| `dealer_alignment.gamma_flip` | Strike where net GEX crosses zero; `null` when no level is published |
| `dealer_alignment.gamma_flip_status` | `"available"` when a level is published, otherwise the reason code it was withheld (`no_boundary`, `insufficient_local_coverage`, `insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`, `stored_sign_mismatch`, `search_budget`, `quality_budget`). Treat anything other than `"available"` as no level published |
| `dealer_alignment.call_wall` / `put_wall` | Strikes with highest absolute call/put GEX |
| `regime` | `positive_gamma` or `negative_gamma` based on spot vs gamma flip |
| `expected_move.straddle_price` | ATM straddle mid price |
Expand Down Expand Up @@ -4122,7 +4146,8 @@ Full Volatility Risk Premium dashboard. Combines live IV/RV/GEX data with histor
| `regime.gamma` | `string` | `positive_gamma` or `negative_gamma` |
| `regime.vrp_regime` | `string?` | `harvestable`, `event_only`, `toxic_short_vol`, `cheap_convexity`, or `surface_distorted` |
| `regime.net_gex` | `number` | Net gamma exposure ($) |
| `regime.gamma_flip` | `number` | Gamma flip strike |
| `regime.gamma_flip` | `number?` | Gamma flip strike; `null` when no level is published |
| `regime.gamma_flip_status` | `string` | `"available"` when a level is published, otherwise the reason code it was withheld (`no_boundary`, `insufficient_local_coverage`, `insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`, `stored_sign_mismatch`, `search_budget`, `quality_budget`). Treat anything other than `"available"` as no level published |
| **Strategy Scores** (0-100) | | |
| `strategy_scores.short_put_spread` | `number` | Short put spread suitability |
| `strategy_scores.short_strangle` | `number` | Short strangle suitability |
Expand Down Expand Up @@ -4422,7 +4447,7 @@ curl "https://lab.flashalpha.com/v1/strategies/expiry-positioning/SPY?expiry=202

Returns the [strategy decision envelope](#strategy-decision-envelope) with strategy-specific `metrics` and `regime`.

**Notable `metrics`:** `max_pain_strike`, `distance_to_pain_pct`, `oi_concentration_score`, `total_open_interest`, `expiry`, `days_to_expiry`, `gamma_flip`, `call_wall`, `put_wall`, `distance_to_flip_pct`, `spot_position_label`, `underlying_price`.
**Notable `metrics`:** `max_pain_strike`, `distance_to_pain_pct`, `oi_concentration_score`, `total_open_interest`, `expiry`, `days_to_expiry`, `gamma_flip`, `gamma_flip_status`, `call_wall`, `put_wall`, `distance_to_flip_pct`, `spot_position_label`, `underlying_price`.

**`regime` values:** `strong_pin_likely`, `moderate_pin`, `no_pin_setup`.

Expand Down Expand Up @@ -4460,7 +4485,7 @@ curl "https://lab.flashalpha.com/v1/strategies/zero-dte/SPY" \

Returns the [strategy decision envelope](#strategy-decision-envelope) with strategy-specific `metrics` and `regime`.

**Notable `metrics`:** `max_pain_strike`, `distance_to_pain_pct`, `oi_concentration_score`, `total_open_interest`, `gamma_flip`, `call_wall`, `put_wall`, `distance_to_flip_pct`, `spot_position_label`, `minutes_to_close`, `session_open_spot`, `expected_move_today`, `expected_move_consumed_pct`, `theta_acceleration`, `underlying_price`.
**Notable `metrics`:** `max_pain_strike`, `distance_to_pain_pct`, `oi_concentration_score`, `total_open_interest`, `gamma_flip`, `gamma_flip_status`, `call_wall`, `put_wall`, `distance_to_flip_pct`, `spot_position_label`, `minutes_to_close`, `session_open_spot`, `expected_move_today`, `expected_move_consumed_pct`, `theta_acceleration`, `underlying_price`.

**`regime` values:** `pin_risk_positive_gamma`, `range_compression`, `trend_risk_or_no_setup`; plus `no_same_day_expiry` / `no_expiry_chain` (returned with `decision: insufficient_data` when no chain exists for the selected expiry).

Expand Down Expand Up @@ -4496,7 +4521,7 @@ curl "https://lab.flashalpha.com/v1/strategies/dealer-regime/SPY" \

Returns the [strategy decision envelope](#strategy-decision-envelope) with strategy-specific `metrics` and `regime`.

**Notable `metrics`:** `net_gamma`, `net_delta`, `gamma_source`, `gamma_flip`, `call_wall`, `put_wall`, `distance_to_flip_pct`, `spot_position_label`, `net_vex`, `net_chex`, `underlying_price`.
**Notable `metrics`:** `net_gamma`, `net_delta`, `gamma_source`, `gamma_flip`, `gamma_flip_status`, `call_wall`, `put_wall`, `distance_to_flip_pct`, `spot_position_label`, `net_vex`, `net_chex`, `underlying_price`.

**`regime` values:** `positive_gamma_compression`, `negative_gamma_acceleration`, `transition`.

Expand Down Expand Up @@ -5099,6 +5124,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
"event_expiry": "2026-06-12",
"levels": {
"gamma_flip": 210.0,
"gamma_flip_status": "available",
"call_wall": 220.0,
"put_wall": 205.0,
"highest_oi_strike": 215.0
Expand All @@ -5122,6 +5148,7 @@ curl -H "X-Api-Key: YOUR_API_KEY" \
|-------|-------------|
| `event_expiry` | Closest options expiry on or after the earnings date; null if none found. |
| `levels.gamma_flip` | Strike where net GEX flips sign (event-week scope); nullable. |
| `levels.gamma_flip_status` | `"available"` when a level is published, otherwise the reason code it was withheld (`no_boundary`, `insufficient_local_coverage`, `insufficient_quote_quality`, `sensitive_root`, `uncertain_root_path`, `stored_sign_mismatch`, `search_budget`, `quality_budget`). Treat anything other than `"available"` as no level published. |
| `levels.call_wall` / `put_wall` | Largest positive-GEX strike above / largest below spot; nullable. |
| `levels.highest_oi_strike` | Strike with the most open interest (event-week scope); nullable. |
| `gex_by_dte_bucket[]` | Net GEX and contract count for the `pre_event`, `event_week`, and `post_event` expiry buckets (buckets with no contracts are omitted). |
Expand Down
18 changes: 16 additions & 2 deletions examples/quickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,35 @@

from flashalpha import FlashAlpha


def flip(payload):
"""Format ``gamma_flip``, which is ``None`` for most chains.

When no level is published the API returns ``gamma_flip: null`` and a
``gamma_flip_status`` reason code (e.g. ``"no_boundary"``). Anything
other than ``"available"`` means there is no flip to show.
"""
level = payload.get("gamma_flip")
if level is None:
return f"n/a ({payload.get('gamma_flip_status') or 'unavailable'})"
return f"{level:.2f}"


# 1. Initialize with your API key
fa = FlashAlpha("YOUR_API_KEY")

# 2. Get gamma exposure for SPY
gex = fa.gex("SPY")
print(f"SPY Net GEX: ${gex['net_gex']:,.0f}")
print(f"Gamma flip: {gex['gamma_flip']:.2f}")
print(f"Gamma flip: {flip(gex)}")
print(f"Regime: {gex['net_gex_label']}")
print()

# 3. Key support/resistance levels
levels = fa.exposure_levels("SPY")["levels"]
print(f"Call wall (resistance): {levels['call_wall']}")
print(f"Put wall (support): {levels['put_wall']}")
print(f"Gamma flip: {levels['gamma_flip']:.2f}")
print(f"Gamma flip: {flip(levels)}")
print(f"0DTE magnet: {levels['zero_dte_magnet']}")
print()

Expand Down
14 changes: 14 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,22 @@ from flashalpha import StockSummaryResponse, ExposureSummaryResponse

summary: StockSummaryResponse = client.stock_summary("SPY")
gamma_flip = summary["exposure"]["gamma_flip"] # autocompleted

# gamma_flip is None for most chains; the sibling status says why.
# Never format it as a number without checking.
if summary["exposure"]["gamma_flip_status"] != "available":
... # no level published; regime is "unknown"
```

Nullability note: `gamma_flip` (and `live_gamma_flip` on the flow
endpoints) is only populated when the level is well-determined, which is
a minority of chains. Otherwise it is `null`, `regime` is `"unknown"`,
and `gamma_flip_status` carries a reason code (`no_boundary`,
`insufficient_local_coverage`, `insufficient_quote_quality`,
`sensitive_root`, `uncertain_root_path`, `stored_sign_mismatch`,
`search_budget`, `quality_budget`). Only `"available"` guarantees a
number; treat any other value as no level published.

## Tier breakdown

- **Free**: dual-mode preview tier — `stock_summary` returns a
Expand Down
Loading
Loading