|
| 1 | +"""Typed response models for FlashAlpha SDK. |
| 2 | +
|
| 3 | +These are ``TypedDict`` aliases — at runtime each is a plain ``dict``. Existing |
| 4 | +code that does ``result["field"]`` keeps working unchanged. Static type |
| 5 | +checkers (mypy/pyright) and IDEs see the field shape and provide autocomplete. |
| 6 | +
|
| 7 | +Currently typed: |
| 8 | + - ``ZeroDteResponse`` (full payload of GET /v1/exposure/zero-dte/{symbol}) |
| 9 | +
|
| 10 | +All numeric fields are typed ``Optional[float]``/``Optional[int]`` because |
| 11 | +the API returns ``null`` for any value it can't compute (insufficient data, |
| 12 | +market closed, etc.). Treat the typed shape as a *hint*, not a guarantee — |
| 13 | +unknown fields added by the API in future revisions will still pass through. |
| 14 | +""" |
| 15 | + |
| 16 | +from typing import List, Literal, Optional, TypedDict |
| 17 | + |
| 18 | + |
| 19 | +class ZeroDteRegime(TypedDict, total=False): |
| 20 | + label: str |
| 21 | + description: str |
| 22 | + gamma_flip: Optional[float] |
| 23 | + spot_vs_flip: Literal["above", "below"] |
| 24 | + spot_to_flip_pct: Optional[float] |
| 25 | + distance_to_flip_dollars: Optional[float] |
| 26 | + distance_to_flip_sigmas: Optional[float] |
| 27 | + |
| 28 | + |
| 29 | +class ZeroDteExposures(TypedDict, total=False): |
| 30 | + net_gex: float |
| 31 | + net_dex: float |
| 32 | + net_vex: float |
| 33 | + net_chex: float |
| 34 | + pct_of_total_gex: Optional[float] |
| 35 | + total_chain_net_gex: float |
| 36 | + |
| 37 | + |
| 38 | +class ZeroDteExpectedMove(TypedDict, total=False): |
| 39 | + implied_1sd_dollars: Optional[float] |
| 40 | + implied_1sd_pct: Optional[float] |
| 41 | + remaining_1sd_dollars: Optional[float] |
| 42 | + remaining_1sd_pct: Optional[float] |
| 43 | + upper_bound: Optional[float] |
| 44 | + lower_bound: Optional[float] |
| 45 | + straddle_price: Optional[float] |
| 46 | + atm_iv: Optional[float] |
| 47 | + |
| 48 | + |
| 49 | +class ZeroDtePinComponents(TypedDict, total=False): |
| 50 | + oi_score: int |
| 51 | + proximity_score: int |
| 52 | + time_score: int |
| 53 | + gamma_score: int |
| 54 | + |
| 55 | + |
| 56 | +class ZeroDtePinRisk(TypedDict, total=False): |
| 57 | + magnet_strike: Optional[float] |
| 58 | + magnet_gex: Optional[float] |
| 59 | + distance_to_magnet_pct: Optional[float] |
| 60 | + pin_score: int |
| 61 | + components: ZeroDtePinComponents |
| 62 | + max_pain: Optional[float] |
| 63 | + oi_concentration_top3_pct: Optional[float] |
| 64 | + description: str |
| 65 | + |
| 66 | + |
| 67 | +class ZeroDteHedgingBucket(TypedDict, total=False): |
| 68 | + dealer_shares_to_trade: float |
| 69 | + direction: Literal["buy", "sell"] |
| 70 | + notional_usd: float |
| 71 | + |
| 72 | + |
| 73 | +class ZeroDteHedging(TypedDict, total=False): |
| 74 | + spot_up_10bp: ZeroDteHedgingBucket |
| 75 | + spot_down_10bp: ZeroDteHedgingBucket |
| 76 | + spot_up_25bp: ZeroDteHedgingBucket |
| 77 | + spot_down_25bp: ZeroDteHedgingBucket |
| 78 | + spot_up_half_pct: ZeroDteHedgingBucket |
| 79 | + spot_down_half_pct: ZeroDteHedgingBucket |
| 80 | + spot_up_1pct: ZeroDteHedgingBucket |
| 81 | + spot_down_1pct: ZeroDteHedgingBucket |
| 82 | + convexity_at_spot: Optional[float] |
| 83 | + |
| 84 | + |
| 85 | +class ZeroDteDecay(TypedDict, total=False): |
| 86 | + net_theta_dollars: Optional[float] |
| 87 | + theta_per_hour_remaining: Optional[float] |
| 88 | + charm_regime: str |
| 89 | + charm_description: str |
| 90 | + gamma_acceleration: Optional[float] |
| 91 | + description: str |
| 92 | + |
| 93 | + |
| 94 | +class ZeroDteVolContext(TypedDict, total=False): |
| 95 | + zero_dte_atm_iv: Optional[float] |
| 96 | + seven_dte_atm_iv: Optional[float] |
| 97 | + iv_ratio_0dte_7dte: Optional[float] |
| 98 | + vix: Optional[float] |
| 99 | + vanna_exposure: Optional[float] |
| 100 | + vanna_interpretation: str |
| 101 | + description: str |
| 102 | + |
| 103 | + |
| 104 | +class ZeroDteFlow(TypedDict, total=False): |
| 105 | + total_volume: int |
| 106 | + call_volume: int |
| 107 | + put_volume: int |
| 108 | + net_call_minus_put_volume: int |
| 109 | + total_oi: int |
| 110 | + call_oi: int |
| 111 | + put_oi: int |
| 112 | + pc_ratio_volume: Optional[float] |
| 113 | + pc_ratio_oi: Optional[float] |
| 114 | + volume_to_oi_ratio: Optional[float] |
| 115 | + atm_volume_share_pct: Optional[float] |
| 116 | + top3_strike_volume_pct: Optional[float] |
| 117 | + |
| 118 | + |
| 119 | +class ZeroDteLevels(TypedDict, total=False): |
| 120 | + call_wall: Optional[float] |
| 121 | + call_wall_gex: Optional[float] |
| 122 | + call_wall_strength: Optional[float] |
| 123 | + distance_to_call_wall_pct: Optional[float] |
| 124 | + put_wall: Optional[float] |
| 125 | + put_wall_gex: Optional[float] |
| 126 | + put_wall_strength: Optional[float] |
| 127 | + distance_to_put_wall_pct: Optional[float] |
| 128 | + distance_to_magnet_dollars: Optional[float] |
| 129 | + highest_oi_strike: Optional[float] |
| 130 | + highest_oi_total: Optional[int] |
| 131 | + max_positive_gamma: Optional[float] |
| 132 | + max_negative_gamma: Optional[float] |
| 133 | + level_cluster_score: Optional[int] |
| 134 | + |
| 135 | + |
| 136 | +class ZeroDteLiquidity(TypedDict, total=False): |
| 137 | + atm_spread_pct: Optional[float] |
| 138 | + weighted_spread_pct: Optional[float] |
| 139 | + execution_score: Optional[int] |
| 140 | + |
| 141 | + |
| 142 | +class ZeroDteMetadata(TypedDict, total=False): |
| 143 | + snapshot_age_seconds: Optional[float] |
| 144 | + chain_contract_count: int |
| 145 | + data_quality_score: Optional[int] |
| 146 | + greek_smoothness_score: Optional[int] |
| 147 | + |
| 148 | + |
| 149 | +class ZeroDteStrike(TypedDict, total=False): |
| 150 | + strike: float |
| 151 | + distance_from_spot_pct: float |
| 152 | + call_symbol: str |
| 153 | + put_symbol: str |
| 154 | + call_gex: Optional[float] |
| 155 | + put_gex: Optional[float] |
| 156 | + net_gex: Optional[float] |
| 157 | + call_dex: Optional[float] |
| 158 | + put_dex: Optional[float] |
| 159 | + net_dex: Optional[float] |
| 160 | + net_vex: Optional[float] |
| 161 | + net_chex: Optional[float] |
| 162 | + call_oi: Optional[int] |
| 163 | + put_oi: Optional[int] |
| 164 | + call_volume: Optional[int] |
| 165 | + put_volume: Optional[int] |
| 166 | + gex_share_pct: Optional[float] |
| 167 | + oi_share_pct: Optional[float] |
| 168 | + volume_share_pct: Optional[float] |
| 169 | + call_iv: Optional[float] |
| 170 | + put_iv: Optional[float] |
| 171 | + call_delta: Optional[float] |
| 172 | + put_delta: Optional[float] |
| 173 | + call_gamma: Optional[float] |
| 174 | + put_gamma: Optional[float] |
| 175 | + call_theta: Optional[float] |
| 176 | + put_theta: Optional[float] |
| 177 | + call_mid: Optional[float] |
| 178 | + put_mid: Optional[float] |
| 179 | + call_spread_pct: Optional[float] |
| 180 | + put_spread_pct: Optional[float] |
| 181 | + |
| 182 | + |
| 183 | +class ZeroDteResponse(TypedDict, total=False): |
| 184 | + """Full response for GET /v1/exposure/zero-dte/{symbol}. |
| 185 | +
|
| 186 | + On weekends/holidays or symbols without 0DTE today, ``no_zero_dte`` is |
| 187 | + ``True`` and most fields are absent — only ``symbol``, ``as_of``, |
| 188 | + ``message``, and ``next_zero_dte_expiry`` are populated. |
| 189 | + """ |
| 190 | + |
| 191 | + symbol: str |
| 192 | + underlying_price: float |
| 193 | + expiration: Optional[str] |
| 194 | + as_of: str |
| 195 | + market_open: bool |
| 196 | + time_to_close_hours: Optional[float] |
| 197 | + time_to_close_pct: Optional[float] |
| 198 | + regime: ZeroDteRegime |
| 199 | + exposures: ZeroDteExposures |
| 200 | + expected_move: ZeroDteExpectedMove |
| 201 | + pin_risk: ZeroDtePinRisk |
| 202 | + hedging: ZeroDteHedging |
| 203 | + decay: ZeroDteDecay |
| 204 | + vol_context: ZeroDteVolContext |
| 205 | + flow: ZeroDteFlow |
| 206 | + levels: ZeroDteLevels |
| 207 | + liquidity: ZeroDteLiquidity |
| 208 | + metadata: ZeroDteMetadata |
| 209 | + strikes: List[ZeroDteStrike] |
| 210 | + # Optional — only present near close (<5 min) when greeks may be unstable. |
| 211 | + warnings: List[str] |
| 212 | + # ── No-0DTE fallback ───────────────────────────────────────────── |
| 213 | + no_zero_dte: bool |
| 214 | + message: str |
| 215 | + next_zero_dte_expiry: Optional[str] |
0 commit comments