feat(V2): IBKR simple write tools (place/amend/cancel/close)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-03 21:25:34 +00:00
parent 8914d613ec
commit 3510605fdd
2 changed files with 124 additions and 1 deletions
+83 -1
View File
@@ -4,10 +4,11 @@ from __future__ import annotations
import asyncio
from typing import Any
from fastapi import HTTPException
from pydantic import BaseModel
from cerbero_mcp.exchanges.ibkr.client import _SEC_TYPE_MAP, IBKRClient, IBKRError
from cerbero_mcp.exchanges.ibkr.leverage_cap import enforce_leverage, get_max_leverage # noqa: F401
from cerbero_mcp.exchanges.ibkr.leverage_cap import get_max_leverage
from cerbero_mcp.exchanges.ibkr.ws import IBKRWebSocket
# === Schemas: reads ===
@@ -247,3 +248,84 @@ async def unsubscribe(
conid = await client.resolve_conid(params.symbol, sec)
await ws.unsubscribe(conid)
return {"symbol": params.symbol, "conid": conid, "unsubscribed": True}
# === Write tools: simple ===
async def place_order(
client: IBKRClient, params: PlaceOrderReq,
*, creds: dict, last_price: float | None = None,
) -> dict:
cap = get_max_leverage(creds)
if last_price is None:
try:
ticker = await client.get_ticker(params.symbol, params.asset_class)
last_price = ticker.get("last_price") or ticker.get("ask")
except Exception:
last_price = None
if last_price:
notional = params.qty * float(last_price)
try:
account = await client.get_account()
equity = float(
(account.get("netliquidation") or {}).get("amount") or 0
)
except Exception:
equity = 0.0
if equity > 0 and notional / equity > cap:
raise HTTPException(
status_code=403,
detail={
"error": "LEVERAGE_CAP_EXCEEDED",
"exchange": "ibkr",
"requested_ratio": notional / equity,
"max": cap,
},
)
return await client.place_order(
symbol=params.symbol,
side=params.side,
qty=params.qty,
order_type=params.order_type,
limit_price=params.limit_price,
stop_price=params.stop_price,
tif=params.tif,
asset_class=params.asset_class,
sec_type=params.sec_type,
exchange=params.exchange,
outside_rth=params.outside_rth,
)
async def amend_order(client: IBKRClient, params: AmendOrderReq) -> dict:
return await client.amend_order(
params.order_id,
qty=params.qty,
limit_price=params.limit_price,
stop_price=params.stop_price,
tif=params.tif,
)
async def cancel_order(client: IBKRClient, params: CancelOrderReq) -> dict:
return await client.cancel_order(params.order_id)
async def cancel_all_orders(
client: IBKRClient, params: CancelAllOrdersReq
) -> dict:
return {"canceled": await client.cancel_all_orders()}
async def close_position(
client: IBKRClient, params: ClosePositionReq
) -> dict:
return await client.close_position(params.symbol, params.qty)
async def close_all_positions(
client: IBKRClient, params: CloseAllPositionsReq
) -> dict:
return {"closed": await client.close_all_positions()}