diff --git a/scripts/live/book_execute.py b/scripts/live/book_execute.py index 39c1b44..6bfe49f 100644 --- a/scripts/live/book_execute.py +++ b/scripts/live/book_execute.py @@ -314,6 +314,11 @@ def _run(): try: if do_execute and order is not None and not solo_protezione: + # U1/U2 si uniscono dopo questo rilascio: `set_open_cap` e' di U1 (execution.py, + # stesso file, non toccato qui). hasattr perche' oggi DeribitTrader non lo ha + # ancora — l'hasattr si toglie al merge, quando entrambi vivono nello stesso ramo. + if hasattr(trader, "set_open_cap"): + trader.set_open_cap(inst, r["cap_per_asset"] * r.get("scala", 1.0), mark) try: fills = trader.rebalance_signed(inst, net, mark, min_usd=min_order) except RebalanceParziale as rp: diff --git a/tests/test_book_u2.py b/tests/test_book_u2.py index f4f8a28..845406f 100644 --- a/tests/test_book_u2.py +++ b/tests/test_book_u2.py @@ -399,3 +399,61 @@ def test_parse_cron_log_legge_il_fill_di_un_RebalanceParziale(monkeypatch, capsy assert len(runs) == 1 and len(runs[0].fills) == 1 f = runs[0].fills[0] assert f.asset == "BTC" and f.side == "sell" and abs(f.qty - 0.002) < 1e-9 + + +# --------------------------------------------------------------------------- +# set_open_cap (da U1, stesso rilascio): chiamato SOLO se il trader lo espone (hasattr — U1 e +# U2 si uniscono dopo questo rilascio), subito prima di rebalance_signed. +# --------------------------------------------------------------------------- +def test_set_open_cap_chiamato_se_il_trader_lo_espone(monkeypatch): + mod = carica_script("scripts/live/book_execute.py") + + class _TraderConCap: + def __init__(self): + self.cap_visti = [] + def set_open_cap(self, inst, cap_usd, mark): + self.cap_visti.append((inst, cap_usd, mark)) + def rebalance_signed(self, inst, net, mark, min_usd=5.0): + return [] + def position_usd(self, inst): + return 0.0 + def ensure_disaster_sl(self, inst, sl_pct): + return {"state": "ok", "stop": 1.0, "amount": 0.0} + + trader = _TraderConCap() + canned = _canned_report(cap_per_asset=300.0, scala=1.25) + monkeypatch.setattr(mod, "book_report", lambda **k: canned) + monkeypatch.setattr(mod, "notify", lambda t, d=None: None) + monkeypatch.setattr(mod, "DeribitTrader", lambda *a, **k: trader) + monkeypatch.setattr(mod, "load_config", + lambda: dict(execution_enabled=True, min_order_usd=5.0, + disaster_sl_pct=0.30, max_data_age_days=2.0, + skh_feed_max_age_min=30.0)) + monkeypatch.setattr(sys, "argv", ["book_execute.py", "--execute"]) + + mod._run() + assert trader.cap_visti == [("BTC_USDC-PERPETUAL", 300.0 * 1.25, 60000.0)] # solo BTC ha un ordine + + +def test_set_open_cap_non_richiesto_se_il_trader_non_lo_espone(monkeypatch): + """hasattr: un trader senza set_open_cap (oggi, prima del merge con U1) non deve rompersi.""" + mod = carica_script("scripts/live/book_execute.py") + + class _TraderSenzaCap: + def rebalance_signed(self, inst, net, mark, min_usd=5.0): + return [] + def position_usd(self, inst): + return 0.0 + def ensure_disaster_sl(self, inst, sl_pct): + return {"state": "ok", "stop": 1.0, "amount": 0.0} + + canned = _canned_report() + monkeypatch.setattr(mod, "book_report", lambda **k: canned) + monkeypatch.setattr(mod, "notify", lambda t, d=None: None) + monkeypatch.setattr(mod, "DeribitTrader", lambda *a, **k: _TraderSenzaCap()) + monkeypatch.setattr(mod, "load_config", + lambda: dict(execution_enabled=True, min_order_usd=5.0, + disaster_sl_pct=0.30, max_data_age_days=2.0, + skh_feed_max_age_min=30.0)) + monkeypatch.setattr(sys, "argv", ["book_execute.py", "--execute"]) + mod._run() # non deve sollevare AttributeError