Files
PythagorasGoal/tests/test_eq_meanrev.py
T
Adriano Dal Pastro e28176efef research(equity): scalping 'sottoquotate' su IB + check dati dalla rete
Goal: comprare azioni IB quando sottoquotate, con verifica incrociata dei dati.

1) CHECK DATI DALLA RETE (il pezzo richiesto): IB certificato (eq_*, ADJUSTED) vs
   Yahoo adjclose (sorgente rete indipendente, tokenless). Tutti CONCORDE <=1.2bps
   sui rendimenti. Teaching: un confronto naif (adjusted vs grezzo) falso-allarmava
   4/6 ticker a 30-52bps = TUTTO stacco dividendo -> ogni divergenza va SPIEGATA
   prima di gridare 'feed sporco' (v2.0.0). Strumento riutilizzabile (validatore feed
   / pre-trade price-check).

2) Scalping 'sottoquotate': NON testabile (no dati intraday) ne' eseguibile (PDT rule
   5k blocca il day-trading; IB instabile). Versione testabile = swing MR (Connors
   RSI2<10 + MA200, exit MA5): Sharpe modesto (SPY 0.75/0.70) ma NON batte il buy&hold
   (B&H hold 0.81), expo 13%, CAGR 2-5%, fee-sensitive. Niente edge schierabile.

- scripts/research/eq_meanrev_ib.py
- tests/test_eq_meanrev.py (incl. network check)
- docs/diary/2026-06-26-equity-meanrev-network-check.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 19:42:14 +00:00

52 lines
2.0 KiB
Python

"""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