"""
================================================================================
Strategy **D003** — ``s003``
================================================================================

Theme
-----
**Breakdown / range failure with long convexity.**

Economic idea
-------------
When **close** falls through the trailing 20-session **minimum close**, it can indicate
a local range break (liquidity vacuum, macro surprise, or sector shock transmitted into
SPY). This sleeve buys **ATM straddle** convexity rather than directional puts alone,
betting that the next few sessions will re-price volatility or produce a two-sided move
large enough versus the entry premium. This is **not** the same as a VIX spike strategy:
it is keyed to **price** location relative to its own recent floor.

Entry
-----
1. ``i>=20``.
2. ``close[i] <= roll_low_20[i-1]`` (compare to **prior** session’s trailing min to avoid
   same-bar tautology).
3. ``VIX <= 28`` (avoid paying extreme wings when fear is already maximal).
4. ``n_contracts[i] >= 80``.

Exit
----
Long ATM straddle ~14 DTE, hold **4** sessions.
================================================================================
"""

from __future__ import annotations

import math

import pandas as pd

from RenTech.core.options_data_loader import OptionChain
from RenTech.strategy_stack.diverse_theta_strategies_v1.context import ResearchContext

META = {"sid": "D003", "theme": "breakdown_range", "title": "Close breaks 20d rolling min + capped VIX long straddle"}

HOLD_SESSIONS = 4
TRADE_KIND = "sl"
TRADE_PARAMS = (14,)


def wants_entry(i: int, row: pd.Series, ch: OptionChain, spy: float, ctx: ResearchContext) -> bool:
    if i < 20:
        return False
    prev_lo = ctx.row(i - 1).get("roll_low_20")
    if pd.isna(prev_lo):
        return False
    c = float(row["close"])
    if c > float(prev_lo):
        return False
    vx = ctx.vix(i)
    if not math.isfinite(vx) or vx > 28.0:
        return False
    if ctx.chain_contracts(i) < 80:
        return False
    if not ch.contracts:
        return False
    _ = spy
    return True
