"""Lock 2026-06-26 (equity MR + check rete): la mean-reversion "sottoquotata" daily NON batte il buy&hold, e il check dati dalla rete (IB vs Yahoo) deve concordare adjusted-vs-adjusted. Diario docs/diary/2026-06-26-equity-meanrev-network-check.md.""" import sys from pathlib import Path import numpy as np import pytest ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "scripts" / "research")) from eq_meanrev_ib import rsi, mr_target, backtest, metrics, buyhold, yahoo_daily # type: ignore from eqlib import load_eq # type: ignore import pandas as pd def test_rsi_in_bounds(): c = np.cumprod(1 + np.linspace(-0.02, 0.02, 300)) r = rsi(c, 2) assert np.nanmin(r) >= 0.0 and np.nanmax(r) <= 100.0 def test_mr_target_binary_and_has_trades(): """La posizione รจ 0/1 ed entra solo da sottoquotata (genera scambi, exposure < 50%).""" c = load_eq("SPY")["close"].astype(float).values pos = mr_target(c) assert set(np.unique(pos)).issubset({0.0, 1.0}) expo = pos.mean() assert 0.02 < expo < 0.5 # mean-reversion = poco investito def test_meanrev_does_not_beat_buyhold_holdout(): """Verdetto onesto: la MR daily NON batte il buy&hold risk-adjusted nel hold-out su SPY.""" HO = pd.Timestamp("2015-01-01", tz="UTC") mr = metrics(backtest("SPY"), lo=HO)["sharpe"] bh = buyhold("SPY", lo=HO)["sharpe"] assert mr <= bh + 0.05 # non supera il benchmark (entro rumore) @pytest.mark.network def test_ib_feed_concords_with_network_source(): """Check dati dalla rete: i rendimenti IB (adjusted) concordano con Yahoo adjclose a pochi bps.""" ib = load_eq("SPY")["close"].astype(float); ib.index = ib.index.normalize() yh = yahoo_daily("SPY")["adjclose"] J = pd.concat({"a": ib, "b": yh}, axis=1, join="inner").dropna().tail(120) d = (J["a"].pct_change() - J["b"].pct_change()).abs().dropna() assert d.max() < 0.001 # <10bps adjusted-vs-adjusted assert abs(J["a"].iloc[-1] / J["b"].iloc[-1] - 1) < 0.002