merge lavoro/goldrake-u11 (secondo giro 27/09)
This commit is contained in:
@@ -79,6 +79,7 @@ SOGLIA_OK = 5.0 # <= -> nessuna azione
|
||||
SOGLIA_AZIONE = 10.0 # > -> rivedere il peso di SKH01
|
||||
D_SHARPE_PER_BPS = -0.017 # sensibilita' marginale del BOOK, misurata
|
||||
D_CAGR_PER_BPS = -0.0023
|
||||
SOGLIA_DIVERGENZA_BPS = 1.0 # #37 B9: |fee realizzata - tier pubblico| oltre questo -> notify
|
||||
|
||||
|
||||
def fetch_fees(instrument: str) -> dict:
|
||||
@@ -209,14 +210,30 @@ def run() -> dict:
|
||||
changes = diff_vs(prev, cur)
|
||||
worst = max(c["taker_bps"] for c in cur.values())
|
||||
lvl, why = verdict(worst)
|
||||
# issue #37 (B9). realized_fee_bps() girava SOLO dentro il blocco di stampa: in cron
|
||||
# (--quiet, il caso normale) non girava mai e una fee realmente pagata diversa dal tier
|
||||
# pubblico non avvisava nessuno. Qui si calcola SEMPRE ed entra nel record del giro
|
||||
# (registra_giro, sotto). {} -> None: "non misurata" (nessun trade recente), mai "zero".
|
||||
real = realized_fee_bps() or None
|
||||
divergenze = []
|
||||
if real:
|
||||
for ins, bps in real.items():
|
||||
tier = cur.get(ins, {}).get("taker_bps")
|
||||
if tier is None:
|
||||
continue
|
||||
d = bps - tier
|
||||
if abs(d) > SOGLIA_DIVERGENZA_BPS:
|
||||
divergenze.append(
|
||||
f"{ins}: realizzata {bps:.2f} bps vs tier pubblico {tier:.2f} ({d:+.2f})")
|
||||
return dict(current=cur, changes=changes, worst_taker_bps=worst, level=lvl, reason=why,
|
||||
first_read=not prev)
|
||||
first_read=not prev, realized=real, divergenze=divergenze,
|
||||
divergenza_gia_detta=bool(prev.get("divergenza_detta")))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
r = run()
|
||||
quiet = "--quiet" in sys.argv
|
||||
interessante = bool(r["changes"]) or r["level"] != "OK"
|
||||
interessante = bool(r["changes"]) or r["level"] != "OK" or bool(r.get("divergenze"))
|
||||
|
||||
if not quiet or interessante:
|
||||
print("=" * 86)
|
||||
@@ -232,7 +249,7 @@ def main() -> int:
|
||||
print(" (prima lettura: stato inizializzato, nessun confronto possibile)")
|
||||
for c in r["changes"]:
|
||||
print(f" CAMBIO: {c}")
|
||||
real = realized_fee_bps()
|
||||
real = r.get("realized") # calcolato una volta in run(), non piu' qui (#37 B9)
|
||||
if real:
|
||||
print("\n cross-check sui trade REALI del conto (fonte autorevole):")
|
||||
for ins, bps in real.items():
|
||||
@@ -240,7 +257,8 @@ def main() -> int:
|
||||
nota = ""
|
||||
if tier is not None:
|
||||
d = bps - tier
|
||||
nota = f" (tier taker {tier:.2f} → {d:+.2f})" + (" ⚠️ DIVERGE" if abs(d) > 1.0 else "")
|
||||
nota = (f" (tier taker {tier:.2f} → {d:+.2f})"
|
||||
+ (" ⚠️ DIVERGE" if abs(d) > SOGLIA_DIVERGENZA_BPS else ""))
|
||||
print(f" {ins:<22}{bps:>9.2f} bps/lato effettivi{nota}")
|
||||
else:
|
||||
print("\n cross-check sui trade reali: NON MISURATO (nessun trade recente leggibile)")
|
||||
@@ -257,9 +275,31 @@ def main() -> int:
|
||||
})
|
||||
if not inviato and (not quiet or interessante):
|
||||
print(" ⚠️ invio FALLITO — stato NON salvato: il prossimo giro ritenta")
|
||||
# issue #37 (B9): fee realmente pagata che diverge dal tier pubblico atteso -> notify UNA
|
||||
# volta per episodio. Marcatore "divergenza_detta" nello stesso state.json (stessa regola di
|
||||
# B8: si scrive solo se l'invio e' riuscito), azzerato quando la divergenza torna sotto
|
||||
# soglia. E' un evento indipendente dal cambio di tier: se qui serve un invio e quello di B8
|
||||
# e' appena fallito, save_state sotto non gira (stessa gate di B8) e il marcatore non si
|
||||
# scrive — al giro dopo si ridice, ridondante ma mai persa, la stessa scelta di B8.
|
||||
divergenze = r.get("divergenze") or []
|
||||
gia_detta = bool(r.get("divergenza_gia_detta"))
|
||||
marcatore = gia_detta
|
||||
if divergenze and not gia_detta:
|
||||
inviata_div = notify("💸 FEE WATCH — fee realizzata diverge dal tier pubblico", {
|
||||
f"divergenza {i+1}": d for i, d in enumerate(divergenze)
|
||||
})
|
||||
if inviata_div:
|
||||
marcatore = True
|
||||
elif not quiet or interessante:
|
||||
print(" ⚠️ invio divergenza FALLITO — marcatore non scritto: il prossimo giro ritenta")
|
||||
elif not divergenze:
|
||||
marcatore = False
|
||||
|
||||
stato_da_salvare = dict(r["current"])
|
||||
stato_da_salvare["divergenza_detta"] = marcatore
|
||||
if not serve_invio or inviato:
|
||||
save_state(r["current"])
|
||||
registra_giro(RUNS, level=r["level"])
|
||||
save_state(stato_da_salvare)
|
||||
registra_giro(RUNS, level=r["level"], realized=r.get("realized"))
|
||||
return 2 if r["level"] == "AZIONE" else 0
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live import tradesdb as T # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
|
||||
def sync(verbose: bool = True) -> dict:
|
||||
@@ -31,10 +32,35 @@ def sync(verbose: bool = True) -> dict:
|
||||
T.set_meta(con, "ultimo_sync", T.ora())
|
||||
tot = con.execute("SELECT COUNT(*) c FROM fills").fetchone()["c"]
|
||||
tot_e = con.execute("SELECT COUNT(*) c FROM equity").fetchone()["c"]
|
||||
|
||||
# issue #37 (C11). Dal 2026-08-24T01:07Z (T.CUTOFF_TS_VERO) book_executions.jsonl porta l'ora
|
||||
# VERA per-fill (docstring di tradesdb.py), ma `sync()` popola il DB SOLO da cron_book.log —
|
||||
# l'arricchimento dal jsonl e' TradingBook#11, non qui. Un conteggio diverso in quella
|
||||
# finestra segnala che una delle due fonti sta perdendo fill. Notify UNA volta per episodio:
|
||||
# marcatore in `meta` (stessa regola di fee_watch #37 B9 — scritto solo se l'invio riesce,
|
||||
# azzerato quando i conteggi tornano uguali).
|
||||
righe_jsonl = T.parse_executions_jsonl(T.EXEC_JSONL.read_text()) if T.EXEC_JSONL.exists() else []
|
||||
n_jsonl_dal = sum(1 for r in righe_jsonl if str(r.get("ts_utc", "")) >= T.CUTOFF_TS_VERO)
|
||||
n_db_dal = con.execute("SELECT COUNT(*) c FROM fills WHERE ts_utc >= ?",
|
||||
(T.CUTOFF_TS_VERO,)).fetchone()["c"]
|
||||
diverge = n_jsonl_dal != n_db_dal
|
||||
gia_detto = T.get_meta(con, "conteggio_diverso_detto") == "1"
|
||||
if diverge:
|
||||
msg = f"⚠️ trades.db: {n_jsonl_dal} fill nel jsonl, {n_db_dal} nel DB dal 24/08"
|
||||
if verbose:
|
||||
print(f" {msg}")
|
||||
if not gia_detto:
|
||||
if notify("⚠️ trades.db — conteggio fill diverso dal jsonl dal 24/08",
|
||||
{"fill nel jsonl": n_jsonl_dal, "fill nel DB": n_db_dal}):
|
||||
T.set_meta(con, "conteggio_diverso_detto", "1")
|
||||
elif gia_detto:
|
||||
T.set_meta(con, "conteggio_diverso_detto", "0")
|
||||
|
||||
if verbose:
|
||||
print(f" fills: +{n_f} (totale {tot}) | equity: +{n_e} (totale {tot_e}) | round-trip {n_r}")
|
||||
con.close()
|
||||
return dict(nuovi_fill=n_f, nuovi_equity=n_e, roundtrip=n_r, totale_fill=tot)
|
||||
return dict(nuovi_fill=n_f, nuovi_equity=n_e, roundtrip=n_r, totale_fill=tot,
|
||||
jsonl_dal_24_08=n_jsonl_dal, db_dal_24_08=n_db_dal)
|
||||
|
||||
|
||||
def reconcile() -> None:
|
||||
@@ -42,8 +68,8 @@ def reconcile() -> None:
|
||||
fills = [f for r in runs for f in r.fills]
|
||||
righe = T.parse_executions_jsonl(T.EXEC_JSONL.read_text()) if T.EXEC_JSONL.exists() else []
|
||||
rec = T.reconcile(fills, righe)
|
||||
print(f"\n cron_book.log : {len(fills)} fill (con ORA VERA)")
|
||||
print(f" book_executions : {len(righe)} righe (senza ora)")
|
||||
print(f"\n cron_book.log : {len(fills)} fill (ora di inizio giro)")
|
||||
print(f" book_executions : {len(righe)} righe (ora VERA per-fill dal 24/08, #37 C11)")
|
||||
print(f" concordano : {len(rec['ok'])}")
|
||||
print(f" solo nel log : {len(rec['solo_log'])}")
|
||||
print(f" solo nel jsonl : {len(rec['solo_jsonl'])}")
|
||||
|
||||
Reference in New Issue
Block a user