@@ -709,3 +709,193 @@ class VrpResponse(TypedDict, total=False):
709709 warnings : List [str ]
710710 # Macro context. See ``VrpMacro``.
711711 macro : VrpMacro
712+
713+
714+ # ─── MaxPain ─────────────────────────────────────────────────────────────────
715+ #
716+ # Typed model for ``GET /v1/maxpain/{symbol}`` (Basic+).
717+ #
718+ # Max pain is the strike where total option-holder intrinsic value across all
719+ # OI in the chain is minimized — equivalently, the strike at which dealers
720+ # (the counterparty) lose the least to expiring contracts. The endpoint also
721+ # overlays GEX-based dealer alignment, a multi-expiry calendar (full chain
722+ # only), and a 0-100 pin probability score.
723+
724+
725+ class MaxPainDistance (TypedDict , total = False ):
726+ """Distance from spot to the max-pain strike."""
727+
728+ # Dollar distance: ``|underlying_price - max_pain_strike|``.
729+ absolute : Optional [float ]
730+ # Percent of spot: ``absolute / underlying_price * 100``. Use this to
731+ # compare across symbols of different price levels.
732+ percent : Optional [float ]
733+ # ``"above"`` (spot > max_pain), ``"below"`` (spot < max_pain), or
734+ # ``"at"`` (within rounding). The signal field uses this + a 5%
735+ # threshold to derive the bullish/bearish/neutral classification.
736+ direction : Optional [Literal ["above" , "below" , "at" ]]
737+
738+
739+ class MaxPainCurveRow (TypedDict , total = False ):
740+ """One row of the strike-by-strike pain curve.
741+
742+ Each row is the dollar pain (intrinsic value × OI × 100 contract
743+ multiplier) summed across all expirations at that strike. The strike
744+ where ``total_pain`` is minimized is the max-pain strike.
745+ """
746+
747+ strike : Optional [float ]
748+ # Dollar intrinsic value of all calls at this strike summed across the
749+ # chain (when spot pins here, this is what call holders collectively win
750+ # vs the dealer side).
751+ call_pain : Optional [float ]
752+ # Dollar intrinsic value of all puts at this strike. Mirror of
753+ # ``call_pain`` for the put side.
754+ put_pain : Optional [float ]
755+ # ``call_pain + put_pain``. The pain curve's minimum identifies max pain.
756+ total_pain : Optional [float ]
757+
758+
759+ class MaxPainOiRow (TypedDict , total = False ):
760+ """One row of the OI-by-strike breakdown.
761+
762+ Per-strike open interest and volume by side. Lets you see where the
763+ OI is concentrated independent of the dollar-weighted pain calculation.
764+
765+ Note: on the Historical API, ``call_volume`` and ``put_volume`` are
766+ always ``0`` (placeholder fields — the minute table doesn't carry
767+ intraday volume).
768+ """
769+
770+ strike : Optional [float ]
771+ call_oi : Optional [int ]
772+ put_oi : Optional [int ]
773+ total_oi : Optional [int ]
774+ call_volume : Optional [int ]
775+ put_volume : Optional [int ]
776+
777+
778+ class MaxPainByExpirationRow (TypedDict , total = False ):
779+ """Per-expiry max-pain breakdown when no ``expiration`` filter is applied.
780+
781+ Lets you see how max pain shifts across the term structure — useful for
782+ spotting cases where the front-week max pain differs sharply from the
783+ LEAP max pain (often a sign of where the dealer flow is most active).
784+
785+ This list is ``None`` when the request specifies an ``expiration``
786+ filter — the response is then scoped to that single expiry and the
787+ multi-expiry view is suppressed.
788+ """
789+
790+ # ``"yyyy-MM-dd"`` of this expiry.
791+ expiration : Optional [str ]
792+ # Max-pain strike for this expiry's option chain alone.
793+ max_pain_strike : Optional [float ]
794+ # Days to expiry (counting from ``as_of``).
795+ dte : Optional [int ]
796+ # Sum of OI across all contracts at this expiry.
797+ total_oi : Optional [int ]
798+
799+
800+ class MaxPainDealerAlignment (TypedDict , total = False ):
801+ """GEX-based dealer-alignment overlay on the max-pain view.
802+
803+ Re-uses the same gamma-exposure inputs as ``/v1/exposure/levels`` and
804+ ``/v1/exposure/summary``. The headline ``alignment`` label tells you
805+ whether dealer hedging will REINFORCE the max-pain pin or fight it:
806+
807+ - ``"converging"``: max pain near gamma flip and between the
808+ walls — dealer hedging supports the pin (strongest pin setup).
809+ - ``"moderate"``: max pain between the walls but far from flip.
810+ - ``"diverging"``: max pain outside the wall range — dealer
811+ hedging actively pushes spot away from max pain.
812+ - ``"unknown"``: insufficient data to classify.
813+ """
814+
815+ alignment : Optional [Literal ["converging" , "moderate" , "diverging" , "unknown" ]]
816+ # Plain-English explanation. Safe to surface verbatim.
817+ description : Optional [str ]
818+ # Strike where net dealer gamma crosses zero. Same definition as
819+ # ``exposure_summary.gamma_flip``.
820+ gamma_flip : Optional [float ]
821+ # Strike with highest absolute call GEX (dealer-side resistance).
822+ call_wall : Optional [float ]
823+ # Strike with highest absolute put GEX (dealer-side support).
824+ put_wall : Optional [float ]
825+
826+
827+ class MaxPainExpectedMove (TypedDict , total = False ):
828+ """Implied move from the ATM straddle, contextualized vs max pain.
829+
830+ Tells you whether the max-pain strike is even reachable within the
831+ options-implied 1σ move. If ``max_pain_within_expected_range`` is
832+ ``False``, the pin is unlikely to play out by expiry under the current
833+ IV regime — the magnet exists but spot probably can't get there.
834+ """
835+
836+ # ATM straddle mid in dollars. Rough proxy for the 1σ implied move.
837+ straddle_price : Optional [float ]
838+ # ATM implied volatility (annualised %, e.g. 18.5 = 18.5%).
839+ atm_iv : Optional [float ]
840+ # ``True`` when ``|spot - max_pain_strike| <= straddle_price``.
841+ max_pain_within_expected_range : Optional [bool ]
842+
843+
844+ class MaxPainResponse (TypedDict , total = False ):
845+ """Max pain dashboard from ``GET /v1/maxpain/{symbol}``.
846+
847+ Returns the strike where total option-holder pain (intrinsic value × OI)
848+ is minimized, plus:
849+
850+ - per-strike pain curve and OI breakdown
851+ - per-expiry calendar (when no ``expiration`` filter is set)
852+ - GEX-based dealer alignment overlay (call wall / put wall /
853+ gamma flip — same numbers as ``/v1/exposure/levels``)
854+ - expected move from the ATM straddle
855+ - 0-100 pin probability composite
856+
857+ The endpoint accepts an optional ``expiration`` query filter
858+ (``yyyy-MM-dd``). When present, the response is scoped to that single
859+ expiry and ``max_pain_by_expiration`` is ``None``. When absent, the
860+ full-chain max pain is returned alongside the multi-expiry calendar.
861+
862+ Returns 403 ``tier_restricted`` for Free-tier users; requires Basic+.
863+ """
864+
865+ symbol : str
866+ underlying_price : Optional [float ]
867+ as_of : str
868+ # The headline number. Strike where total chain pain is minimized.
869+ max_pain_strike : Optional [float ]
870+ # Distance from spot to ``max_pain_strike`` (absolute, percent, direction).
871+ distance : MaxPainDistance
872+ # ``"bullish"`` (spot >= 5% below max_pain — pin attracts upside),
873+ # ``"bearish"`` (>= 5% above), or ``"neutral"`` (within 5%).
874+ signal : Optional [Literal ["bullish" , "bearish" , "neutral" ]]
875+ # Expiration this view is scoped to. When the request omits the
876+ # ``expiration`` filter, this field is the front-month expiry the
877+ # full-chain max pain happened to land on.
878+ expiration : Optional [str ]
879+ # Total put OI / total call OI across the relevant chain. >1.0 means
880+ # put-heavy positioning. Often correlates with ``signal == "bullish"``
881+ # (puts are protection; heavy-put chains often have spot below max pain).
882+ put_call_oi_ratio : Optional [float ]
883+ # Strike-by-strike pain curve. The minimum is at ``max_pain_strike``.
884+ pain_curve : List [MaxPainCurveRow ]
885+ # Per-strike OI + volume breakdown. Same strike grid as ``pain_curve``.
886+ oi_by_strike : List [MaxPainOiRow ]
887+ # Per-expiry calendar. ``None`` when the request specified an expiry.
888+ max_pain_by_expiration : Optional [List [MaxPainByExpirationRow ]]
889+ # GEX-based dealer alignment overlay. See ``MaxPainDealerAlignment``.
890+ dealer_alignment : MaxPainDealerAlignment
891+ # Same gamma classification as on ``exposure_summary``:
892+ # positive_gamma | negative_gamma | neutral | undetermined.
893+ regime : Optional [Literal ["positive_gamma" , "negative_gamma" ,
894+ "neutral" , "undetermined" ]]
895+ # Expected move from the ATM straddle, contextualized vs max pain.
896+ expected_move : MaxPainExpectedMove
897+ # 0-100 composite — likelihood of pinning to ``max_pain_strike``.
898+ # Inputs: OI concentration (30%), magnet proximity (25%), time
899+ # remaining (25%), gamma magnitude (20%). Most meaningful for near-term
900+ # expiries — for LEAPs this score will be low regardless of OI shape.
901+ pin_probability : Optional [int ]
0 commit comments