Files
Cerbero-mcp/src/cerbero_mcp/routers/alpaca.py
T
2026-05-01 08:44:28 +02:00

223 lines
6.7 KiB
Python

"""Router /mcp-alpaca/* — DI per env, client e (write) creds.
Mappa 1:1 i tool di `cerbero_mcp.exchanges.alpaca.tools` a endpoint
`POST /mcp-alpaca/tools/{tool_name}`. L'autenticazione bearer è gestita
dal middleware in `cerbero_mcp.auth`; qui leggiamo solo `request.state.environment`.
"""
from __future__ import annotations
from typing import Literal, cast
from fastapi import APIRouter, Depends, Request
from cerbero_mcp.client_registry import ClientRegistry
from cerbero_mcp.common.audit_helpers import audit_call
from cerbero_mcp.exchanges.alpaca import tools as t
from cerbero_mcp.exchanges.alpaca.client import AlpacaClient
Environment = Literal["testnet", "mainnet"]
def get_environment(request: Request) -> Environment:
return cast(Environment, request.state.environment)
async def get_alpaca_client(
request: Request, env: Environment = Depends(get_environment)
) -> AlpacaClient:
registry: ClientRegistry = request.app.state.registry
return cast(AlpacaClient, await registry.get("alpaca", env))
def _build_creds(request: Request) -> dict:
"""Costruisce dict `creds` minimale per leverage cap / metadata."""
settings = request.app.state.settings
return {
"max_leverage": settings.alpaca.max_leverage,
"api_key_id": settings.alpaca.api_key_id,
}
def make_router() -> APIRouter:
r = APIRouter(prefix="/mcp-alpaca", tags=["alpaca"])
# === READ tools ===
@r.post("/tools/environment_info")
async def _environment_info(
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
creds = _build_creds(request)
return await t.environment_info(client, creds=creds)
@r.post("/tools/get_account")
async def _get_account(
params: t.GetAccountReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_account(client, params)
@r.post("/tools/get_positions")
async def _get_positions(
params: t.GetPositionsReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_positions(client, params)
@r.post("/tools/get_activities")
async def _get_activities(
params: t.GetActivitiesReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_activities(client, params)
@r.post("/tools/get_assets")
async def _get_assets(
params: t.GetAssetsReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_assets(client, params)
@r.post("/tools/get_ticker")
async def _get_ticker(
params: t.GetTickerReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_ticker(client, params)
@r.post("/tools/get_bars")
async def _get_bars(
params: t.GetBarsReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_bars(client, params)
@r.post("/tools/get_snapshot")
async def _get_snapshot(
params: t.GetSnapshotReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_snapshot(client, params)
@r.post("/tools/get_option_chain")
async def _get_option_chain(
params: t.GetOptionChainReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_option_chain(client, params)
@r.post("/tools/get_open_orders")
async def _get_open_orders(
params: t.GetOpenOrdersReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_open_orders(client, params)
@r.post("/tools/get_clock")
async def _get_clock(
params: t.GetClockReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_clock(client, params)
@r.post("/tools/get_calendar")
async def _get_calendar(
params: t.GetCalendarReq,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await t.get_calendar(client, params)
# === WRITE tools ===
@r.post("/tools/place_order")
async def _place_order(
params: t.PlaceOrderReq,
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
creds = _build_creds(request)
return await audit_call(
request=request,
exchange="alpaca",
action="place_order",
target_field="symbol",
params=params,
tool_fn=lambda: t.place_order(client, params, creds=creds),
)
@r.post("/tools/amend_order")
async def _amend_order(
params: t.AmendOrderReq,
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await audit_call(
request=request,
exchange="alpaca",
action="amend_order",
target_field="order_id",
params=params,
tool_fn=lambda: t.amend_order(client, params),
)
@r.post("/tools/cancel_order")
async def _cancel_order(
params: t.CancelOrderReq,
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await audit_call(
request=request,
exchange="alpaca",
action="cancel_order",
target_field="order_id",
params=params,
tool_fn=lambda: t.cancel_order(client, params),
)
@r.post("/tools/cancel_all_orders")
async def _cancel_all_orders(
params: t.CancelAllOrdersReq,
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await audit_call(
request=request,
exchange="alpaca",
action="cancel_all_orders",
params=params,
tool_fn=lambda: t.cancel_all_orders(client, params),
)
@r.post("/tools/close_position")
async def _close_position(
params: t.ClosePositionReq,
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await audit_call(
request=request,
exchange="alpaca",
action="close_position",
target_field="symbol",
params=params,
tool_fn=lambda: t.close_position(client, params),
)
@r.post("/tools/close_all_positions")
async def _close_all_positions(
params: t.CloseAllPositionsReq,
request: Request,
client: AlpacaClient = Depends(get_alpaca_client),
):
return await audit_call(
request=request,
exchange="alpaca",
action="close_all_positions",
params=params,
tool_fn=lambda: t.close_all_positions(client, params),
)
return r