"""paper_guard: solo barre CHIUSE nei forward-monitor (riparazione del difetto §5.1). Il difetto: `advance()` consumava la barra del giorno in corso e 4 monitor su 6 registravano minuti di mercato spacciati per giornate. La riparazione e' un filtro condiviso; qui si prova il filtro nei due versi, la sua indipendenza dalla risoluzione dei timestamp (D6 — pagata DI NUOVO alla prima stesura di questo stesso modulo), e che ogni monitor lo usi davvero. """ from __future__ import annotations import re import sys from pathlib import Path import numpy as np import pandas as pd ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) from src.live.paper_guard import ( # noqa: E402 MS_1D, MS_1H, chiusa, indice_chiuso, nuove_chiuse) NOW = int(pd.Timestamp("2026-08-26T00:35:00Z").value // 10**6) OGGI = int(pd.Timestamp("2026-08-26T00:00:00Z").value // 10**6) IERI = OGGI - MS_1D def test_la_barra_del_giorno_in_corso_non_e_chiusa(): assert chiusa(IERI, MS_1D, NOW) assert not chiusa(OGGI, MS_1D, NOW) def test_nuove_chiuse_salta_la_parziale_e_prende_la_chiusa(): ts = [IERI - MS_1D, IERI, OGGI] assert nuove_chiuse(ts, IERI - MS_1D, MS_1D, NOW) == [1] # il giro successivo (dopo la mezzanotte seguente) la prende, una volta sola assert nuove_chiuse(ts, IERI, MS_1D, NOW + MS_1D) == [2] def test_nuove_chiuse_su_griglia_oraria(): h0 = int(pd.Timestamp("2026-08-26T00:00:00Z").value // 10**6) ts = [h0 - 2 * MS_1H, h0 - MS_1H, h0] # l'ultima e' l'ora IN CORSO alle 00:35 assert nuove_chiuse(ts, h0 - 3 * MS_1H, MS_1H, NOW) == [0, 1] def test_al_confine_esatto_la_barra_e_chiusa(): # ts + cadenza == adesso: la barra e' finita in questo istante -> chiusa (<=, non <) assert chiusa(IERI, MS_1D, OGGI) def test_indice_chiuso_e_indipendente_dalla_risoluzione(): """D6: `asi8`/`view('int64')` cambiano scala con la risoluzione dell'indice (pandas 3 usa 'us'), e un intero nella scala sbagliata passa senza eccezioni. La prima stesura di `indice_chiuso` e' caduta ESATTAMENTE qui: dava chiusa la barra del giorno in corso.""" for unit in ("ns", "us", "s"): ix = pd.DatetimeIndex( np.array(["2026-08-25T00:00:00", "2026-08-26T00:00:00"], dtype=f"datetime64[{unit}]") ).tz_localize("UTC") m = indice_chiuso(ix, MS_1D, NOW) assert list(m) == [True, False], (unit, list(m)) def test_indice_naive_e_trattato_come_utc(): ix = pd.DatetimeIndex(["2026-08-25", "2026-08-26"]) assert list(indice_chiuso(ix, MS_1D, NOW)) == [True, False] # =========================================================================== # guardia sul SORGENTE: ogni monitor deve usare il filtro, non ridichiararlo # =========================================================================== # Stessa forma della guardia su cron_daily.sh: il difetto e' rientrato nel progetto ogni volta # che una regola viveva solo nella memoria di chi l'aveva scritta (P1). MONITOR_1D = ("paper_xsr", "paper_statarb", "paper_dvolspread") MONITOR_IDX = ("paper_portfolio", "paper_combo") def _src(nome: str) -> str: return (ROOT / "scripts" / "live" / f"{nome}.py").read_text() def test_i_monitor_1d_usano_nuove_chiuse(): for nome in MONITOR_1D: s = _src(nome) assert "PG.nuove_chiuse(" in s, f"{nome}: advance() non filtra le barre chiuse" assert re.search(r"new = \[i for i in range\(len\(ts\)\) if ts\[i\] > st", s) is None, \ f"{nome}: la vecchia riga che consumava la barra parziale e' ancora nel sorgente" def test_prevday_filtra_le_ore_chiuse(): s = _src("paper_prevday") assert "PG.chiusa(" in s and "MS_1H" in s def test_i_monitor_a_indice_usano_indice_chiuso(): for nome in MONITOR_IDX: assert "PG.indice_chiuso(" in _src(nome), f"{nome}: advance() non filtra i giorni chiusi"