d597fc64e8
S5.1 RIPARATO E RIGENERATO. Filtro condiviso src/live/paper_guard.py (barra open-labeled chiusa = ts + cadenza <= adesso) importato da tutti e 6 i monitor; serie rigenerate dallo stesso start_ts con scripts/live/paper_regen.py (evidenza in *.pre_regen_20260826.*): statarb +1,95 -> -1,61 (il ribaltamento del gate 27/09 previsto dall'audit), dvolspread -14,73 -> -4,41, xsr -4,98 -> -2,72, prevday invariato. Nessuna data di gate si sposta. Guardia cablata in monitor_health: stato PREMATURO (ultima barra che chiude dopo l'mtime, grazia 5 min, open_labeled=False per collect_chain) — sul dato vivo segnala i 5 rotti e tace sui 2 sani; dopo la rigenerazione 7/7 OK. paper_portfolio non rigenerato (GTAA su ADJUSTED_LAST: replay != serie registrata, P12), tolta la coda non chiusa. D6 pagata di nuovo nel fix: asi8 in pandas 3 e' in us, non ns — blindata con test su tre risoluzioni. S5.12 ESTESO: conftest devia anche trades.db (wrapper su connect: il default e' catturato alla definizione) e docs/journal/; book_executions.jsonl sorvegliato con impronta inizio/fine suite. S5.5 FATTO: test_leva_massima cancellato con nota (misurava frac*n_asset: con una chiave di scala avrebbe continuato a passare smettendo di controllare). S5.9 INDAGATO E RIPARATO (r0826_skh_band_drift): il dato regge (taglio 02/07 riproduce l'audit 1,6376, in-sample identico su ogni taglio); la deriva era la finestra hold-out — e la sola settimana 15-22/08 vale +0,35 di Sharpe hold-out. Il test ora taglia il feed al 02/07 e verifica la riproduzione stretta. Suite: 751 passati, 0 falliti. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""Lock del forward-monitor STATARB-RESID (scripts/live/paper_statarb.py):
|
|
* config CONGELATA (W=45, sgn=+1) — non deve cambiare silenziosamente;
|
|
* advance() processa SOLO le barre forward ed è IDEMPOTENTE alla punta;
|
|
* a 1d il libro REAL-$600 traccia il MODELED (haircut di fill piccolo).
|
|
Il segnale è quello esatto dello sweep (orthogonal_signals) → niente reimplementazione.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "scripts" / "live"))
|
|
import paper_statarb as ps # noqa: E402
|
|
|
|
|
|
def _fresh_state(j, back):
|
|
ts, _, pos, _ = ps._signal(j)
|
|
start = int(ts[-back - 1])
|
|
return dict(start_ts=start, last_ts=start, n_bars=0,
|
|
pos_modeled=float(pos[-back - 1]), pos_real=float(pos[-back - 1]),
|
|
cap_modeled=ps.MODELED_CAPITAL, cap_real=ps.REAL_CAPITAL,
|
|
peak_modeled=ps.MODELED_CAPITAL, peak_real=ps.REAL_CAPITAL,
|
|
dd_modeled=0.0, dd_real=0.0, n_trades=0)
|
|
|
|
|
|
def _redirect(tmp_path, monkeypatch):
|
|
monkeypatch.setattr(ps, "STATE_DIR", tmp_path)
|
|
monkeypatch.setattr(ps, "STATE_FILE", tmp_path / "state.json")
|
|
monkeypatch.setattr(ps, "RETURNS_FILE", tmp_path / "returns.jsonl")
|
|
monkeypatch.setattr(ps, "TRADES_FILE", tmp_path / "trades.jsonl")
|
|
|
|
|
|
def test_frozen_config():
|
|
"""La cella vincente in-sample è W=45 sgn=+1 (relative-momentum, non mean-reversion)."""
|
|
assert ps.W_FROZEN == 45
|
|
assert ps.SGN_FROZEN == +1
|
|
|
|
|
|
def test_advance_processes_forward_then_idempotent(tmp_path, monkeypatch):
|
|
_redirect(tmp_path, monkeypatch)
|
|
j = ps.build_joint("1d")
|
|
ts, *_ = ps._signal(j)
|
|
st = _fresh_state(j, back=60)
|
|
st = ps.advance(st, j)
|
|
# ⚠️ 2026-08-26: l'attesa "== 60" codificava il DIFETTO §5.1 — l'ultima barra del
|
|
# pannello e' il giorno IN CORSO e advance() ora la salta finche' non e' chiusa.
|
|
# L'attesa giusta si DERIVA dal filtro di produzione, non si ridichiara (P1).
|
|
from src.live.paper_guard import MS_1D, nuove_chiuse
|
|
attese = len(nuove_chiuse(ts, int(ts[-61]), MS_1D))
|
|
assert attese in (59, 60) # 60 solo se il pannello finisce su barra chiusa
|
|
assert st["n_bars"] == attese
|
|
assert st["last_ts"] == int(ts[attese - 61]) # la punta CHIUSA, non la punta del pannello
|
|
assert st["cap_modeled"] != ps.MODELED_CAPITAL # l'equity si è mossa
|
|
# idempotente: ri-avanzare alla punta non aggiunge barre né muove l'equity
|
|
eq_before = st["cap_modeled"]
|
|
st2 = ps.advance(dict(st), j)
|
|
assert st2["n_bars"] == attese
|
|
assert st2["cap_modeled"] == eq_before
|
|
|
|
|
|
def test_real600_tracks_modeled_low_turnover(tmp_path, monkeypatch):
|
|
_redirect(tmp_path, monkeypatch)
|
|
j = ps.build_joint("1d")
|
|
st = ps.advance(_fresh_state(j, back=250), j)
|
|
rm = st["cap_modeled"] / ps.MODELED_CAPITAL - 1
|
|
rr = st["cap_real"] / ps.REAL_CAPITAL - 1
|
|
assert abs(rm - rr) < 0.05 # haircut di fill piccolo a 1d ($600)
|