""" Tests for live/monitors — cross-venue, funding/basis, liquidation risk. """ from live.monitors.cross_venue import CrossVenueMonitor from live.monitors.funding_basis import FundingBasisMonitor from live.monitors.liq_risk import LiquidationRiskOverlay from live.treasury import Treasury class TestCrossVenueMonitor: def test_update_and_lead_lag(self): cm = CrossVenueMonitor(window=50, max_lag=5) for i in range(50): cm.update("hl", "BTC", 50000.0 + i * 10, float(i)) cm.update("binance", "BTC", 50000.0 + i * 10 + 2, float(i)) result = cm.lead_lag("BTC", "hl", "binance") assert result is not None assert "correlation" in result assert "lag" in result def test_spot_premium(self): cm = CrossVenueMonitor() for _ in range(10): cm.update("hl", "BTC", 50005.0, 0) cm.update("binance", "BTC", 50000.0, 0) premium = cm.spot_premium("BTC") assert premium is not None assert premium["basis_bps"] > 0 def test_nonexistent_coin_returns_none(self): cm = CrossVenueMonitor() assert cm.lead_lag("XYZ", "hl", "binance") is None def test_summary(self): cm = CrossVenueMonitor() for i in range(50): cm.update("hl", "BTC", 50000.0 + i * 10, float(i)) cm.update("binance", "BTC", 50000.0 + i * 10, float(i)) s = cm.summary("BTC") assert "hl_binance" in s class TestFundingBasisMonitor: def test_initial_no_signal(self): fm = FundingBasisMonitor() result = fm.signal("BTC") assert result["signal"] == "insufficient_data" def test_signal_with_data(self): fm = FundingBasisMonitor(funding_window=100, samples_per_hour=60) for _ in range(200): fm.update_funding("BTC", 0.00001) result = fm.signal("BTC") assert result["signal"] in ("neutral", "positive", "negative", "high_positive", "high_negative") assert "funding_mean_annual_pct" in result def test_basis_requires_spot_and_perp(self): fm = FundingBasisMonitor() for i in range(50): fm.update_perp("BTC", 50005.0) fm.update_spot("BTC", 50000.0) fm.update_funding("BTC", 0.00001) result = fm.signal("BTC") assert result["basis_current_bps"] > 0 class TestLiquidationRiskOverlay: def test_safe_position(self): t = Treasury(initial_equity=100000.0) t.record_fill("BTC", side="buy", size=0.001, price=50000.0, fee=10.0, pnl=0) overlay = LiquidationRiskOverlay(treasury=t) result = overlay.check("BTC") assert result["level"] == "safe" def test_no_position(self): t = Treasury(initial_equity=10000.0) overlay = LiquidationRiskOverlay(treasury=t) result = overlay.check("BTC") assert result["distance_pct"] > 1e5 # capped at 999999 for display def test_recommended_action(self): t = Treasury(initial_equity=10000.0) t.record_fill("BTC", side="buy", size=0.001, price=50000.0, fee=10.0, pnl=0) overlay = LiquidationRiskOverlay(treasury=t) assert overlay.recommended_action("BTC") == "none" def test_summary(self): t = Treasury(initial_equity=100000.0) t.record_fill("BTC", side="buy", size=0.001, price=50000.0, fee=10.0, pnl=0) overlay = LiquidationRiskOverlay(treasury=t) s = overlay.summary() assert "positions" in s assert "worst_case" in s