from __future__ import annotations from datetime import UTC, datetime, timedelta import pytest from arbitrade.config.settings import Settings from arbitrade.metrics import MetricsCalculator from arbitrade.storage.db import DuckDBStore def test_metrics_calculator_summarizes_execution_data(tmp_path) -> None: settings = Settings(_env_file=None, DUCKDB_PATH=tmp_path / "metrics.duckdb") store = DuckDBStore(settings) store.migrate() started = datetime.now(UTC) finished = started + timedelta(seconds=30) started_two = started + timedelta(minutes=1) finished_two = started_two + timedelta(seconds=90) with store.connect() as conn: conn.execute( """ INSERT INTO trades ( trade_ref, started_at, finished_at, status, realized_pnl, estimated_pnl, capital_used, cycle, leg_count ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?) """, [ "trade-1", started, finished, "filled", 12.5, 10.0, 100.0, "USD->BTC->ETH->USD", 3, "trade-2", started_two, finished_two, "filled", -4.5, -2.0, 200.0, "USD->ETH->BTC->USD", 3, ], ) conn.execute( """ INSERT INTO opportunities ( detected_at, cycle, gross_pct, net_pct, est_profit, executed ) VALUES (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?) """, [ started, "USD->BTC->ETH->USD", 4.0, 3.0, 0.03, True, started_two, "USD->ETH->BTC->USD", 2.0, 1.0, 0.01, False, started_two + timedelta(seconds=30), "USD->BTC->ETH->USD", 5.0, 4.0, 0.04, True, ], ) conn.execute( """ INSERT INTO orders ( trade_ref, order_ref, leg_index, pair, side, volume, user_ref, status, filled_volume, avg_price, raw_response, recorded_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, [ "trade-1", "order-1", 0, "BTC/USD", "buy", 2.0, 101, "closed", 2.0, 100.0, "{}", started, "trade-2", "order-2", 0, "ETH/USD", "sell", 4.0, 202, "closed", 3.0, 200.0, "{}", started_two, ], ) metrics = MetricsCalculator(store).compute() assert metrics.realized_pnl_usd == 8.0 assert metrics.win_rate == 0.5 assert metrics.avg_trade_duration_seconds == 60.0 assert metrics.opportunities_per_minute == 2.0 assert metrics.fill_rate == 0.875 assert metrics.latency_p50_seconds == 60.0 assert metrics.latency_p95_seconds == 87.0 assert metrics.latency_p99_seconds == pytest.approx(89.4)