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>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user