un flag sconosciuto non e' l'azione di default: guardia su 18 script, indice USDE orario, pulizia
MISURATO oggi durante la revisione: `trades_db.py --help` non stampava l'uso, cadeva in sync() e riscriveva meta.ultimo_sync. Nessuno dei 18 script di scripts/live/ usava argparse: un flag sbagliato era il ramo else. Su journal.py avrebbe scritto pagina e riga di DB, su analista.py avrebbe speso una chiamata al modello e mandato un Telegram. - src/live/cli.valida: prima istruzione di ogni __main__, prima di connect()/sync/rete. --help -> 0 con l'uso; flag ignoto, valore mancante o posizionale -> 2 con l'elenco dei previsti (P4). NIENTE argparse: cambierebbe messaggi, codici d'uscita e --help di script che il cron gia' chiama. - 18 script cablati (i 3 che scrivono + 14 + cc01), flag invariati. - tests/test_cli_flag.py (30): elenco DERIVATO dalla cartella (P1), valida come prima istruzione, uso che documenta i flag, e i flag che il CRON usa davvero restano accettati (P15/P16); end-to-end su --help e flag ignoto con trades.db non toccato (M15). Verificato a mano: monitor_health --quiet, trades_db --sync --quiet, book_execute dry-run. Debito §5.15, primo passo: balance_watch (orario) registra `usde_usdc` a ogni campione — None con la ragione se illeggibile, mai 1,0. Il cablaggio nel bound quando la serie ha storia. Pulizia dalla revisione: tests/helpers.carica_script al posto della 15a copia del loader importlib (5 file del libro live); il fill di prova via upsert_fills invece di un INSERT che lasciava verified NULL; asserzioni non ancorate al padding; movimenti_capitale accetta le righe gia' lette (una SELECT invece di due ai due lati di una scrittura del cron). Test 910 verdi (+52). Diario 2026-09-02c. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDJsH3iDSaBns3ccpPBwiu
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Argomenti da riga di comando degli script operativi: un flag ignoto NON e' l'azione di default.
|
||||
|
||||
PERCHE' ESISTE (2026-09-02). Nessuno degli script di `scripts/live/` usava argparse: leggevano
|
||||
`sys.argv` con `in` e `index`, quindi un flag sbagliato non era un errore — era il ramo `else`.
|
||||
Costo misurato lo stesso giorno, durante una revisione: `trades_db.py --help` non ha stampato
|
||||
l'uso, e' caduto in `sync()` e ha riscritto `meta.ultimo_sync`. Gli altri due sono peggio:
|
||||
`journal.py --help` avrebbe scritto la pagina di oggi e una riga nel DB, `analista.py --help`
|
||||
avrebbe speso una chiamata al modello e mandato un messaggio Telegram.
|
||||
|
||||
TRE REGOLE, e nient'altro:
|
||||
1. **Nessun argparse.** Gli script hanno flag posizionali-per-nome (`--report`, `--secco`) e
|
||||
flag con valore (`--giorno 2026-08-21`); argparse cambierebbe i messaggi d'errore, il
|
||||
codice d'uscita e il comportamento di `--help` di 18 script che il cron gia' chiama. Qui
|
||||
serve il contrario: **lasciare intatto cio' che gira** e rifiutare cio' che non e' previsto.
|
||||
2. **Il cron non deve cambiare.** I flag dichiarati sono quelli che gli script gia' accettano:
|
||||
`valida()` non ne aggiunge e non ne toglie (P15: una regola si prova contro il codice che
|
||||
dovrebbe eseguirla). L'unica novita' e' `--help`, che prima non esisteva.
|
||||
3. **Si esce PRIMA di qualunque effetto.** `valida()` va chiamata come prima istruzione del
|
||||
blocco `__main__`, prima di `connect()`, prima del sync, prima della rete.
|
||||
|
||||
Codici d'uscita: 0 per `--help` (l'utente ha chiesto l'uso), 2 per un flag ignoto (errore d'uso,
|
||||
distinto dal 1 con cui gli script segnalano un esito negativo del loro lavoro).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
class UsoSbagliato(Exception):
|
||||
"""Flag non previsto da questo script. Portato con l'elenco di cio' che e' previsto (P4)."""
|
||||
|
||||
|
||||
def valida(nome: str, uso: str, *, flag: tuple[str, ...] = (), con_valore: tuple[str, ...] = (),
|
||||
argv: list[str] | None = None, esci: bool = True) -> list[str]:
|
||||
"""Controlla `argv` contro i flag dichiarati. Ritorna gli argomenti; solleva o esce se ignoti.
|
||||
|
||||
`flag` sono i booleani (`--quiet`), `con_valore` quelli che consumano l'argomento seguente
|
||||
(`--giorno 2026-08-21`). `--help`/`-h` stampano `uso` ed escono 0. Con `esci=False` non
|
||||
tocca `sys.exit` e solleva `UsoSbagliato`: e' la forma che i test usano.
|
||||
"""
|
||||
args = list(sys.argv[1:] if argv is None else argv)
|
||||
if "--help" in args or "-h" in args:
|
||||
if not esci:
|
||||
return args
|
||||
print(uso.strip())
|
||||
raise SystemExit(0)
|
||||
noti, ignoti, attesa_valore = set(flag) | set(con_valore), [], False
|
||||
for a in args:
|
||||
if attesa_valore: # e' il valore di un flag noto, non un flag
|
||||
attesa_valore = False
|
||||
continue
|
||||
if not a.startswith("-"):
|
||||
ignoti.append(a); continue # posizionale: nessuno di questi script ne ha
|
||||
if a not in noti:
|
||||
ignoti.append(a); continue
|
||||
attesa_valore = a in con_valore
|
||||
if attesa_valore:
|
||||
ignoti.append(f"{args[-1]} (manca il valore)")
|
||||
if ignoti:
|
||||
msg = (f"{nome}: argomento non previsto: {', '.join(ignoti)}\n"
|
||||
f" previsti: {' '.join(sorted(noti)) or '(nessuno)'}\n\n{uso.strip()}")
|
||||
if not esci:
|
||||
raise UsoSbagliato(msg)
|
||||
print(msg, file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
return args
|
||||
+7
-4
@@ -118,7 +118,7 @@ def _px_1h(asset: str) -> pd.Series | None:
|
||||
return None
|
||||
|
||||
|
||||
def movimenti_capitale(con, fino_ts: str) -> dict:
|
||||
def movimenti_capitale(con, fino_ts: str, rows=None) -> dict:
|
||||
"""Salti di equity fra letture consecutive: versamenti e prelievi, da scorporare dal P&L.
|
||||
|
||||
PERCHE' ESISTE. Il P&L di giornale e' un delta di equity, quindi un versamento ci finisce
|
||||
@@ -139,8 +139,11 @@ def movimenti_capitale(con, fino_ts: str) -> dict:
|
||||
non si distingue dal mercato e RESTA nel P&L. E' lo stesso limite del rilevatore live.
|
||||
"""
|
||||
from src.live import book as _book # lazy: prende la soglia viva, anche monkeypatchata
|
||||
rows = con.execute("SELECT ts_utc, equity FROM equity WHERE ts_utc <= ? ORDER BY ts_utc",
|
||||
(fino_ts,)).fetchall()
|
||||
# `rows` si puo' passare gia' letto: due SELECT sulla stessa tabella a un'ora del cron
|
||||
# possono cadere ai due lati di una scrittura e descrivere due istanti diversi.
|
||||
if rows is None:
|
||||
rows = con.execute("SELECT ts_utc, equity FROM equity WHERE ts_utc <= ? ORDER BY ts_utc",
|
||||
(fino_ts,)).fetchall()
|
||||
soglia = float(_book.EQUITY_JUMP_ALERT)
|
||||
candidati = [(a, b) for a, b in zip(rows, rows[1:])
|
||||
if a["equity"] and abs(b["equity"] - a["equity"]) / a["equity"] >= soglia]
|
||||
@@ -231,7 +234,7 @@ def rendimento_twr(con, fino_ts: str) -> dict:
|
||||
if not rows:
|
||||
return dict(twr=None, motivo="nessuna lettura di equity", segmenti=[], eventi=[],
|
||||
certi=0.0, ambigui=0.0, delta_equity=None, trading=None, letture=0)
|
||||
mv = movimenti_capitale(con, fino_ts)
|
||||
mv = movimenti_capitale(con, fino_ts, rows=rows) # stessa lettura, un solo istante
|
||||
e0, e1 = float(rows[0]["equity"]), float(rows[-1]["equity"])
|
||||
if e0 <= 0:
|
||||
return dict(twr=None, trading=None,
|
||||
|
||||
Reference in New Issue
Block a user