"""Tests for the BacktestEngine.""" from datetime import datetime, timezone from decimal import Decimal from unittest.mock import MagicMock from shared.models import Candle, Signal, OrderSide from backtester.engine import BacktestEngine def make_candle(symbol: str, price: float, timeframe: str = "1h") -> Candle: return Candle( symbol=symbol, timeframe=timeframe, open_time=datetime.now(timezone.utc), open=Decimal(str(price)), high=Decimal(str(price * 1.01)), low=Decimal(str(price * 0.99)), close=Decimal(str(price)), volume=Decimal("100"), ) def make_candles(prices: list[float], symbol: str = "AAPL") -> list[Candle]: return [make_candle(symbol, p) for p in prices] def make_signal(side: OrderSide, price: str, quantity: str = "0.1") -> Signal: return Signal( strategy="test", symbol="AAPL", side=side, price=Decimal(price), quantity=Decimal(quantity), reason="test", ) def test_backtest_engine_runs_strategy_over_candles(): strategy = MagicMock() strategy.name = "mock_strategy" strategy.on_candle.return_value = None candles = make_candles([50000.0, 51000.0, 52000.0]) engine = BacktestEngine(strategy, Decimal("10000"), slippage_pct=0.0, taker_fee_pct=0.0) result = engine.run(candles) assert strategy.on_candle.call_count == 3 assert result.total_trades == 0 assert result.final_balance == Decimal("10000") assert result.strategy_name == "mock_strategy" def test_backtest_engine_executes_signals(): buy_signal = make_signal(OrderSide.BUY, "50000", "0.1") sell_signal = make_signal(OrderSide.SELL, "55000", "0.1") strategy = MagicMock() strategy.name = "mock_strategy" strategy.on_candle.side_effect = [buy_signal, None, sell_signal] candles = make_candles([50000.0, 52000.0, 55000.0]) engine = BacktestEngine(strategy, Decimal("10000"), slippage_pct=0.0, taker_fee_pct=0.0) result = engine.run(candles) assert result.total_trades == 2 # Initial: 10000, bought 0.1 BTC @ 50000 (cost 5000) → balance 5000 # Sold 0.1 BTC @ 55000 (proceeds 5500) → balance 10500 expected_final = Decimal("10500") assert result.final_balance == expected_final expected_profit = Decimal("500") assert result.profit == expected_profit