"""EQLIB — harness di ricerca EQUITY/ETF (branch research/equities-ib).

Legge la CACHE su disco (data/raw/eq_*.parquet, ADJUSTED_LAST, scritta una volta da
fetch_ib_equities.py) — MAI da IB. `lru_cache` -> ogni parquet si legge una sola volta per processo.
"Memorizza i dati per non rileggerli ogni volta": la persistenza e' il parquet su disco + questa cache.

Espone: universi (SECTORS/BROAD), load_eq(sym), panel(universe) allineato, e riusa lo scorer
indurito di altlib (_sh, _dd_ret, _to_daily, marginal_vs_tp01) per giudicare i candidati con la
stessa disciplina del lato crypto.
"""
from functools import lru_cache
from pathlib import Path
import numpy as np, pandas as pd

ROOT = Path(__file__).resolve().parents[2]
RAW = ROOT / "data" / "raw"

# 11 SPDR settoriali. I 9 "classici" (sotto SECTORS_CLASSIC) partono 1998; XLRE 2015, XLC 2018.
SECTORS = ["XLK", "XLF", "XLE", "XLV", "XLI", "XLP", "XLY", "XLU", "XLB", "XLRE", "XLC"]
SECTORS_CLASSIC = ["XLK", "XLF", "XLE", "XLV", "XLI", "XLP", "XLY", "XLU", "XLB"]   # storia lunga (1998+)
BROAD = ["SPY", "QQQ", "IWM", "TLT", "GLD", "HYG"]


@lru_cache(maxsize=64)
def load_eq(sym: str) -> pd.DataFrame:
    """OHLCV aggiustato (dividendi+split) per `sym`, indicizzato datetime UTC. Cache su disco -> RAM."""
    p = RAW / f"eq_{sym.lower()}_1d.parquet"
    if not p.exists():
        raise FileNotFoundError(f"{p} assente — gira: uv run --with ib_async python scripts/research/fetch_ib_equities.py")
    d = pd.read_parquet(p).copy()
    d.index = pd.to_datetime(d["timestamp"], unit="ms", utc=True)
    return d[["open", "high", "low", "close", "volume"]]


@lru_cache(maxsize=16)
def _close_panel(universe: tuple) -> pd.DataFrame:
    cols = {s: load_eq(s)["close"].astype(float) for s in universe}
    return pd.concat(cols, axis=1).sort_index()


def panel(universe=tuple(SECTORS), how: str = "inner") -> pd.DataFrame:
    """Prezzi close aggiustati [date x asset]. how='inner' = date comuni a TUTTI (start = ETF piu' giovane);
    'outer' = unione (NaN dove un ETF non esiste ancora)."""
    P = _close_panel(tuple(universe))
    return P.dropna(how="any") if how == "inner" else P


def describe(universe=None):
    universe = universe or (SECTORS + BROAD)
    print(f"  {'sym':6} {'barre':>6} {'da':>11} {'a':>11} {'anni':>5}")
    for s in universe:
        try:
            d = load_eq(s)
            print(f"  {s:6} {len(d):>6} {str(d.index[0].date()):>11} {str(d.index[-1].date()):>11} "
                  f"{(d.index[-1]-d.index[0]).days/365.25:>5.1f}")
        except FileNotFoundError:
            print(f"  {s:6}  (assente)")
    Pc = panel(SECTORS_CLASSIC); Pa = panel(SECTORS)
    print(f"\n  panel 9 settori CLASSICI: {Pc.shape[1]}x{len(Pc)}  start comune {Pc.index[0].date()}")
    print(f"  panel 11 settori        : {Pa.shape[1]}x{len(Pa)}  start comune {Pa.index[0].date()}")


if __name__ == "__main__":
    print("=" * 70); print("  EQLIB — cache equity su disco (nessun IB)"); print("=" * 70)
    describe()
