diff options
Diffstat (limited to 'tests/integration')
| -rw-r--r-- | tests/integration/test_backtest_end_to_end.py | 15 | ||||
| -rw-r--r-- | tests/integration/test_order_execution_flow.py | 34 | ||||
| -rw-r--r-- | tests/integration/test_portfolio_tracking_flow.py | 59 | ||||
| -rw-r--r-- | tests/integration/test_strategy_signal_flow.py | 29 |
4 files changed, 94 insertions, 43 deletions
diff --git a/tests/integration/test_backtest_end_to_end.py b/tests/integration/test_backtest_end_to_end.py index 4bdb5f3..4a484f5 100644 --- a/tests/integration/test_backtest_end_to_end.py +++ b/tests/integration/test_backtest_end_to_end.py @@ -1,12 +1,14 @@ """Integration test: full backtest with real strategy on generated candles.""" + import sys from pathlib import Path -sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine" / "src")) +sys.path.insert( + 0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine" / "src") +) 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")) -import pytest from decimal import Decimal from datetime import datetime, timedelta, timezone @@ -17,10 +19,13 @@ from backtester.engine import BacktestEngine def _generate_candles(prices: list[float], symbol="BTCUSDT") -> list[Candle]: return [ Candle( - symbol=symbol, timeframe="1h", + symbol=symbol, + timeframe="1h", open_time=datetime(2025, 1, 1, tzinfo=timezone.utc) + timedelta(hours=i), - open=Decimal(str(p)), high=Decimal(str(p + 100)), - low=Decimal(str(p - 100)), close=Decimal(str(p)), + open=Decimal(str(p)), + high=Decimal(str(p + 100)), + low=Decimal(str(p - 100)), + close=Decimal(str(p)), volume=Decimal("100"), ) for i, p in enumerate(prices) diff --git a/tests/integration/test_order_execution_flow.py b/tests/integration/test_order_execution_flow.py index 1ea0485..819b7db 100644 --- a/tests/integration/test_order_execution_flow.py +++ b/tests/integration/test_order_execution_flow.py @@ -1,4 +1,5 @@ """Integration test: signal -> risk manager -> order executor -> order event.""" + import sys from pathlib import Path @@ -6,7 +7,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "order import pytest from decimal import Decimal -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import AsyncMock from shared.models import Signal, OrderSide, OrderStatus from order_executor.executor import OrderExecutor @@ -17,8 +18,12 @@ from order_executor.risk_manager import RiskManager async def test_signal_to_order_flow(): """A valid signal passes risk checks and produces a filled order.""" signal = Signal( - strategy="rsi", symbol="BTC/USDT", side=OrderSide.BUY, - price=Decimal("50000"), quantity=Decimal("0.01"), reason="RSI oversold", + strategy="rsi", + symbol="BTC/USDT", + side=OrderSide.BUY, + price=Decimal("50000"), + quantity=Decimal("0.01"), + reason="RSI oversold", ) exchange = AsyncMock() @@ -35,8 +40,12 @@ async def test_signal_to_order_flow(): notifier = AsyncMock() executor = OrderExecutor( - exchange=exchange, risk_manager=risk_manager, - broker=broker, db=db, notifier=notifier, dry_run=True, + exchange=exchange, + risk_manager=risk_manager, + broker=broker, + db=db, + notifier=notifier, + dry_run=True, ) order = await executor.execute(signal) @@ -56,8 +65,11 @@ async def test_signal_to_order_flow(): async def test_signal_rejected_by_risk_manager(): """A signal that exceeds position size is rejected.""" signal = Signal( - strategy="rsi", symbol="BTC/USDT", side=OrderSide.BUY, - price=Decimal("50000"), quantity=Decimal("100"), # Way too large + strategy="rsi", + symbol="BTC/USDT", + side=OrderSide.BUY, + price=Decimal("50000"), + quantity=Decimal("100"), # Way too large reason="test", ) @@ -71,8 +83,12 @@ async def test_signal_rejected_by_risk_manager(): ) executor = OrderExecutor( - exchange=exchange, risk_manager=risk_manager, - broker=AsyncMock(), db=AsyncMock(), notifier=AsyncMock(), dry_run=True, + exchange=exchange, + risk_manager=risk_manager, + broker=AsyncMock(), + db=AsyncMock(), + notifier=AsyncMock(), + dry_run=True, ) order = await executor.execute(signal) diff --git a/tests/integration/test_portfolio_tracking_flow.py b/tests/integration/test_portfolio_tracking_flow.py index 386e78f..80a781c 100644 --- a/tests/integration/test_portfolio_tracking_flow.py +++ b/tests/integration/test_portfolio_tracking_flow.py @@ -1,12 +1,13 @@ """Integration test: order -> portfolio tracker -> position state.""" + import sys from pathlib import Path -sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "portfolio-manager" / "src")) +sys.path.insert( + 0, str(Path(__file__).resolve().parents[2] / "services" / "portfolio-manager" / "src") +) -import pytest from decimal import Decimal -from datetime import datetime, timezone from shared.models import Order, OrderSide, OrderType, OrderStatus from portfolio_manager.portfolio import PortfolioTracker @@ -17,9 +18,13 @@ def test_portfolio_tracks_buy_sell_cycle(): tracker = PortfolioTracker() buy_order = Order( - signal_id="sig-1", symbol="BTCUSDT", side=OrderSide.BUY, - type=OrderType.MARKET, price=Decimal("50000"), - quantity=Decimal("0.1"), status=OrderStatus.FILLED, + signal_id="sig-1", + symbol="BTCUSDT", + side=OrderSide.BUY, + type=OrderType.MARKET, + price=Decimal("50000"), + quantity=Decimal("0.1"), + status=OrderStatus.FILLED, ) tracker.apply_order(buy_order) @@ -29,9 +34,13 @@ def test_portfolio_tracks_buy_sell_cycle(): assert pos.avg_entry_price == Decimal("50000") sell_order = Order( - signal_id="sig-2", symbol="BTCUSDT", side=OrderSide.SELL, - type=OrderType.MARKET, price=Decimal("55000"), - quantity=Decimal("0.1"), status=OrderStatus.FILLED, + signal_id="sig-2", + symbol="BTCUSDT", + side=OrderSide.SELL, + type=OrderType.MARKET, + price=Decimal("55000"), + quantity=Decimal("0.1"), + status=OrderStatus.FILLED, ) tracker.apply_order(sell_order) @@ -43,16 +52,28 @@ def test_portfolio_weighted_average_on_multiple_buys(): """Multiple buys at different prices should compute weighted average.""" tracker = PortfolioTracker() - tracker.apply_order(Order( - signal_id="s1", symbol="BTCUSDT", side=OrderSide.BUY, - type=OrderType.MARKET, price=Decimal("50000"), - quantity=Decimal("0.1"), status=OrderStatus.FILLED, - )) - tracker.apply_order(Order( - signal_id="s2", symbol="BTCUSDT", side=OrderSide.BUY, - type=OrderType.MARKET, price=Decimal("60000"), - quantity=Decimal("0.1"), status=OrderStatus.FILLED, - )) + tracker.apply_order( + Order( + signal_id="s1", + symbol="BTCUSDT", + side=OrderSide.BUY, + type=OrderType.MARKET, + price=Decimal("50000"), + quantity=Decimal("0.1"), + status=OrderStatus.FILLED, + ) + ) + tracker.apply_order( + Order( + signal_id="s2", + symbol="BTCUSDT", + side=OrderSide.BUY, + type=OrderType.MARKET, + price=Decimal("60000"), + quantity=Decimal("0.1"), + status=OrderStatus.FILLED, + ) + ) pos = tracker.get_position("BTCUSDT") assert pos.quantity == Decimal("0.2") diff --git a/tests/integration/test_strategy_signal_flow.py b/tests/integration/test_strategy_signal_flow.py index ee47f8e..448329f 100644 --- a/tests/integration/test_strategy_signal_flow.py +++ b/tests/integration/test_strategy_signal_flow.py @@ -1,17 +1,20 @@ """Integration test: candle -> strategy engine -> signal.""" + import sys from pathlib import Path -sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine" / "src")) +sys.path.insert( + 0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine" / "src") +) sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "strategy-engine")) import pytest from decimal import Decimal from datetime import datetime, timezone -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import AsyncMock -from shared.models import Candle, OrderSide -from shared.events import CandleEvent, Event +from shared.models import Candle +from shared.events import CandleEvent from strategy_engine.engine import StrategyEngine @@ -21,12 +24,18 @@ def candles(): base = [] for i in range(20): price = Decimal(str(100 - i * 2)) # 100, 98, 96... - base.append(Candle( - symbol="BTCUSDT", timeframe="1m", - open_time=datetime(2025, 1, 1, i, 0, tzinfo=timezone.utc), - open=price, high=price + 1, low=price - 1, - close=price, volume=Decimal("10"), - )) + base.append( + Candle( + symbol="BTCUSDT", + timeframe="1m", + open_time=datetime(2025, 1, 1, i, 0, tzinfo=timezone.utc), + open=price, + high=price + 1, + low=price - 1, + close=price, + volume=Decimal("10"), + ) + ) return base |
