"""Test U2 (gitea:Adriano/PythagorasGoal #20, #21, #22, #23, #25). Principio unico: quando il giro non sa QUANTO vale il conto (#20, #21) o QUALE e' il segnale (#22), NON manda ordini di ribilancio, ma CONTROLLA LO STESSO il disaster-SL di ogni asset — la protezione dipende da posizione e mark, non da equity o segnale. E un fill gia' eseguito non e' un fill perso se un passo successivo del rebalance, o la lettura della posizione dopo, falliscono (#23). `online` richiede TUTTI i mark da mainnet, non solo BTC (#25). Nessun test tocca la rete: trader ed eccezioni sono finti. """ from __future__ import annotations import json import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT)) from helpers import carica_script from src.live import shadow as S from src.live.execution import DeribitTrader, Fill, RebalanceParziale def _fresh_bar() -> str: import pandas as _pd return str(_pd.Timestamp.now(tz="UTC").normalize().date()) # --------------------------------------------------------------------------- # #20 — USDE illeggibile: l'equity diventa IGNOTA (None), non 0. # --------------------------------------------------------------------------- class _ClientUSDEBoom: def __init__(self, usdc=2056.0): self._usdc = usdc def account_summary(self, cur): if cur == "USDE": raise RuntimeError("gateway giu'") return {"equity": self._usdc} def test_usde_illeggibile_rende_equity_ignota_non_zero(): eq, src = S._equity(_ClientUSDEBoom(), {}) assert eq is None assert "USDE illeggibile" in src and "RuntimeError" in src def test_watermark_non_si_scrive_su_equity_ignota(tmp_path, monkeypatch): """`write_equity_watermark` riceve None quando l'equity e' ignota: non deve scrivere il file (gia' cosi' nel codice esistente — qui lo si blinda con un test, come chiesto).""" import src.live.book as book monkeypatch.setattr(book, "EQUITY_WATERMARK", tmp_path / "equity_seen.json") assert book.write_equity_watermark(None) is None assert not (tmp_path / "equity_seen.json").exists() # --------------------------------------------------------------------------- # #25 — online richiede TUTTI i mark mainnet, non solo BTC. # --------------------------------------------------------------------------- def test_online_richiede_tutti_i_mark_mainnet(monkeypatch): monkeypatch.setattr(S, "_safe_client", lambda: object()) monkeypatch.setattr(S, "_marks", lambda c, d: ( {"BTC": 60000.0, "ETH": 3000.0}, {"BTC": "mainnet", "ETH": "fallback close (RuntimeError)"})) monkeypatch.setattr(S, "_positions", lambda c: ({a: 0.0 for a in S.ASSETS}, "mainnet", None)) monkeypatch.setattr(S, "_equity", lambda c, m: (600.0, "mainnet USDC")) r = S.shadow_report() assert r["online"] is False # prima di #25 sarebbe stato True (guardava solo BTC) def test_online_true_quando_tutti_i_mark_sono_mainnet(monkeypatch): monkeypatch.setattr(S, "_safe_client", lambda: object()) monkeypatch.setattr(S, "_marks", lambda c, d: ( {"BTC": 60000.0, "ETH": 3000.0}, {"BTC": "mainnet", "ETH": "mainnet"})) monkeypatch.setattr(S, "_positions", lambda c: ({a: 0.0 for a in S.ASSETS}, "mainnet", None)) monkeypatch.setattr(S, "_equity", lambda c, m: (600.0, "mainnet USDC")) assert S.shadow_report()["online"] is True