"""Tests for extreme value edge cases.""" import sys from datetime import datetime, timezone from decimal import Decimal from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine")) sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "backtester" / "src")) sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "order-executor" / "src")) from shared.models import Candle, Signal, OrderSide from strategies.rsi_strategy import RsiStrategy from strategies.vwap_strategy import VwapStrategy from strategies.bollinger_strategy import BollingerStrategy from backtester.engine import BacktestEngine from backtester.simulator import OrderSimulator from order_executor.risk_manager import RiskManager def _candle(close: str, volume: str = "1000", idx: int = 0) -> Candle: from datetime import timedelta base = datetime(2025, 1, 1, tzinfo=timezone.utc) return Candle( symbol="AAPL", timeframe="1h", open_time=base + timedelta(hours=idx), open=Decimal(close), high=Decimal(close), low=Decimal(close), close=Decimal(close), volume=Decimal(volume), ) class TestZeroPriceCandle: """Strategy should handle Decimal('0') price without crashing.""" def test_rsi_zero_price(self): strategy = RsiStrategy() for i in range(20): strategy.on_candle(_candle("0", idx=i)) # Should not crash; RSI is undefined with constant price result = strategy.on_candle(_candle("0", idx=20)) # With all-zero prices, diffs are zero, RSI is NaN -> returns None assert result is None def test_vwap_zero_price(self): strategy = VwapStrategy() for i in range(40): result = strategy.on_candle(_candle("0", "100", idx=i)) # VWAP of zero-price candles is 0; deviation calc is 0/0 -> should handle gracefully # This tests the division by vwap when vwap == 0 assert result is None class TestVeryLargePrice: """Strategy should handle extremely large prices.""" def test_rsi_large_price(self): strategy = RsiStrategy() for i in range(20): strategy.on_candle(_candle("999999999", idx=i)) result = strategy.on_candle(_candle("999999999", idx=20)) # Constant large price -> no signal assert result is None def test_bollinger_large_price(self): strategy = BollingerStrategy() for i in range(25): strategy.on_candle(_candle("999999999", idx=i)) # Should not overflow; constant price -> std=0, bandwidth=0 -> skip result = strategy.on_candle(_candle("999999999", idx=25)) assert result is None class TestZeroInitialBalance: """Backtester with Decimal('0') initial balance.""" def test_backtest_zero_balance(self): strategy = RsiStrategy() engine = BacktestEngine(strategy, initial_balance=Decimal("0")) candles = [_candle(str(100 - i), idx=i) for i in range(30)] result = engine.run(candles) # Cannot buy with 0 balance, so 0 trades assert result.total_trades == 0 assert result.final_balance == Decimal("0") assert result.profit_pct == Decimal("0") class TestOrderQuantityZero: """Order with quantity=0.""" def test_simulator_zero_quantity_buy(self): sim = OrderSimulator(initial_balance=Decimal("10000")) signal = Signal( strategy="test", symbol="AAPL", side=OrderSide.BUY, price=Decimal("50000"), quantity=Decimal("0"), reason="test zero qty", ) result = sim.execute(signal) # cost = 50000 * 0 = 0; this is technically valid (no cost) assert result is True # Balance unchanged assert sim.balance == Decimal("10000") def test_simulator_zero_quantity_sell(self): sim = OrderSimulator(initial_balance=Decimal("10000")) signal = Signal( strategy="test", symbol="AAPL", side=OrderSide.SELL, price=Decimal("50000"), quantity=Decimal("0"), reason="test zero qty sell", ) result = sim.execute(signal) # No position to sell -> rejected assert result is False class TestRiskManagerZeroDailyLossLimit: """RiskManager with 0% daily loss limit should reject everything on any loss.""" def test_zero_daily_loss_limit_rejects_on_loss(self): rm = RiskManager( max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("0"), ) signal = Signal( strategy="test", symbol="AAPL", side=OrderSide.BUY, price=Decimal("50000"), quantity=Decimal("0.01"), reason="test", ) # Any negative pnl with 0% limit -> should reject result = rm.check( signal=signal, balance=Decimal("10000"), positions={}, daily_pnl=Decimal("-1"), # any loss at all ) assert result.allowed is False def test_zero_daily_loss_limit_allows_no_loss(self): rm = RiskManager( max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("0"), ) signal = Signal( strategy="test", symbol="AAPL", side=OrderSide.BUY, price=Decimal("100"), quantity=Decimal("0.01"), reason="test", ) # No loss -> should allow result = rm.check( signal=signal, balance=Decimal("10000"), positions={}, daily_pnl=Decimal("0"), ) assert result.allowed is True