""" Tests for sim/constraints.py — inventory, funding, fees, liquidation, circuit breakers. """ from sim.constraints import ( InventoryConstraint, FundingConstraint, FeeSchedule, LiquidationRisk, CircuitBreaker, ConstraintManager, ) class TestInventoryConstraint: def test_within_limits(self): ic = InventoryConstraint(max_long=0.01, max_short=0.01) result = ic.check(0.005, 0.003) assert result["long_ok"] assert result["short_ok"] assert result["net_ok"] def test_long_exceeded(self): ic = InventoryConstraint(max_long=0.005) result = ic.check(0.006, 0.001) assert not result["long_ok"] class TestFundingConstraint: def test_cost_positive_funding(self): fc = FundingConstraint(funding_rate_hourly=0.0001) cost = fc.cost_per_hour(position=1.0, mark_price=100000.0) assert cost == 10.0 # 1 * 100000 * 0.0001 = 10 def test_annualized(self): fc = FundingConstraint(funding_rate_hourly=0.0001) ann = fc.annualized_cost_pct(0.0001) assert ann == 0.0001 * 365 * 24 * 100 class TestFeeSchedule: def test_maker_fee(self): fs = FeeSchedule(maker_fee_pct=0.0002) assert fs.maker_fee(100000.0) == 20.0 def test_taker_fee(self): fs = FeeSchedule(taker_fee_pct=0.0005) assert fs.taker_fee(100000.0) == 50.0 class TestLiquidationRisk: def test_liquidation_price_long(self): lr = LiquidationRisk(maintenance_margin_pct=0.03, initial_margin_pct=0.05) liq = lr.liquidation_price(entry_price=100000.0, size=1.0, position_side="long", wallet_balance=10000.0) assert 0 < liq < 100000.0 # below entry def test_liquidation_price_short(self): lr = LiquidationRisk(maintenance_margin_pct=0.03, initial_margin_pct=0.05) liq = lr.liquidation_price(entry_price=100000.0, size=1.0, position_side="short", wallet_balance=10000.0) assert liq > 100000.0 # above entry def test_safe_distance(self): lr = LiquidationRisk() assert lr.is_safe(5.0, threshold_pct=2.0) assert not lr.is_safe(1.0, threshold_pct=2.0) class TestCircuitBreaker: def test_no_trip(self): cb = CircuitBreaker() result = cb.evaluate({"pnl_pct": -1.0, "daily_trades": 10, "toxic_rate": 0.1, "api_errors": 0}) assert not result["tripped"] def test_drawdown_trip(self): cb = CircuitBreaker(max_drawdown_pct=-5.0) result = cb.evaluate({"pnl_pct": -6.0, "daily_trades": 10, "toxic_rate": 0.1, "api_errors": 0}) assert result["tripped"] def test_trade_count_trip(self): cb = CircuitBreaker(max_daily_trades=100) result = cb.evaluate({"pnl_pct": -1.0, "daily_trades": 150, "toxic_rate": 0.1, "api_errors": 0}) assert result["tripped"] class TestConstraintManager: def test_can_place_bid(self): cm = ConstraintManager() result = cm.can_place(side="bid", size=0.001, current_position=0.0, mark_price=100000.0) assert result["allowed"] assert round(result["fee_estimate"], 4) == 0.02 # 0.001 * 100000 * 0.0002 = 0.02 def test_cannot_exceed_inventory(self): cm = ConstraintManager( inventory=InventoryConstraint(max_long=0.002, max_short=0.002) ) result = cm.can_place(side="bid", size=0.001, current_position=0.0015, mark_price=100000.0) assert not result["allowed"]