from __future__ import annotations from dataclasses import dataclass from datetime import UTC, datetime from typing import Any import pytest from arbitrade.detection.engine import OpportunityEvent from arbitrade.execution.idempotency import IdempotencyKeyFactory, OrderReconciler from arbitrade.execution.sequencer import ExecutionLeg @dataclass(slots=True) class _FakeHistoryClient: response: dict[str, Any] async def query_order(self, *, order_id: str, include_trades: bool = True) -> dict[str, Any]: return self.response def _sample_event() -> OpportunityEvent: return OpportunityEvent( detected_at=datetime.now(UTC), cycle="USD->BTC->ETH->USD", updated_pair="BTC/USD", gross_rate=1.02, net_rate=1.01, gross_pct=2.0, net_pct=1.0, est_profit=1.0, allocated_capital=10.0, ) def test_idempotency_key_factory_is_deterministic() -> None: factory = IdempotencyKeyFactory() event = _sample_event() leg = ExecutionLeg( from_currency="USD", to_currency="BTC", pair="BTC/USD", side="buy", volume=10.0, ) first = factory.user_ref_for_leg(event, leg, 0) second = factory.user_ref_for_leg(event, leg, 0) assert first == second assert first > 0 @pytest.mark.asyncio async def test_order_reconciler_maps_query_response_to_report() -> None: client = _FakeHistoryClient( response={ "order-1": { "status": "closed", "vol_exec": "5.0", "price": "100.0", "pair": "BTC/USD", "type": "buy", "userref": 12345, } } ) reconciler = OrderReconciler(client) report = await reconciler.reconcile_order( order_id="order-1", user_ref=12345, expected_pair="BTC/USD", expected_side="buy", expected_volume=10.0, ) assert report.is_terminal assert report.matches_request assert report.status == "closed" assert report.filled_volume == 5.0 assert report.avg_price == 100.0 @pytest.mark.asyncio async def test_order_reconciler_marks_mismatch() -> None: client = _FakeHistoryClient( response={ "order-1": { "status": "closed", "vol_exec": "5.0", "price": "100.0", "pair": "ETH/USD", "type": "sell", "userref": 999, } } ) reconciler = OrderReconciler(client) report = await reconciler.reconcile_order( order_id="order-1", user_ref=12345, expected_pair="BTC/USD", expected_side="buy", expected_volume=10.0, ) assert not report.matches_request