|
|
@@ -5,6 +5,7 @@ import json
|
|
|
import pytest
|
|
|
|
|
|
from exec_mcp import repo, storage
|
|
|
+from exec_mcp import services_orders
|
|
|
from exec_mcp.server import report_balance_snapshots, report_recent_trades
|
|
|
|
|
|
|
|
|
@@ -21,15 +22,16 @@ def _create_account() -> str:
|
|
|
return repo.list_accounts(enabled_only=False)[0]["id"]
|
|
|
|
|
|
|
|
|
-def test_report_recent_trades_defaults_to_filled_and_sorts_newest_first(temp_db):
|
|
|
+def test_report_recent_trades_defaults_to_finished_and_sorts_newest_first(temp_db):
|
|
|
account_id = _create_account()
|
|
|
|
|
|
with storage.get_connection() as conn:
|
|
|
rows = [
|
|
|
- ("rec-1", "filled", "client-a", "1994292738961401", "2026-04-09T08:20:50+00:00"),
|
|
|
+ ("rec-1", "finished", "client-a", "1994292738961401", "2026-04-09T08:20:50+00:00"),
|
|
|
("rec-2", "cancelled", "client-a", "1994292738961402", "2026-04-09T09:20:50+00:00"),
|
|
|
- ("rec-3", "filled", "client-b", "1994292738961403", "2026-04-09T10:20:50+00:00"),
|
|
|
+ ("rec-3", "finished", "client-b", "1994292738961403", "2026-04-09T10:20:50+00:00"),
|
|
|
("rec-4", "open", "client-a", "1994292738961404", "2026-04-09T11:20:50+00:00"),
|
|
|
+ ("rec-5", "new", "client-a", "1994292738961405", "2026-04-09T12:20:50+00:00"),
|
|
|
]
|
|
|
for record_id, status, client_id, order_id, stamp in rows:
|
|
|
conn.execute(
|
|
|
@@ -60,20 +62,67 @@ def test_report_recent_trades_defaults_to_filled_and_sorts_newest_first(temp_db)
|
|
|
|
|
|
result = report_recent_trades(account_id=account_id)
|
|
|
assert result["ok"] is True
|
|
|
- assert result["status"] == "filled"
|
|
|
+ assert result["status"] == "finished"
|
|
|
assert result["count"] == 2
|
|
|
assert [row["bitstamp_order_id"] for row in result["orders"]] == ["1994292738961403", "1994292738961401"]
|
|
|
|
|
|
|
|
|
+def test_report_recent_trades_supports_open_status_and_client_filter(temp_db):
|
|
|
+ account_id = _create_account()
|
|
|
+
|
|
|
+ with storage.get_connection() as conn:
|
|
|
+ for idx, (status, client_id, stamp) in enumerate(
|
|
|
+ [
|
|
|
+ ("open", "client-a", "2026-04-09T08:20:50+00:00"),
|
|
|
+ ("new", "client-a", "2026-04-09T09:20:50+00:00"),
|
|
|
+ ("partially_filled", "client-b", "2026-04-09T10:20:50+00:00"),
|
|
|
+ ("cancelled", "client-a", "2026-04-09T11:20:50+00:00"),
|
|
|
+ ],
|
|
|
+ start=1,
|
|
|
+ ):
|
|
|
+ order_id = f"19942927389614{idx}"
|
|
|
+ conn.execute(
|
|
|
+ """
|
|
|
+ INSERT INTO order_records
|
|
|
+ (id, account_id, market, side, order_type, amount, price, expire_time, status, bitstamp_order_id, client_id, client_order_id, raw_json, created_at, updated_at)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
+ """,
|
|
|
+ (
|
|
|
+ f"rec-{idx}",
|
|
|
+ account_id,
|
|
|
+ "xrpusd",
|
|
|
+ "sell",
|
|
|
+ "limit",
|
|
|
+ "10",
|
|
|
+ "2.00",
|
|
|
+ None,
|
|
|
+ status,
|
|
|
+ order_id,
|
|
|
+ client_id,
|
|
|
+ None,
|
|
|
+ json.dumps({"id": order_id, "status": status}),
|
|
|
+ stamp,
|
|
|
+ stamp,
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ conn.commit()
|
|
|
+
|
|
|
+ result = report_recent_trades(account_id=account_id, status="open", client_id="client-a", limit=10)
|
|
|
+ assert result["ok"] is True
|
|
|
+ assert result["status"] == "open"
|
|
|
+ assert result["client_id"] == "client-a"
|
|
|
+ assert [row["status"] for row in result["orders"]] == ["new", "open"]
|
|
|
+
|
|
|
+
|
|
|
def test_report_recent_trades_supports_all_statuses_and_client_filter(temp_db):
|
|
|
account_id = _create_account()
|
|
|
|
|
|
with storage.get_connection() as conn:
|
|
|
for idx, (status, client_id, stamp) in enumerate(
|
|
|
[
|
|
|
- ("filled", "client-a", "2026-04-09T08:20:50+00:00"),
|
|
|
+ ("finished", "client-a", "2026-04-09T08:20:50+00:00"),
|
|
|
("cancelled", "client-a", "2026-04-09T09:20:50+00:00"),
|
|
|
- ("filled", "client-b", "2026-04-09T10:20:50+00:00"),
|
|
|
+ ("finished", "client-b", "2026-04-09T10:20:50+00:00"),
|
|
|
],
|
|
|
start=1,
|
|
|
):
|
|
|
@@ -85,7 +134,7 @@ def test_report_recent_trades_supports_all_statuses_and_client_filter(temp_db):
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
""",
|
|
|
(
|
|
|
- f"rec-{idx}",
|
|
|
+ f"rec-all-{idx}",
|
|
|
account_id,
|
|
|
"xrpusd",
|
|
|
"sell",
|
|
|
@@ -108,16 +157,93 @@ def test_report_recent_trades_supports_all_statuses_and_client_filter(temp_db):
|
|
|
assert result["ok"] is True
|
|
|
assert result["status"] == "all"
|
|
|
assert result["client_id"] == "client-a"
|
|
|
- assert [row["status"] for row in result["orders"]] == ["cancelled", "filled"]
|
|
|
+ assert [row["status"] for row in result["orders"]] == ["cancelled", "finished"]
|
|
|
|
|
|
|
|
|
def test_report_balance_snapshots_returns_history_newest_first(temp_db):
|
|
|
account_id = _create_account()
|
|
|
|
|
|
- repo.save_balance_snapshot(account_id=account_id, asset_code="BTC", balance=1.25, balance_value=11250.0, value_currency="USD")
|
|
|
- repo.save_balance_snapshot(account_id=account_id, asset_code="USD", balance=500.0, balance_value=500.0, value_currency="USD")
|
|
|
+ repo.save_account_balance_snapshot(
|
|
|
+ account_id=account_id,
|
|
|
+ source_kind="order_finished",
|
|
|
+ source_ref="order-1",
|
|
|
+ snapshot={"balances": [{"asset_code": "BTC"}], "total_value_usd": 11250.0},
|
|
|
+ )
|
|
|
+ repo.save_account_balance_snapshot(
|
|
|
+ account_id=account_id,
|
|
|
+ source_kind="order_finished",
|
|
|
+ source_ref="order-2",
|
|
|
+ snapshot={"balances": [{"asset_code": "USD"}], "total_value_usd": 500.0},
|
|
|
+ )
|
|
|
|
|
|
result = report_balance_snapshots(account_id=account_id)
|
|
|
- assert result["ok"] is True
|
|
|
- assert result["count"] == 2
|
|
|
- assert [row["asset_code"] for row in result["snapshots"]] == ["USD", "BTC"]
|
|
|
+ assert len(result) == 2
|
|
|
+ assert [row["source_ref"] for row in result] == ["order-2", "order-1"]
|
|
|
+ assert [row["snapshot"]["balances"][0]["asset_code"] for row in result] == ["USD", "BTC"]
|
|
|
+
|
|
|
+
|
|
|
+def test_record_order_status_update_persists_raw_json_and_triggers_snapshot_once(temp_db, monkeypatch):
|
|
|
+ account_id = _create_account()
|
|
|
+
|
|
|
+ with storage.get_connection() as conn:
|
|
|
+ conn.execute(
|
|
|
+ """
|
|
|
+ INSERT INTO order_records
|
|
|
+ (id, account_id, market, side, order_type, amount, price, expire_time, status, bitstamp_order_id, client_id, client_order_id, raw_json, created_at, updated_at)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
+ """,
|
|
|
+ (
|
|
|
+ "rec-1",
|
|
|
+ account_id,
|
|
|
+ "xrpusd",
|
|
|
+ "buy",
|
|
|
+ "limit",
|
|
|
+ "10",
|
|
|
+ "2.00",
|
|
|
+ None,
|
|
|
+ "open",
|
|
|
+ "1994292738961401",
|
|
|
+ "client-a",
|
|
|
+ None,
|
|
|
+ json.dumps({"status": "open"}),
|
|
|
+ "2026-04-09T08:20:50+00:00",
|
|
|
+ "2026-04-09T08:20:50+00:00",
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ conn.commit()
|
|
|
+
|
|
|
+ snapshot_calls: list[dict] = []
|
|
|
+ monkeypatch.setattr(
|
|
|
+ services_orders,
|
|
|
+ "capture_account_balance_snapshot",
|
|
|
+ lambda account_id, *, source_kind, source_ref="": snapshot_calls.append(
|
|
|
+ {"account_id": account_id, "source_kind": source_kind, "source_ref": source_ref}
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ services_orders._record_order_status_update(
|
|
|
+ bitstamp_order_id="1994292738961401",
|
|
|
+ status="finished",
|
|
|
+ raw_json={"id": "1994292738961401", "status": "finished"},
|
|
|
+ account_id=account_id,
|
|
|
+ )
|
|
|
+
|
|
|
+ with storage.get_connection() as conn:
|
|
|
+ row = conn.execute(
|
|
|
+ "SELECT status, raw_json FROM order_records WHERE bitstamp_order_id = ?",
|
|
|
+ ("1994292738961401",),
|
|
|
+ ).fetchone()
|
|
|
+
|
|
|
+ assert row["status"] == "finished"
|
|
|
+ assert json.loads(row["raw_json"]) == {"id": "1994292738961401", "status": "finished"}
|
|
|
+ assert snapshot_calls == [
|
|
|
+ {"account_id": account_id, "source_kind": "order_finished", "source_ref": "1994292738961401"}
|
|
|
+ ]
|
|
|
+
|
|
|
+ services_orders._record_order_status_update(
|
|
|
+ bitstamp_order_id="1994292738961401",
|
|
|
+ status="finished",
|
|
|
+ raw_json={"id": "1994292738961401", "status": "finished"},
|
|
|
+ account_id=account_id,
|
|
|
+ )
|
|
|
+ assert len(snapshot_calls) == 1
|