diff --git a/AGENTS.md b/AGENTS.md index 4b14712..7efc17c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 @@ -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 diff --git a/README.md b/README.md index e30c18f..d9f6bf2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,9 @@ 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}") @@ -31,6 +33,15 @@ for strike in gex["strikes"][:5]: 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 @@ -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+) diff --git a/docs/api.md b/docs/api.md index 2904d29..c314e45 100644 --- a/docs/api.md +++ b/docs/api.md @@ -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 @@ -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, @@ -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 | @@ -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": [ @@ -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, @@ -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, @@ -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 | @@ -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", @@ -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, @@ -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 @@ -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, @@ -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, @@ -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": [ @@ -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 @@ -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 }, @@ -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 | @@ -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 | @@ -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`. @@ -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). @@ -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`. @@ -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 @@ -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). | diff --git a/examples/quickstart.py b/examples/quickstart.py index d43498d..e68bbb5 100644 --- a/examples/quickstart.py +++ b/examples/quickstart.py @@ -2,13 +2,27 @@ 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() @@ -16,7 +30,7 @@ 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() diff --git a/llms.txt b/llms.txt index 6ab79a4..fed3517 100644 --- a/llms.txt +++ b/llms.txt @@ -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 diff --git a/src/flashalpha/types.py b/src/flashalpha/types.py index 160a19b..f5a5b6c 100644 --- a/src/flashalpha/types.py +++ b/src/flashalpha/types.py @@ -53,6 +53,7 @@ class ZeroDteRegime(TypedDict, total=False): label: str description: str gamma_flip: Optional[float] + gamma_flip_status: Optional[str] spot_vs_flip: Literal["above", "below"] spot_to_flip_pct: Optional[float] distance_to_flip_dollars: Optional[float] @@ -420,6 +421,15 @@ class ExposureSummaryResponse(TypedDict, total=False): # following likely). One of the two or three numbers most experienced # users actually look at on this endpoint. gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published. ``"available"`` when a + # level is returned; otherwise a reason code for the withholding: + # ``"no_boundary"``, ``"stored_sign_mismatch"``, + # ``"insufficient_local_coverage"``, ``"insufficient_quote_quality"``, + # ``"sensitive_root"``, ``"uncertain_root_path"``, ``"search_budget"``, + # ``"quality_budget"``. New codes may be added server-side, so treat + # ANY value other than ``"available"`` as "no flip published" -- in + # that case ``gamma_flip`` is ``None`` and ``regime`` is ``"unknown"``. + gamma_flip_status: Optional[str] # Dealer-positioning regime classification: # - ``"positive_gamma"``: spot above gamma_flip # - ``"negative_gamma"``: spot below gamma_flip @@ -615,6 +625,10 @@ class VrpRegime(TypedDict, total=False): # Strike where net dealer gamma crosses zero. Same as # exposure_summary.gamma_flip. gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published -- ``"available"``, else a + # reason code. Same values as ``ExposureSummaryResponse.gamma_flip_status``; + # treat anything other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] class VrpStrategyScores(TypedDict, total=False): @@ -863,6 +877,10 @@ class MaxPainDealerAlignment(TypedDict, total=False): # Strike where net dealer gamma crosses zero. Same definition as # ``exposure_summary.gamma_flip``. gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published -- ``"available"``, else a + # reason code. Same values as ``ExposureSummaryResponse.gamma_flip_status``; + # treat anything other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] # Strike with highest absolute call GEX (dealer-side resistance). call_wall: Optional[float] # Strike with highest absolute put GEX (dealer-side support). @@ -1194,6 +1212,10 @@ class StockSummaryExposure(TypedDict, total=False): # gamma regime (mean-reverting); spot BELOW = negative-gamma # (trend-following). gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published -- ``"available"``, else a + # reason code. Same values as ``ExposureSummaryResponse.gamma_flip_status``; + # treat anything other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] # Strike with highest absolute call GEX (dealer-side resistance). call_wall: Optional[float] # Strike with highest absolute put GEX (dealer-side support). @@ -1446,6 +1468,10 @@ class NarrativeData(TypedDict, total=False): vix: Optional[float] # Strike where net dealer gamma crosses zero. gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published -- ``"available"``, else a + # reason code. Same values as ``ExposureSummaryResponse.gamma_flip_status``; + # treat anything other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] # Strike with the highest absolute call GEX (dealer-side resistance). call_wall: Optional[float] # Strike with the highest absolute put GEX (dealer-side support). @@ -1535,6 +1561,10 @@ class ExposureLevels(TypedDict, total=False): # Strike where net dealer gamma crosses zero. Spot above = positive- # gamma regime; spot below = negative-gamma. gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published -- ``"available"``, else a + # reason code. Same values as ``ExposureSummaryResponse.gamma_flip_status``; + # treat anything other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] # Strike carrying the largest positive net GEX (dealers most long # gamma here โ€” strongest local mean-reversion magnet). max_positive_gamma: Optional[float] @@ -2282,6 +2312,10 @@ class GexResponse(TypedDict, total=False): as_of: str # Strike where net dealer gamma crosses zero across the chain. gamma_flip: Optional[float] + # Why ``gamma_flip`` is or isn't published -- ``"available"``, else a + # reason code. Same values as ``ExposureSummaryResponse.gamma_flip_status``; + # treat anything other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] # Net GEX across the chain (dollars per 1% spot move). net_gex: Optional[float] # Plain-text categorical label for the GEX regime @@ -2823,6 +2857,15 @@ class FlowLevelsResponse(TypedDict, total=False): expiry: Optional[str] # Spot where live net dealer gamma crosses zero. ``None`` if no flip. live_gamma_flip: Optional[float] + # Why ``live_gamma_flip`` is or isn't published. NOTE the asymmetric + # name: the value field is ``live_gamma_flip`` but its status field is + # plain ``gamma_flip_status`` on the wire -- there is no + # ``live_gamma_flip_status``. ``"available"`` when a level is returned; + # otherwise a reason code (``"no_boundary"``, + # ``"insufficient_local_coverage"``, ...). See + # ``ExposureSummaryResponse.gamma_flip_status`` for the full code list; + # treat ANY value other than ``"available"`` as no flip published. + gamma_flip_status: Optional[str] # Strike of the largest live call-gamma concentration (upside magnet). live_call_wall: Optional[float] # Strike of the largest live put-gamma concentration (downside magnet). @@ -2959,6 +3002,11 @@ class FlowGexResponse(TypedDict, total=False): live_net_gex_label: str # Live gamma-flip spot. ``None`` if no sign change. live_gamma_flip: Optional[float] + # Why ``live_gamma_flip`` is or isn't published -- ``"available"``, else + # a reason code. Named ``gamma_flip_status`` (not + # ``live_gamma_flip_status``) on the wire; see + # ``FlowLevelsResponse.gamma_flip_status``. + gamma_flip_status: Optional[str] # Per-strike rows (identical schema to settled GEX). See ``GexStrikeRow``. strikes: List[GexStrikeRow] @@ -3097,6 +3145,11 @@ class FlowLiveResponse(TypedDict, total=False): live_gex_delta: Optional[float] # Live gamma-flip spot. ``None`` if no sign change. live_gamma_flip: Optional[float] + # Why ``live_gamma_flip`` is or isn't published -- ``"available"``, else + # a reason code. Named ``gamma_flip_status`` (not + # ``live_gamma_flip_status``) on the wire; see + # ``FlowLevelsResponse.gamma_flip_status``. + gamma_flip_status: Optional[str] # Largest live call-gamma concentration strike (upside magnet). live_call_wall: Optional[float] # Largest live put-gamma concentration strike (downside magnet). @@ -3698,6 +3751,7 @@ class FlowSignalsChain(TypedDict, total=False): put_wall: Optional[float] max_pain: Optional[float] gamma_flip: Optional[float] + gamma_flip_status: Optional[str] class FlowSignalScoreBreakdown(TypedDict, total=False): @@ -4204,6 +4258,7 @@ class EarningsDealerLevels(TypedDict, total=False): """Dealer levels scoped to the event-week expiries.""" gamma_flip: Optional[float] + gamma_flip_status: Optional[str] call_wall: Optional[float] put_wall: Optional[float] highest_oi_strike: Optional[float] @@ -5176,6 +5231,7 @@ class FlowZeroDteSeriesBar(TypedDict, total=False): net_gex: Optional[float] net_dex: Optional[float] gamma_flip: Optional[float] + gamma_flip_status: Optional[str] call_wall: Optional[float] put_wall: Optional[float] magnet: Optional[float]