book_execute: SOLO PROTEZIONE su equity/SKH ignoti; fill mai persi su rebalance parziale (#21, #22, #23)
Prima, con equity reale non leggibile il giro dimensionava comunque un ordine sul paper_cap ($2.000 nominali) e lo mandava; con SKH ignoto il target restava quello (fail-safe solo nel segno). Ora: se non si sa quanto vale il conto o qual e' il segnale, nessun rebalance_signed — ma il disaster-SL si controlla LO STESSO per ogni asset (dipende da posizione/mark, non da equity/segnale). Giro degradato -> exit 2, solo se armato (non in anteprima dry-run). Fill persi: rebalance_signed puo' eseguire un fill (es. il close di un flip) e poi sollevare nel passo successivo (l'open dall'altra parte) — quel fill spariva perche' la lettura di `newpos`, PRIMA del giro che lo registrava, andava in eccezione a sua volta. Estratta la registrazione in _registra_fill(...), chiamata SUBITO dopo ogni fill (anche quelli di RebalanceParziale, nuova eccezione di execution.py con .fills/.causa) e PRIMA di leggere la posizione; la lettura ora e' in un try separato, non fatale (pos_after=None + "reconcile: pos illeggibile" se fallisce). Aggiornati due test che assumevano il comportamento vecchio (eq_fallback "prosegue" sul paper capital) e il guard sulla sorgente di test_tradesdb.py (bar_ts ora e' un parametro di _registra_fill, non piu' `r['last_data']` inline). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+21
-6
@@ -535,8 +535,11 @@ def test_book_report_propagates_eq_fallback(monkeypatch):
|
||||
assert book.book_report(offline=True, equity_override=600.0).get("eq_fallback") == "PAPER_CAP"
|
||||
|
||||
|
||||
def test_book_execute_eq_fallback_warns_but_proceeds(monkeypatch, capsys):
|
||||
"""eq_fallback: avvisa + notify MA PROSEGUE (non e' un halt, a differenza di pos_error)."""
|
||||
def test_book_execute_eq_fallback_e_SOLO_PROTEZIONE(monkeypatch, capsys):
|
||||
"""(#21, U2) Comportamento cambiato: eq_fallback NON fa piu' proseguire un rebalance sul
|
||||
paper_cap — passa a SOLO PROTEZIONE (nessun ordine, disaster-SL comunque controllato, exit 2).
|
||||
Prima di questa riparazione il giro dimensionava un ordine reale su un capitale fittizio da
|
||||
$2.000 quando non si sapeva quanto valesse il conto vero; ora non manda alcun ordine."""
|
||||
from helpers import carica_script
|
||||
mod = carica_script("scripts/live/book_execute.py")
|
||||
|
||||
@@ -551,18 +554,30 @@ def test_book_execute_eq_fallback_warns_but_proceeds(monkeypatch, capsys):
|
||||
alerts = []
|
||||
|
||||
class FakeDT:
|
||||
def __init__(self):
|
||||
self.rebalance_chiamato = False
|
||||
def rebalance_signed(self, *a, **k):
|
||||
self.rebalance_chiamato = True
|
||||
return []
|
||||
def ensure_disaster_sl(self, inst, sl_pct): return {"state": "flat"}
|
||||
def position_usd(self, inst): return 0.0
|
||||
|
||||
trader = FakeDT()
|
||||
monkeypatch.setattr(mod, "book_report", lambda **k: canned)
|
||||
monkeypatch.setattr(mod, "notify", lambda title, det=None: alerts.append((title, det)))
|
||||
monkeypatch.setattr(mod, "DeribitTrader", lambda: FakeDT())
|
||||
monkeypatch.setattr(mod, "DeribitTrader", lambda: trader)
|
||||
monkeypatch.setattr(mod, "load_config",
|
||||
lambda: dict(execution_enabled=True, min_order_usd=5.0, disaster_sl_pct=0.30))
|
||||
monkeypatch.setattr(sys, "argv", ["book_execute.py", "--execute"])
|
||||
|
||||
mod._run()
|
||||
uscita = None
|
||||
try:
|
||||
mod._run()
|
||||
except SystemExit as e:
|
||||
uscita = e.code
|
||||
out = capsys.readouterr().out
|
||||
assert "EQUITY FALLBACK" in out # avvisato
|
||||
assert any("equity fallback" in t for t, _ in alerts) # alert inviato
|
||||
assert "Nessuna azione" in out # HA PROSEGUITO (non halt)
|
||||
assert any("equity fallback" in t for t, _ in alerts) # alert diagnostico esistente
|
||||
assert any("solo protezione" in t.lower() for t, _ in alerts) # NUOVO alert di modo
|
||||
assert trader.rebalance_chiamato is False # NESSUN ordine dimensionato
|
||||
assert uscita == 2 # giro degradato
|
||||
|
||||
Reference in New Issue
Block a user