summaryrefslogtreecommitdiff
path: root/tests/integration/test_order_execution_flow.py
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 17:05:45 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 17:05:45 +0900
commitb8dc7344ff99eb23d5f003795f17cdba3b89c40b (patch)
treeb072312d8c7b3dcd6c0f2e521370deb9a6758630 /tests/integration/test_order_execution_flow.py
parentb4624c77de2ea615a65c04a39d657a38ff2a7c95 (diff)
test: add integration tests for strategy, order, portfolio, and backtest flows
Diffstat (limited to 'tests/integration/test_order_execution_flow.py')
-rw-r--r--tests/integration/test_order_execution_flow.py79
1 files changed, 79 insertions, 0 deletions
diff --git a/tests/integration/test_order_execution_flow.py b/tests/integration/test_order_execution_flow.py
new file mode 100644
index 0000000..1ea0485
--- /dev/null
+++ b/tests/integration/test_order_execution_flow.py
@@ -0,0 +1,79 @@
+"""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