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
93 lines
4.9 KiB
Python
93 lines
4.9 KiB
Python
"""Isolamento della suite dai FILE OPERATIVI VIVI.
|
|
|
|
⚠️ PERCHE' ESISTE (2026-08-25, difetto trovato addosso). Lanciando `uv run pytest` la suite ha
|
|
scritto **$5.000 dentro `data/live/equity_seen.json`**, il watermark dell'equity reale del conto.
|
|
Al primo giro successivo del book (09:47 UTC) l'esecutore ha confrontato l'equity vera contro quel
|
|
valore e ha mandato un allarme Telegram:
|
|
|
|
💰 USCITA DI FONDI: $5,000.00 -> $667.68 (-86.6%)
|
|
|
|
**Falso, e generato da un test.** Il colpevole e'
|
|
`test_book_live.py::test_la_formula_del_report_ha_potenza_anche_a_libro_flat`: prende solo
|
|
`monkeypatch` (niente `tmp_path`), sostituisce `shadow_report` con uno che dichiara
|
|
`real_equity=5000.0` e poi chiama `book.book_report()` — che come effetto collaterale scrive il
|
|
watermark. L'helper `_write_cfg` esisteva gia' proprio per questo (difetto gemello del 2026-07-26),
|
|
ma quel test, aggiunto il 21/08, non lo usa.
|
|
|
|
**NON e' un fastidio cosmetico.** Il watermark alimenta il cap di fallback:
|
|
|
|
cap_fallback = min(cap_fisso_di_config, watermark * frac)
|
|
|
|
Con `watermark = $5.000` il cap diventa `min($3.000, $2.500) = $2.500` per asset invece di `$334`:
|
|
su un conto da $668 sono fino a **$5.000 di nozionale lordo, ~7,5x di leva**. Ed e' raggiungibile:
|
|
il ramo `eq_fallback` di `book_execute` **allerta e NON blocca** per scelta dichiarata. Cioe'
|
|
lanciare la suite di test poteva **armare esattamente il pericolo che il watermark esiste per
|
|
impedire** (vedi `src/live/book.py`, nota del 2026-07-26 sul cap fisso e la leva 3,35x).
|
|
|
|
**LA RIPARAZIONE E' STRUTTURALE, NON PER-TEST.** Chiedere a ogni autore di ricordarsi il
|
|
monkeypatch e' la stessa scommessa che ha gia' perso due volte (26/07 e 21/08). Qui il
|
|
reindirizzamento e' **autouse**: vale per ogni test, anche per quelli che verranno scritti domani
|
|
da chi non ha letto questo file. Un test che vuole davvero pilotare il watermark continua a
|
|
funzionare — il suo monkeypatch esplicito vince su questo, che gira prima.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isola_file_operativi_vivi(tmp_path, monkeypatch):
|
|
"""Ogni test scrive il watermark in una cartella temporanea, mai in `data/live/`.
|
|
|
|
Il valore iniziale NON viene precaricato: un test che si aspetta un watermark deve
|
|
dichiararlo, e la prima lettura in assoluto non produce allarmi per costruzione
|
|
(`write_equity_watermark` ritorna None quando non c'e' un "prima").
|
|
"""
|
|
import src.live.book as book
|
|
monkeypatch.setattr(book, "EQUITY_WATERMARK", tmp_path / "equity_seen.json")
|
|
|
|
# --- trades.db (2026-08-26, estensione §5.12) --------------------------------------------
|
|
# ⚠️ `connect(path=DB_PATH)` cattura il default ALLA DEFINIZIONE: patchare la costante
|
|
# `DB_PATH` non cambierebbe niente. Si avvolge la funzione: un test che passa il path
|
|
# esplicito (tutti quelli esistenti usano `T.connect(tmp_path / ...)`) non cambia
|
|
# comportamento; un test che chiamasse `connect()` nudo finisce in tmp, non sul libro
|
|
# di bordo vivo.
|
|
import src.live.tradesdb as tradesdb
|
|
_connect_vero = tradesdb.connect
|
|
|
|
def _connect_isolato(path=None):
|
|
return _connect_vero(tmp_path / "trades.db" if path is None else path)
|
|
|
|
monkeypatch.setattr(tradesdb, "connect", _connect_isolato)
|
|
|
|
# --- docs/journal/ -----------------------------------------------------------------------
|
|
# `journal.scrivi_file` scrive pagine markdown TRACCIATE DA GIT: un test che lo chiama
|
|
# non deve poter sporcare il working tree (P16: il cron gira dal working tree).
|
|
import src.live.journal as journal
|
|
monkeypatch.setattr(journal, "JOURNAL_DIR", tmp_path / "journal")
|
|
yield
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="session")
|
|
def _guardia_log_esecuzioni_vivo():
|
|
"""`book_executions.jsonl` e' scritto da `book_execute.py`, che i test caricano ad-hoc con
|
|
importlib: nessun monkeypatch di conftest puo' raggiungerlo PRIMA. Qui si fa l'altra meta'
|
|
del lavoro: impronta a inizio suite, verifica a fine suite. Se questo assert scatta, un
|
|
test ha scritto nel registro VIVO delle esecuzioni — trovarlo e dargli un path suo.
|
|
|
|
Falso positivo possibile ma raro: un FILL REALE del cron :47 mentre la suite gira
|
|
(30 fill in 43 giorni). In quel caso l'orario dell'ultima riga del file coincide col
|
|
giro del cron, non con la suite: verificarlo prima di dare la colpa a un test."""
|
|
f = PROJECT_ROOT / "data" / "live" / "book_executions.jsonl"
|
|
prima = (f.stat().st_mtime_ns, f.stat().st_size) if f.exists() else None
|
|
yield
|
|
dopo = (f.stat().st_mtime_ns, f.stat().st_size) if f.exists() else None
|
|
assert prima == dopo, (
|
|
"un test ha scritto in data/live/book_executions.jsonl (registro VIVO): "
|
|
f"{prima} -> {dopo}. Se l'ultima riga del file e' delle :47 puo' essere un fill "
|
|
"reale del cron; altrimenti trovare il test e isolarlo.")
|