merge lavoro/goldrake-u11 (secondo giro 27/09)
This commit is contained in:
+33
-5
@@ -39,8 +39,16 @@ FINESTRA_GIORNALE_G = 7 # pagine del giornale lette
|
||||
FINESTRA_DIARI_G = 7 # diari letti (la settimana: e' una revisione settimanale)
|
||||
FINESTRA_COMMIT_G = 14 # commit letti
|
||||
CARTELLA = Path("docs") / "revisioni"
|
||||
MAX_CHARS_VOCE = 90_000 # CLAUDE.md sta sotto; il resto e' molto piu' piccolo
|
||||
MAX_CHARS_VOCE = 90_000 # il resto delle voci e' molto piu' piccolo
|
||||
# #37 C12. CLAUDE.md e' arrivato a 89.590 caratteri (99,5% di MAX_CHARS_VOCE, 27/09): al primo
|
||||
# paragrafo aggiunto si sarebbe troncato. Tetto SUO, piu' largo — il vincolo vero resta
|
||||
# MAX_CHARS_TOTALE, applicato A PARTE in `materiale()` a prescindere dal tetto di ogni voce: la
|
||||
# somma dei tetti per-voce (che supera 400k) non e' il limite, lo e' il secondo giro di `_tronca`.
|
||||
MAX_CHARS_CLAUDE_MD = 150_000
|
||||
MAX_CHARS_TOTALE = 400_000
|
||||
SOGLIA_AVVISO_TETTO = 0.95 # avviso quando una voce supera questa frazione del SUO tetto
|
||||
_MARCA_VICINO_TETTO = "[… vicino al tetto:" # marcatore condiviso: materiale() lo scrive, i
|
||||
# consumatori (prompt, rapporto) lo rileggono
|
||||
MAX_NUMERI_LIBERI = 12 # una revisione confronta e sottrae: qualche numero suo e' lecito
|
||||
LIMITE_TELEGRAM = 3_500 # sotto i 4.096 di Telegram, con la testata
|
||||
TIMEOUT_S = 900 # il materiale e' grande: 5 minuti non bastano sempre
|
||||
@@ -89,6 +97,12 @@ def _tronca(testo: str, n: int | None = None) -> tuple[str, bool]:
|
||||
return testo[:n] + f"\n[… troncato a {n:,} caratteri su {len(testo):,}]", True
|
||||
|
||||
|
||||
def _cap_voce(nome: str) -> int:
|
||||
"""Tetto PER-VOCE: CLAUDE.md ne ha uno suo, piu' largo (#37 C12). Il totale
|
||||
(MAX_CHARS_TOTALE) resta un vincolo A PARTE, applicato in `materiale()` a valle di questo."""
|
||||
return MAX_CHARS_CLAUDE_MD if nome.startswith("CLAUDE.md") else MAX_CHARS_VOCE
|
||||
|
||||
|
||||
def _cmd(args: list[str], cwd: Path) -> str:
|
||||
"""Uscita di un comando di SOLA LETTURA, o il motivo per cui non si legge (P5: «non vedo»
|
||||
non e' «va tutto bene»)."""
|
||||
@@ -175,7 +189,18 @@ def materiale(root: Path, adesso: datetime) -> list[tuple[str, str, bool]]:
|
||||
|
||||
out, tot = [], 0
|
||||
for nome, testo in voci:
|
||||
t, tr = _tronca(testo)
|
||||
cap = _cap_voce(nome)
|
||||
# #37 C12: avviso PRIMA di troncare, quando la voce e' vicina al SUO tetto (non ancora al
|
||||
# tetto totale, sotto): la nota entra nel testo, cosi' arriva sia al prompt del revisore
|
||||
# sia — via _MARCA_VICINO_TETTO — al rapporto, come [TRONCATO] oggi. Il margine
|
||||
# (`+ len(nota) <= cap`) e' voluto: la nota stessa non deve MAI far scattare il
|
||||
# troncamento che sta segnalando in anticipo.
|
||||
if len(testo) >= SOGLIA_AVVISO_TETTO * cap:
|
||||
nota = (f"\n{_MARCA_VICINO_TETTO} {len(testo):,} di {cap:,} caratteri "
|
||||
f"({100 * len(testo) / cap:.0f}%)]")
|
||||
if len(testo) + len(nota) <= cap:
|
||||
testo = testo + nota
|
||||
t, tr = _tronca(testo, cap)
|
||||
if tot + len(t) > MAX_CHARS_TOTALE:
|
||||
t, tr = _tronca(t, max(0, MAX_CHARS_TOTALE - tot)), True
|
||||
tot += len(t)
|
||||
@@ -186,7 +211,8 @@ def materiale(root: Path, adesso: datetime) -> list[tuple[str, str, bool]]:
|
||||
def costruisci_prompt(voci: list[tuple[str, str, bool]], adesso: datetime) -> str:
|
||||
parti = [ISTRUZIONI, f"\nDATA DELLA REVISIONE: {adesso:%Y-%m-%d %H:%M}Z (usa questa per «oggi» e per le date dei gate).\n"]
|
||||
for nome, testo, tr in voci:
|
||||
parti.append(f"\n=== {nome}{' [TRONCATO]' if tr else ''} ===\n{testo}\n")
|
||||
tag = " [TRONCATO]" if tr else (" [VICINO AL TETTO]" if _MARCA_VICINO_TETTO in testo else "")
|
||||
parti.append(f"\n=== {nome}{tag} ===\n{testo}\n")
|
||||
parti.append("\nScrivi ora la revisione, coi cinque titoli, e nient'altro.")
|
||||
return "\n".join(parti)
|
||||
|
||||
@@ -236,7 +262,8 @@ def rapporto(testo: str | None, errore: str | None, stato: str, motivi: list[str
|
||||
"- scritto da `scripts/live/revisione.py` (cron `scripts/cron_review.sh`); l'unico file "
|
||||
"scritto e' questo", "",
|
||||
"| voce | caratteri | troncata |", "|---|---|---|"]
|
||||
coda += [f"| {n} | {len(t):,} | {'si' if tr else ''} |" for n, t, tr in voci]
|
||||
coda += [f"| {n} | {len(t):,} | "
|
||||
f"{'si' if tr else ('quasi' if _MARCA_VICINO_TETTO in t else '')} |" for n, t, tr in voci]
|
||||
return "\n".join(testa + corpo + coda) + "\n"
|
||||
|
||||
|
||||
@@ -316,7 +343,8 @@ def scrivi(root: Path, adesso: datetime | None = None, modello: str = MODELLO, *
|
||||
print(f" revisione {esito['data']} modello {modello} materiale {len(voci)} voci, "
|
||||
f"{len(prompt):,} caratteri" + (" [SECCO: nessuna chiamata, nessuna scrittura]" if secco else ""))
|
||||
for n, t, tr in voci:
|
||||
print(f" {len(t):>8,} {n}{' [troncata]' if tr else ''}")
|
||||
tag = " [troncata]" if tr else (" [vicino al tetto]" if _MARCA_VICINO_TETTO in t else "")
|
||||
print(f" {len(t):>8,} {n}{tag}")
|
||||
if secco:
|
||||
return esito
|
||||
|
||||
|
||||
+23
-8
@@ -2,20 +2,31 @@
|
||||
|
||||
PERCHE' ESISTE
|
||||
--------------
|
||||
`data/live/book_executions.jsonl` registra i fill, ma il suo `ts_utc` e' la data della
|
||||
**barra di segnale** (`book_execute.py`: `pd.Timestamp(r['last_data'])`), non l'ora del fill:
|
||||
19 righe su 19 a `00:00:00`. L'ora vera sta solo in `logs/cron_book.log` — che e' **gitignored,
|
||||
non nel backup e ruotabile**. Quindi oggi la cronologia reale del libro live vive in un file
|
||||
che una rotazione cancella, e nessuno se ne accorgerebbe.
|
||||
`logs/cron_book.log` e' **gitignored, non nel backup e ruotabile**, ed e' la fonte che `sync()`
|
||||
usa per popolare il DB: una rotazione lo cancellerebbe senza che nessuno se ne accorga.
|
||||
|
||||
Questo modulo MATERIALIZZA quella cronologia in `data/live/trades.db` (sqlite), che sta dentro
|
||||
il perimetro gia' coperto dal backup rotativo della VPS.
|
||||
|
||||
⚠️ CORREZIONE (#37 C11, #35 A9). Fino al 2026-08-23 `data/live/book_executions.jsonl` scriveva in
|
||||
`ts_utc` la data della **barra di segnale** (`pd.Timestamp(r['last_data'])`), non l'ora del fill:
|
||||
19 righe su 19 a `00:00:00`. Dal 2026-08-24 01:07Z `book_execute.py` (`_registra_fill`) scrive
|
||||
l'ora VERA del fill (`datetime.now(timezone.utc)` al momento dell'ordine, non piu' la barra),
|
||||
UNA per fill — con il prezzo a piena precisione (non arrotondato come nella stampa del log) e il
|
||||
campo `pos_after` (oggi sempre `None` qui: la posizione post-rebalance si legge dopo, best-effort,
|
||||
e non viene riscritta nel jsonl — vedi il commento in `_registra_fill`). E' quindi, dal 24/08 in
|
||||
poi, la fonte con l'ora PIU' precisa per-fill — ma resta usata solo da `reconcile()`: arricchire
|
||||
`fills` con ts/prezzo/pos_after dal jsonl e' uno SVILUPPO non fatto qui (TradingBook#11).
|
||||
|
||||
FONTI, in ordine di autorita' — e nessuna delle tre e' completa da sola
|
||||
----------------------------------------------------------------------
|
||||
1. `logs/cron_book.log` -> ORA VERA del giro + contesto (equity, tp_frac, skh, target, posizione)
|
||||
e la riga di fill `-> BUY 0.0008 @ $78,094.2 fee 0.02187 (OK)`.
|
||||
2. `data/live/book_executions.jsonl` -> gli stessi fill con piu' cifre, ma senza ora.
|
||||
1. `logs/cron_book.log` -> l'ora di INIZIO GIRO (una per run, condivisa da tutti i fill di quel
|
||||
run, non per-fill) + contesto (equity, tp_frac, skh, target,
|
||||
posizione) e la riga di fill
|
||||
`-> BUY 0.0008 @ $78,094.2 fee 0.02187 (OK)`. FONTE usata da `sync()`.
|
||||
2. `data/live/book_executions.jsonl` -> gli stessi fill, con l'ora VERA per-fill dal 2026-08-24
|
||||
01:07Z (prima: la data della barra di segnale, 00:00:00), prezzo a
|
||||
piena precisione e `pos_after`. Entra SOLO in `reconcile()`.
|
||||
3. venue (`DeribitRead.trade_history`) -> autorevole su `order_id` e timestamp in ms,
|
||||
**ma tronca**: al 2026-08-23 ritorna 1 trade su BTC e 0 su ETH.
|
||||
|
||||
@@ -42,6 +53,10 @@ EXEC_JSONL = PROJECT_ROOT / "data" / "live" / "book_executions.jsonl"
|
||||
|
||||
# Data di armamento dell'esecuzione reale (CLAUDE.md, 2026-06-20).
|
||||
ARMING = "2026-06-20"
|
||||
# Da qui in poi book_executions.jsonl porta l'ora VERA del fill in `ts_utc` (fix #35 A9, docstring
|
||||
# sopra): soglia per il controllo di conteggio jsonl-vs-DB in scripts/live/trades_db.py:sync()
|
||||
# (#37 C11). Prima di questo istante il campo e' la barra di segnale, non comparabile.
|
||||
CUTOFF_TS_VERO = "2026-08-24T01:07:00+00:00"
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS fills (
|
||||
|
||||
Reference in New Issue
Block a user