"""P22b: Stability check on best alternative-universe slice.

P22 highlighted $5-$10 / $5M-$25M ADV with +0.51% proxy expectancy / n=218.
Before recommending engine work on this slice, verify the positive expectancy
is stable across subperiods (not driven by 2-3 outliers).
"""
from __future__ import annotations

from pathlib import Path

import pandas as pd

DAILY_DIR = Path("data/cache/daily_snapshots/orb_daily_v46_20260423")
WINDOW_START = pd.Timestamp("2025-07-09")
WINDOW_END = pd.Timestamp("2026-04-23")
GAP_THR = 0.02
TOP_N = 5

PRICE_LO, PRICE_HI = 5.0, 10.0
ADV_LO, ADV_HI = 5e6, 25e6


def main() -> None:
    files = sorted(DAILY_DIR.glob("*.parquet"))
    rows = []
    for fp in files:
        try:
            df = pd.read_parquet(fp)
        except Exception:
            continue
        if len(df) < 25:
            continue
        sym = fp.stem
        df = df.sort_values("date").reset_index(drop=True)
        df["date"] = pd.to_datetime(df["date"])
        df["prev_close"] = df["close"].shift(1)
        df["gap"] = (df["open"] / df["prev_close"]) - 1.0
        df["oc_ret"] = (df["close"] / df["open"]) - 1.0
        df["dollar_vol"] = df["close"] * df["volume"]
        df["adv20"] = df["dollar_vol"].rolling(20).mean()
        sub = df[
            (df["date"] >= WINDOW_START)
            & (df["date"] <= WINDOW_END)
            & df["gap"].notna()
            & df["adv20"].notna()
            & (df["close"] >= PRICE_LO) & (df["close"] < PRICE_HI)
            & (df["adv20"] >= ADV_LO) & (df["adv20"] < ADV_HI)
            & (df["gap"] >= GAP_THR)
        ].copy()
        if len(sub):
            sub["ticker"] = sym
            rows.append(sub[["ticker", "date", "gap", "oc_ret"]])

    big = pd.concat(rows, ignore_index=True)
    picks = big.sort_values(["date", "gap"], ascending=[True, False]).groupby("date").head(TOP_N)
    print(f"Total picks: {len(picks)} on {picks['date'].nunique()} days, {picks['ticker'].nunique()} unique tickers")
    print(f"Mean expectancy: {picks['oc_ret'].mean()*100:+.4f}%")
    print(f"Median: {picks['oc_ret'].median()*100:+.4f}%")
    print(f"Std: {picks['oc_ret'].std()*100:.3f}%")
    print(f"Cum: {picks['oc_ret'].sum()*100:+.2f}%")
    print()

    # Subperiod splits
    picks["date"] = pd.to_datetime(picks["date"])
    picks_sorted = picks.sort_values("date").reset_index(drop=True)
    n = len(picks_sorted)
    q1 = picks_sorted.iloc[: n // 4]
    q2 = picks_sorted.iloc[n // 4 : n // 2]
    q3 = picks_sorted.iloc[n // 2 : 3 * n // 4]
    q4 = picks_sorted.iloc[3 * n // 4 :]
    print(f"=== Subperiod expectancy (4 quartiles by trade index, ~chronological) ===")
    for i, qq in enumerate([q1, q2, q3, q4], 1):
        print(f"Q{i}: n={len(qq):3d}  date_range={qq['date'].min().date()} → {qq['date'].max().date()}  expectancy={qq['oc_ret'].mean()*100:+.4f}%  cum={qq['oc_ret'].sum()*100:+.2f}%")

    # Outlier check — top 5 wins / losses contribution
    s = picks_sorted["oc_ret"].sort_values(ascending=False)
    top5_contrib = s.head(5).sum()
    bot5_contrib = s.tail(5).sum()
    total = s.sum()
    print()
    print(f"=== Outlier sensitivity ===")
    print(f"Top 5 picks contribute: {top5_contrib*100:+.2f}% of {total*100:+.2f}% cum ({top5_contrib/total*100:+.1f}%)")
    print(f"Bottom 5 picks contribute: {bot5_contrib*100:+.2f}% of {total*100:+.2f}% cum ({bot5_contrib/total*100:+.1f}%)")
    print(f"Trimmed (drop top 5 + bottom 5) expectancy: {((total - top5_contrib - bot5_contrib) / (n - 10))*100:+.4f}% (vs full {picks['oc_ret'].mean()*100:+.4f}%)")
    print()
    print(f"=== Top 5 winners ===")
    top5 = picks_sorted.sort_values("oc_ret", ascending=False).head(5)
    print(top5.to_string(index=False))
    print()
    print(f"=== Top 5 losers ===")
    print(picks_sorted.sort_values("oc_ret", ascending=True).head(5).to_string(index=False))


if __name__ == "__main__":
    main()
