Files
PythagorasGoal/tests/test_telegram_daily.py
Adriano Dal Pastro 71b39c2c86 ops(live): staleness-gate bloccante + report Telegram giornaliero
Presa in carico operativa del conto Deribit (delega dell'utente). Nessun cambio a
strategie, pesi o sizing: il libro resta TP01 0.75 + SKH01 0.25.

1) STALENESS-GATE (protezione di capitale, era un follow-up mai cablato)
Il 2026-07-14 alle 14:00 UTC il book ha COMPRATO ETH $75 con l'ultima barra del
feed certificato ferma al 2026-07-08: feed congelato da 6 giorni (diario
2026-07-15-feed-freeze). I due gate esistenti non potevano vederlo — il conto ERA
online e la posizione ERA leggibile: proteggono dai problemi di CONTO, non da un
feed morto. Il diario raccomandava un alert; qui il gate e' BLOCCANTE, coerente
con gli altri due ("non opero a cieco"), col disaster-SL on-book come rete su
eventuali posizioni gia' aperte.
- config/live.json: max_data_age_days = 2;
- book_execute: _data_age_days() + blocco PRIMA di costruire DeribitTrader
  (nessuna sessione autenticata aperta su dati morti) + alert Telegram con il
  comando di sblocco; data illeggibile => trattata come stantia;
- letto con cfg.get(default): una config priva della chiave ricade sulla soglia
  sicura invece di sollevare KeyError dentro il percorso con soldi veri;
- tests/test_book_staleness_gate.py: 8 casi, incluso il funzionale che riproduce
  la situazione del 14/07 (online + posizione leggibile + ordine pronto) e
  verifica che DeribitTrader NON venga costruito.
- tests/test_book_live.py: i 3 report finti avevano last_data="2026-07-01"
  hardcoded -> ora data fresca calcolata. Quei test riguardano skh_error /
  pos_error / eq_fallback, non la staleness: con la data fissa sarebbero marciti
  al superamento della soglia.

2) REPORT TELEGRAM GIORNALIERO (scripts/live/telegram_daily.py, in cron_daily.sh)
Gli alert esistenti scattano solo su ordine o errore: con il libro flat — stato
normale e corretto col trend giu' — significava silenzio per settimane,
indistinguibile da un sistema morto. Il report dice ogni giorno dove sta il conto,
perche' non opera e quanto manca perche' operi (con la convenzione TP01 corretta:
media dei SEGNI, quindi servono 2 orizzonti su 3, non basta il piu' breve).
Sola lettura: un test verifica che il modulo non possa inviare ordini.

Stato al commit: equity $596.92, flat e a target, disaster-SL -30% verificato
(placed @ $1,308.7 sull'ultima posizione). TP01 0.00 su entrambi: accensione a
BTC $78.680 (+22,7%) / ETH $2.370 (+27,2%).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 10:05:47 +00:00

55 lines
2.2 KiB
Python

"""Lock del report giornaliero Telegram (in cron dal 2026-07-25).
Gira in produzione dentro cron_daily.sh: se rompe, non deve rompere nulla d'altro e non deve
MAI inviare ordini. I test bloccano proprio questo: e' sola lettura, tollera i pezzi mancanti,
e non invia niente in dry-run.
"""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "scripts" / "live"))
telegram_daily = pytest.importorskip("telegram_daily")
def test_build_non_solleva_e_ha_le_sezioni():
txt = telegram_daily.build()
assert isinstance(txt, str) and len(txt) > 50
for sezione in ("Conto", "Perche' non opera", "Ordini", "Monitor in osservazione"):
assert sezione in txt, f"sezione mancante: {sezione}"
def test_nessuna_scrittura_ne_ordine():
"""Il modulo non deve importare l'esecutore ne' avere funzioni che inviano ordini."""
src = (ROOT / "scripts" / "live" / "telegram_daily.py").read_text()
for vietato in ("DeribitTrader", "book_execute", "--execute", "place_order", "buy(", "sell("):
assert vietato not in src, f"il report non deve poter operare: trovato {vietato!r}"
def test_tollera_i_log_mancanti(monkeypatch, tmp_path):
"""Se i log dei monitor non esistono, il report deve degradare, non esplodere."""
monkeypatch.setattr(telegram_daily, "ROOT", tmp_path)
righe = telegram_daily.monitors()
assert len(righe) == len(telegram_daily.GATES)
assert all(isinstance(r, str) and r.strip() for r in righe)
assert telegram_daily.last_trade().strip()
def test_gate_coerenti_con_i_pre_registrati():
"""Le date qui devono coincidere con i gate pre-registrati negli script di decisione."""
d = dict((n, x) for n, x, _, _ in telegram_daily.GATES)
assert str(d["STATARB-RESID"]) == "2026-09-27"
assert str(d["XSR01"]) == "2026-10-23"
def test_convenzione_trend_e_documentata():
"""La convenzione media-dei-segni (non frazione) e' la ragione per cui TP01 e' 0 con 1/3 su:
se sparisce dal codice, il report mentirebbe sul 'quanto manca'."""
assert "media dei SEGNI" in telegram_daily.trend_state.__doc__