Un'eccezione sulla lettura del conto USDE valeva 0 USDE: se il conto ne aveva davvero, l'equity totale risultava sottostimata senza che nulla lo dicesse. Ora _collaterale_usde ritorna None + la nota dell'eccezione, e _equity propaga l'ignoto (real_eq None -> eq_fallback, non un numero falso). shadow_report.online guardava solo il mark di BTC: con ETH in fallback il giro si considerava comunque online. Ora richiede TUTTI i mark da mainnet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"""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
|
||||
@@ -73,6 +73,12 @@ def test_indice_illeggibile_vale_uno_DICHIARATO_mai_zero():
|
||||
assert "non leggibile" in src
|
||||
|
||||
|
||||
def test_gateway_usde_giu_non_rompe_la_lettura_usdc():
|
||||
def test_gateway_usde_giu_rende_lequity_IGNOTA_non_solo_usdc():
|
||||
"""(#20) Comportamento cambiato: un'eccezione sulla lettura del conto USDE NON vale 0 USDE.
|
||||
Prima di questa riparazione, `eq` restava $2056 (solo USDC) come se l'USDE non ci fosse:
|
||||
se sul conto c'era davvero collaterale USDE, quel $2056 era un equity sottostimato spacciato
|
||||
per certo. Ora l'intera equity e' IGNOTA (None) — coerente col trattamento di ogni altra
|
||||
lettura fallita nel modulo (posizione, mark): non si assume, si segnala."""
|
||||
eq, src = S._equity(_Client(usdc=2056.0, usde=500.0, usde_raises=True), {})
|
||||
assert eq == 2056.0 and src == "mainnet USDC"
|
||||
assert eq is None
|
||||
assert "USDE illeggibile" in src and "RuntimeError" in src
|
||||
|
||||
Reference in New Issue
Block a user