"""Test dei due gate codificati il 2026-07-26-bis: `implausible_sharpe` e `anchor_luck_band`. Entrambi erano DEBITO DICHIARATO del progetto (raccomandati piu' volte, mai scritti). Un gate e' utile solo se ha **potenza** (segnala cio' che deve) E **specificita'** (non segnala il resto): un gate che non scatta mai passerebbe inosservato per mesi sembrando una buona notizia. Quindi qui si testano sempre le due facce. Il caso piu' importante e' `test_sleeve_flat_la_maggior_parte_del_tempo_non_e_implausibile`: la prima stesura del gate contava le perdite su TUTTE le barre e segnalava VRP01 (settimanale su griglia giornaliera, 94% di zeri) — un falso positivo su uno sleeve IN PRODUZIONE. """ from __future__ import annotations import sys from pathlib import Path import numpy as np import pytest ROOT = Path(__file__).resolve().parents[1] for p in (ROOT, ROOT / "scripts" / "research" / "alt"): sys.path.insert(0, str(p)) pytest.importorskip("pandas") import pandas as pd # noqa: E402 import altlib as al # noqa: E402 def _idx(n): return pd.date_range("2020-01-01", periods=n, freq="1D", tz="UTC") def _noise(n=900, mu=0.0006, sd=0.011, seed=1): rng = np.random.default_rng(seed) return pd.Series(rng.normal(mu, sd, n), index=_idx(n)) # --------------------------------------------------------------- implausible_sharpe: POTENZA def test_firma_cc01_viene_segnalata(): """CC01 (Sharpe modellato 11-13, maxDD 0.3%) passava OGNI gate del marginal scorer: e' il punto cieco che questo gate esiste per coprire.""" rng = np.random.default_rng(0) s = pd.Series(0.0006 + rng.normal(0, 0.00012, 900), index=_idx(900)) r = al.implausible_sharpe(s) assert r["implausible"] assert any("Sharpe" in x for x in r["reasons"]) def test_zero_perdite_su_molti_trade_attiva_la_regola_del_tre(): """0/142 non significa 'senza rischio': lascia un tasso vero fino a ~3/142 = 2.1%, e su un payoff deep-OTM quel residuo ribalta l'expectancy.""" s = pd.Series(np.abs(_noise(900, 0.0008, 0.0003, seed=2).values), index=_idx(900)) r = al.implausible_sharpe(s, n_trades=142, n_losing_trades=0) assert r["implausible"] assert r["loss_rate_ub95"] == pytest.approx(3.0 / 142) assert any("regola del tre" in x for x in r["reasons"]) def test_calmar_assurdo_viene_segnalato_anche_a_sharpe_moderato(): """Il modo di perdita puo' mancare senza che lo Sharpe esploda: un DD ~0 basta da solo.""" v = np.full(900, 0.0004) v[::120] = -0.00005 # perdite presenti ma minuscole -> DD ~0 r = al.implausible_sharpe(pd.Series(v, index=_idx(900))) assert r["implausible"] assert any("Calmar" in x or "maxDD" in x for x in r["reasons"]) # --------------------------------------------------------------- implausible_sharpe: SPECIFICITA' def test_rumore_a_sharpe_uno_non_viene_segnalato(): """Specificita': senza questo, un gate sempre-acceso sarebbe inutile quanto uno sempre-spento.""" assert not al.implausible_sharpe(_noise())["implausible"] def test_sleeve_flat_la_maggior_parte_del_tempo_non_e_implausibile(): """IL falso positivo della prima stesura. VRP01 e' settimanale su griglia giornaliera: 94% di zeri. Contando le perdite su TUTTE le barre esce 0.96% ('coda assente') invece del 16.5% reale sulle barre ATTIVE -> il gate segnalava uno sleeve in PRODUZIONE.""" rng = np.random.default_rng(3) v = np.zeros(900) act = np.arange(0, 900, 7) # una barra attiva a settimana v[act] = np.where(rng.random(len(act)) < 0.17, -rng.uniform(0.02, 0.05, len(act)), # ~17% di settimane in perdita rng.uniform(0.004, 0.012, len(act))) r = al.implausible_sharpe(pd.Series(v, index=_idx(900))) assert r["n_active"] == len(act) assert 0.05 < r["loss_frac"] < 0.35, r["loss_frac"] assert not r["implausible"], r["reasons"] def test_poche_barre_attive_sospende_il_giudizio_invece_di_promuovere(): """Con pochissime osservazioni attive la coda non e' 'assente', e' NON MISURABILE: il gate deve bloccare, non assolvere.""" v = np.zeros(900) v[::100] = 0.01 r = al.implausible_sharpe(pd.Series(v, index=_idx(900))) assert r["implausible"] assert any("non e' misurabile" in x for x in r["reasons"]) def test_indice_non_temporale_e_un_errore_esplicito(): """Senza indice temporale l'annualizzazione sarebbe arbitraria: meglio alzare che indovinare.""" with pytest.raises(TypeError): al.implausible_sharpe(pd.Series(np.zeros(100) + 0.001)) # --------------------------------------------------------------- anchor_luck_band def test_banda_dancora_riconosce_il_massimo_come_fortuna(): """Se l'ancora canonica e' la MIGLIORE della griglia, il gate deve dirlo: pctl alto e stima onesta = mediana, non il valore canonico.""" vals = {o: 0.05 * o for o in range(10)} # 9 = il migliore b = al.anchor_luck_band(lambda o: vals[o], range(10), canonical=9, metric=lambda x: x) assert b["canonical_pctl"] >= 0.85 assert b["median"] == pytest.approx(0.225) assert b["luck"] > 0 def test_banda_dancora_non_penalizza_chi_e_al_centro(): """VRP01 e' l'unico sleeve la cui ancora canonica NON e' fortunata: il gate non deve inventare una penalita' dove non c'e'.""" vals = {o: 1.0 + 0.01 * o for o in range(9)} b = al.anchor_luck_band(lambda o: vals[o], range(9), canonical=4, metric=lambda x: x) assert 0.3 <= b["canonical_pctl"] <= 0.7 assert abs(b["luck"]) < 1e-9 assert b["gate_pass"] def test_segno_che_dipende_dallancora_non_passa(): """Il senso del gate: se la meta' delle ancore e' negativa, il titolo e' una scelta d'ancora.""" vals = {o: (1.0 if o % 2 else -1.0) for o in range(10)} b = al.anchor_luck_band(lambda o: vals[o], range(10), canonical=1, metric=lambda x: x) assert not b["gate_pass"] def test_ancore_rotte_non_uccidono_la_banda(): """Un'ancora che solleva (dati mancanti a quell'offset) va saltata, non propagata.""" def f(o): if o == 3: raise ValueError("feed mancante") return 1.0 b = al.anchor_luck_band(f, range(8), canonical=0, metric=lambda x: x) assert b["n_anchors"] == 7 and b["gate_pass"] # --------------------------------------------------------------- anchor_luck_delta def test_delta_appaiato_ribalta_la_differenza_di_mediane(): """IL motivo per cui questa funzione esiste (errore commesso il 26/07 sul recupero on-book di SKH01): le mediane di A e B cadono su offset DIVERSI, quindi la loro differenza confronta ancore diverse e puo' RIBALTARE il verdetto. NB: la costruzione non e' banale — se A battesse B a OGNI offset le mediane non potrebbero invertirsi (la mediana e' monotona). Il caso reale, e quello qui: A vince nella MAGGIORANZA degli offset (4/5) ma la sua mediana cade sotto quella di B.""" A = {0: 1.0, 1: 1.0, 2: 3.0, 3: 5.0, 4: 6.0} # mediana 3 B = {0: 0.0, 1: 0.0, 2: 4.0, 3: 4.5, 4: 5.0} # mediana 4 naive = float(np.median(list(A.values())) - np.median(list(B.values()))) d = al.anchor_luck_delta(lambda o: A[o], lambda o: B[o], range(5), metric=lambda x: x) assert naive < 0, "il test non direbbe nulla se la statistica ingenua non sbagliasse" assert d["median_paired"] > 0 assert d["n_positive"] == 4 and d["gate_pass"] def test_delta_appaiato_non_promuove_una_vittoria_di_una_sola_ancora(): """Un vantaggio concentrato su un'ancora sola non e' un vantaggio: e' la fortuna di quell'ancora.""" A = {0: 5.0, 1: 0.9, 2: 0.8, 3: 0.7} B = {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0} d = al.anchor_luck_delta(lambda o: A[o], lambda o: B[o], range(4), metric=lambda x: x) assert d["median_paired"] < 0 and not d["gate_pass"]