from __future__ import annotations from dataclasses import dataclass from datetime import UTC, datetime import pytest from arbitrade.config.settings import Settings from arbitrade.detection.engine import OpportunityEvent from arbitrade.execution.sequencer import TriangularExecutionSequencer from arbitrade.storage.db import DuckDBStore from arbitrade.storage.executions import AsyncExecutionWriter from arbitrade.storage.repositories import OrderRepository, PnLRepository, TradeRepository @dataclass(slots=True) class _FakeRestClient: calls: int = 0 async def place_market_order(self, *, pair: str, side: str, volume: float) -> dict[str, object]: self.calls += 1 return {"txid": [f"tx-{self.calls}"], "status": "submitted"} def _sample_event() -> OpportunityEvent: return OpportunityEvent( detected_at=datetime.now(UTC), cycle="USD->BTC->ETH->USD", updated_pair="BTC/USD", gross_rate=1.04, net_rate=1.03, gross_pct=4.0, net_pct=3.0, est_profit=0.03, ) @pytest.mark.asyncio async def test_execution_writer_persists_trade_order_and_pnl(tmp_path) -> None: settings = Settings(_env_file=None, DUCKDB_PATH=tmp_path / "exec.duckdb") store = DuckDBStore(settings) store.migrate() writer = AsyncExecutionWriter( TradeRepository(store), OrderRepository(store), PnLRepository(store), max_queue_size=10, ) await writer.start() client = _FakeRestClient() sequencer = TriangularExecutionSequencer( client, available_pairs=["BTC/USD", "ETH/BTC", "ETH/USD"], execution_writer=writer, ) result = await sequencer.execute(_sample_event()) await writer.stop() assert result.success assert client.calls == 3 with store.connect() as conn: trades = conn.execute( "SELECT trade_ref, status, estimated_pnl, capital_used, cycle, leg_count FROM trades" ).fetchall() orders = conn.execute( "SELECT trade_ref, order_ref, leg_index, pair, side, volume, status " "FROM orders ORDER BY leg_index" ).fetchall() pnls = conn.execute("SELECT trade_ref, kind, pnl_usd, source FROM pnl_events").fetchall() assert len(trades) == 1 assert trades[0][1] == "filled" assert trades[0][2] == 0.03 assert trades[0][3] == 1.0 assert trades[0][4] == "USD->BTC->ETH->USD" assert trades[0][5] == 3 assert len(orders) == 3 assert orders[0][2] == 0 assert orders[1][2] == 1 assert orders[2][2] == 2 assert orders[0][6] == "submitted" assert len(pnls) == 1 assert pnls[0][1] == "estimated" assert pnls[0][2] == 0.03