fee_watch: save_state solo se l'invio necessario e' riuscito (#29 B8)
save_state girava sempre dopo l'eventuale notify, anche quando l'invio falliva: il giro dopo diff_vs confrontava lo stato nuovo con se stesso e il cambiamento (o il livello AZIONE) spariva senza essere mai stato detto. Ora lo stato si salva solo se non serviva un invio, o se l'invio e' riuscito. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -245,11 +245,19 @@ def main() -> int:
|
|||||||
else:
|
else:
|
||||||
print("\n cross-check sui trade reali: NON MISURATO (nessun trade recente leggibile)")
|
print("\n cross-check sui trade reali: NON MISURATO (nessun trade recente leggibile)")
|
||||||
|
|
||||||
if r["changes"] or r["level"] == "AZIONE":
|
# issue #29 (B8): save_state veniva chiamato SEMPRE, anche se l'invio necessario falliva —
|
||||||
notify("💸 FEE WATCH — schema fee Deribit", {
|
# il giro dopo `diff_vs` non avrebbe piu' visto il cambiamento (lo stato salvato era gia'
|
||||||
|
# quello nuovo) e il cambio andava perso per sempre invece che ritentare al giro dopo.
|
||||||
|
serve_invio = bool(r["changes"] or r["level"] == "AZIONE")
|
||||||
|
inviato = True
|
||||||
|
if serve_invio:
|
||||||
|
inviato = notify("💸 FEE WATCH — schema fee Deribit", {
|
||||||
"verdetto": f"[{r['level']}] {r['reason']}",
|
"verdetto": f"[{r['level']}] {r['reason']}",
|
||||||
**{f"cambio {i+1}": c for i, c in enumerate(r["changes"])},
|
**{f"cambio {i+1}": c for i, c in enumerate(r["changes"])},
|
||||||
})
|
})
|
||||||
|
if not inviato and (not quiet or interessante):
|
||||||
|
print(" ⚠️ invio FALLITO — stato NON salvato: il prossimo giro ritenta")
|
||||||
|
if not serve_invio or inviato:
|
||||||
save_state(r["current"])
|
save_state(r["current"])
|
||||||
registra_giro(RUNS, level=r["level"])
|
registra_giro(RUNS, level=r["level"])
|
||||||
return 2 if r["level"] == "AZIONE" else 0
|
return 2 if r["level"] == "AZIONE" else 0
|
||||||
|
|||||||
@@ -162,3 +162,54 @@ def test_un_fill_non_misurabile_e_None_non_zero():
|
|||||||
assert FW.fee_bps_di_un_fill("BTC_USDC-PERPETUAL", *bad) is None
|
assert FW.fee_bps_di_un_fill("BTC_USDC-PERPETUAL", *bad) is None
|
||||||
# fee nulla e' invece una misura legittima (ordine maker a rebate zero)
|
# fee nulla e' invece una misura legittima (ordine maker a rebate zero)
|
||||||
assert FW.fee_bps_di_un_fill("BTC_USDC-PERPETUAL", 1.0, 100.0, 0.0) == 0.0
|
assert FW.fee_bps_di_un_fill("BTC_USDC-PERPETUAL", 1.0, 100.0, 0.0) == 0.0
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# issue #29 (B8) — save_state SOLO se l'invio (quando serviva) e' riuscito
|
||||||
|
# ===========================================================================
|
||||||
|
# Fino a qui `save_state` girava SEMPRE dopo l'eventuale notify, anche se l'invio necessario
|
||||||
|
# falliva: il giro dopo `diff_vs` avrebbe confrontato lo stato nuovo con se stesso e il cambio
|
||||||
|
# sarebbe sparito senza essere mai stato detto.
|
||||||
|
|
||||||
|
_RIS_AZIONE = dict(
|
||||||
|
current={"BTC_USDC-PERPETUAL": dict(taker_bps=12.0, maker_bps=0.0, liq_bps=100.0)},
|
||||||
|
changes=["BTC_USDC-PERPETUAL taker: 5.00 -> 12.00 bps"], worst_taker_bps=12.0,
|
||||||
|
level="AZIONE", reason="taker 12.0bps > 10: rivedere il peso di SKH01", first_read=False,
|
||||||
|
)
|
||||||
|
_RIS_OK_SENZA_CAMBI = dict(
|
||||||
|
current={"BTC_USDC-PERPETUAL": dict(taker_bps=3.0, maker_bps=0.0, liq_bps=75.0)},
|
||||||
|
changes=[], worst_taker_bps=3.0, level="OK", reason="ok", first_read=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _prepara(monkeypatch, risultato, esito_invio):
|
||||||
|
salvato, inviato = [], []
|
||||||
|
monkeypatch.setattr(FW, "run", lambda: risultato)
|
||||||
|
monkeypatch.setattr(FW, "save_state", lambda cur: salvato.append(cur))
|
||||||
|
monkeypatch.setattr(FW, "notify", lambda *a, **k: inviato.append((a, k)) or esito_invio)
|
||||||
|
monkeypatch.setattr(FW, "registra_giro", lambda *a, **k: None)
|
||||||
|
monkeypatch.setattr(FW, "realized_fee_bps", lambda *a, **k: {})
|
||||||
|
return salvato, inviato
|
||||||
|
|
||||||
|
|
||||||
|
def test_invio_fallito_non_salva_lo_stato(monkeypatch):
|
||||||
|
salvato, inviato = _prepara(monkeypatch, _RIS_AZIONE, esito_invio=False)
|
||||||
|
rc = FW.main()
|
||||||
|
assert len(inviato) == 1, "un cambio con livello AZIONE deve tentare l'invio"
|
||||||
|
assert salvato == [], "invio fallito -> non si salva, il giro dopo ritenta lo stesso cambio"
|
||||||
|
assert rc == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_invio_riuscito_salva_lo_stato(monkeypatch):
|
||||||
|
salvato, inviato = _prepara(monkeypatch, _RIS_AZIONE, esito_invio=True)
|
||||||
|
FW.main()
|
||||||
|
assert len(inviato) == 1
|
||||||
|
assert len(salvato) == 1 and salvato[0] == _RIS_AZIONE["current"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_nessun_invio_dovuto_salva_comunque(monkeypatch):
|
||||||
|
"""Senza cambi e a livello OK non c'e' niente da dire: si salva, e non si chiama notify."""
|
||||||
|
salvato, inviato = _prepara(monkeypatch, _RIS_OK_SENZA_CAMBI, esito_invio=False)
|
||||||
|
FW.main()
|
||||||
|
assert inviato == [], "senza changes e livello OK non deve tentare l'invio"
|
||||||
|
assert len(salvato) == 1
|
||||||
|
|||||||
Reference in New Issue
Block a user