"""Integration test: signal -> risk manager -> order executor -> order event.""" import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "services" / "order-executor" / "src")) import pytest from decimal import Decimal from unittest.mock import AsyncMock, MagicMock from shared.models import Signal, OrderSide, OrderStatus from order_executor.executor import OrderExecutor from order_executor.risk_manager import RiskManager @pytest.mark.asyncio 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", ) exchange = AsyncMock() exchange.fetch_balance = AsyncMock(return_value={"free": {"USDT": 10000}}) risk_manager = RiskManager( max_position_size=Decimal("0.5"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), ) broker = AsyncMock() db = AsyncMock() notifier = AsyncMock() executor = OrderExecutor( exchange=exchange, risk_manager=risk_manager, broker=broker, db=db, notifier=notifier, dry_run=True, ) order = await executor.execute(signal) assert order is not None assert order.status == OrderStatus.FILLED assert order.symbol == "BTC/USDT" assert order.side == OrderSide.BUY # Verify order was persisted and published db.insert_order.assert_called_once() broker.publish.assert_called_once() notifier.send_order.assert_called_once() @pytest.mark.asyncio 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 reason="test", ) exchange = AsyncMock() exchange.fetch_balance = AsyncMock(return_value={"free": {"USDT": 1000}}) risk_manager = RiskManager( max_position_size=Decimal("0.1"), stop_loss_pct=Decimal("5"), daily_loss_limit_pct=Decimal("10"), ) executor = OrderExecutor( exchange=exchange, risk_manager=risk_manager, broker=AsyncMock(), db=AsyncMock(), notifier=AsyncMock(), dry_run=True, ) order = await executor.execute(signal) assert order is None # Rejected