monitor: advance() consuma solo barre CHIUSE — serie rigenerate, guardia PREMATURO, 4 debiti chiusi
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
This commit is contained in:
@@ -41,13 +41,14 @@ from __future__ import annotations
|
||||
import csv
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
MIN_COVERAGE = 0.80 # sotto: la serie ha buchi -> non ci si decide sopra un gate
|
||||
MIN_BARS_PER_JUDGE = 2 # con una barra sola non esiste ne' coda ne' copertura
|
||||
GRAZIA_PREMATURO_MIN = 5.0 # tolleranza fra chiusura della barra e mtime del file (clock/IO)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -60,6 +61,11 @@ class MonitorSpec:
|
||||
calendar: str = "crypto" # crypto (365g) | equity (giorni di borsa)
|
||||
max_age_h: float = 48.0 # oltre: FERMO
|
||||
gate: str | None = None
|
||||
# True = i timestamp sono APERTURE di barra (convenzione resample_tf/ccxt/HL) -> vale il
|
||||
# controllo PREMATURO. False = timbri di esecuzione (es. collect_chain scrive l'ora del
|
||||
# giro): il timbro coincide con la scrittura per costruzione e il controllo mentirebbe
|
||||
# SEMPRE (P14: una guardia piu' stretta del contratto insegna a ignorare la sezione).
|
||||
open_labeled: bool = True
|
||||
|
||||
|
||||
MONITORS: tuple[MonitorSpec, ...] = (
|
||||
@@ -78,7 +84,8 @@ MONITORS: tuple[MonitorSpec, ...] = (
|
||||
# fissa ma una serie IRRECUPERABILE — un'ora non raccolta non si recupera da nessuna fonte,
|
||||
# quindi la soglia d'eta' e' stretta (3h = due giri persi).
|
||||
MonitorSpec("collect_chain", "chain_collect", "runs.jsonl", 1.0, max_age_h=3.0,
|
||||
gate="serie irrecuperabile — la catena opzioni non si ricostruisce a posteriori"),
|
||||
gate="serie irrecuperabile — la catena opzioni non si ricostruisce a posteriori",
|
||||
open_labeled=False),
|
||||
)
|
||||
|
||||
|
||||
@@ -130,14 +137,30 @@ def age_hours(last: datetime, now: datetime, calendar: str) -> float:
|
||||
return raw
|
||||
|
||||
|
||||
def assess(spec: MonitorSpec, stamps: list[datetime], now: datetime) -> dict:
|
||||
def assess(spec: MonitorSpec, stamps: list[datetime], now: datetime,
|
||||
mtime: datetime | None = None) -> dict:
|
||||
"""Verdetto su un monitor. Gli stati NON sono due: 'assente' e 'troppo giovane per un
|
||||
giudizio' non sono 'OK', e non devono poter passare per tali."""
|
||||
giudizio' non sono 'OK', e non devono poter passare per tali.
|
||||
|
||||
`mtime` = quando il file di serie e' stato scritto l'ultima volta. Serve al controllo
|
||||
PREMATURO (r0822_monitor_audit): se l'ultima barra registrata CHIUDE dopo che il file e'
|
||||
stato scritto, il monitor ha consumato una barra parziale — e' il difetto che ha fatto
|
||||
registrare 4/41/2 minuti di mercato al giorno a 3 monitor con gate. Freschezza e copertura
|
||||
NON possono vederlo: una serie fresca, completa e SBAGLIATA le passa entrambe."""
|
||||
base = dict(name=spec.name, gate=spec.gate, n_bars=len(stamps),
|
||||
age_h=None, coverage=None, expected=None)
|
||||
if not stamps:
|
||||
return {**base, "status": "ASSENTE",
|
||||
"why": "nessuna serie leggibile: il monitor non ha mai scritto, o lo stato e' perso"}
|
||||
if mtime is not None and spec.open_labeled:
|
||||
chiude = stamps[-1] + timedelta(hours=spec.cadence_h)
|
||||
ritardo_min = (chiude - mtime).total_seconds() / 60.0
|
||||
if ritardo_min > GRAZIA_PREMATURO_MIN:
|
||||
return {**base, "age_h": age_hours(stamps[-1], now, spec.calendar),
|
||||
"status": "PREMATURO",
|
||||
"why": (f"l'ultima barra chiude alle {chiude:%Y-%m-%d %H:%M}Z ma il file e' "
|
||||
f"stato scritto {ritardo_min/60:.1f}h PRIMA: registrata una barra "
|
||||
"parziale — la serie va rigenerata, non solo riparata")}
|
||||
age = age_hours(stamps[-1], now, spec.calendar)
|
||||
if len(stamps) < MIN_BARS_PER_JUDGE:
|
||||
return {**base, "age_h": age, "status": "NUOVO",
|
||||
@@ -159,8 +182,11 @@ def check_all(root: Path, now: datetime | None = None,
|
||||
now = now or datetime.now(timezone.utc)
|
||||
out = []
|
||||
for spec in monitors:
|
||||
stamps = read_stamps(root / "data" / spec.directory / spec.series)
|
||||
out.append(assess(spec, stamps, now))
|
||||
f = root / "data" / spec.directory / spec.series
|
||||
stamps = read_stamps(f)
|
||||
mtime = (datetime.fromtimestamp(f.stat().st_mtime, tz=timezone.utc)
|
||||
if f.exists() else None)
|
||||
out.append(assess(spec, stamps, now, mtime=mtime))
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user