Files
Cerbero-mcp/tests/unit/exchanges/ibkr/test_tools.py
T
2026-05-03 21:25:34 +00:00

89 lines
2.8 KiB
Python

from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import pytest
from cerbero_mcp.exchanges.ibkr import tools as t
def test_place_order_req_schema():
req = t.PlaceOrderReq(symbol="AAPL", side="buy", qty=1)
assert req.order_type == "market"
assert req.tif == "day"
assert req.exchange == "SMART"
def test_place_order_req_options_validates_occ():
req = t.PlaceOrderReq(
symbol="AAPL 240119C00190000", side="buy", qty=1, asset_class="options",
)
assert req.asset_class == "options"
@pytest.mark.asyncio
async def test_get_account_tool_calls_client():
client = MagicMock()
client.get_account = AsyncMock(return_value={"netliquidation": {"amount": 10000}})
res = await t.get_account(client, t.GetAccountReq())
assert res["netliquidation"]["amount"] == 10000
@pytest.mark.asyncio
async def test_get_tick_uses_cache_or_subscribes():
client = MagicMock()
client.resolve_conid = AsyncMock(return_value=42)
ws = MagicMock()
ws.get_tick_snapshot = MagicMock(side_effect=[
None,
{"conid": 42, "last_price": 99.5, "bid": 99.4, "ask": 99.6,
"bid_size": 1, "ask_size": 1, "timestamp_ms": 1700000000000},
])
ws.subscribe_tick = AsyncMock()
res = await t.get_tick(
client, t.GetTickReq(symbol="AAPL"), ws=ws, timeout_s=0.05,
)
assert res["last_price"] == 99.5
ws.subscribe_tick.assert_awaited_once_with(42)
@pytest.mark.asyncio
async def test_place_order_enforces_leverage():
client = MagicMock()
client.get_account = AsyncMock(return_value={
"netliquidation": {"amount": 10000},
})
client.place_order = AsyncMock(return_value={"order_id": "O1"})
creds = {"max_leverage": 2}
res = await t.place_order(
client, t.PlaceOrderReq(symbol="AAPL", side="buy", qty=10),
creds=creds, last_price=100.0,
)
assert res["order_id"] == "O1"
@pytest.mark.asyncio
async def test_cancel_order_calls_client():
client = MagicMock()
client.cancel_order = AsyncMock(return_value={"order_id": "O1", "canceled": True})
res = await t.cancel_order(client, t.CancelOrderReq(order_id="O1"))
assert res["canceled"] is True
@pytest.mark.asyncio
async def test_place_order_rejects_excessive_leverage():
from fastapi import HTTPException
client = MagicMock()
client.get_account = AsyncMock(return_value={
"netliquidation": {"amount": 1000},
})
creds = {"max_leverage": 2}
# Order notional = 100*100 = 10000 vs equity 1000 → ratio 10x >> 2x cap → 403
with pytest.raises(HTTPException) as exc_info:
await t.place_order(
client, t.PlaceOrderReq(symbol="AAPL", side="buy", qty=100),
creds=creds, last_price=100.0,
)
assert exc_info.value.status_code == 403
assert exc_info.value.detail["error"] == "LEVERAGE_CAP_EXCEEDED"