1b8ba0ef9c
Task 6.7: porting alpaca da services/mcp-alpaca a src/cerbero_mcp. client.py + leverage_cap.py copiati 1:1 (default cap 1 cash). tools.py: 17 tool senza ACL/Principal/audit. Router /mcp-alpaca con 18 route (env_info + 17 tool). Builder branch alpaca: paper=(env=="testnet"), api_key viene da settings.alpaca.api_key_id. Test client + leverage_cap migrati (15 test alpaca pass). Test builder con stub SDK alpaca-py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
177 lines
5.3 KiB
Python
177 lines
5.3 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
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
|
|
from cerbero_mcp.client_registry import ClientRegistry
|
|
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 request.state.environment
|
|
|
|
|
|
async def get_alpaca_client(
|
|
request: Request, env: Environment = Depends(get_environment)
|
|
) -> AlpacaClient:
|
|
registry: ClientRegistry = request.app.state.registry
|
|
return 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 t.place_order(client, params, creds=creds)
|
|
|
|
@r.post("/tools/amend_order")
|
|
async def _amend_order(
|
|
params: t.AmendOrderReq,
|
|
client: AlpacaClient = Depends(get_alpaca_client),
|
|
):
|
|
return await t.amend_order(client, params)
|
|
|
|
@r.post("/tools/cancel_order")
|
|
async def _cancel_order(
|
|
params: t.CancelOrderReq,
|
|
client: AlpacaClient = Depends(get_alpaca_client),
|
|
):
|
|
return await t.cancel_order(client, params)
|
|
|
|
@r.post("/tools/cancel_all_orders")
|
|
async def _cancel_all_orders(
|
|
params: t.CancelAllOrdersReq,
|
|
client: AlpacaClient = Depends(get_alpaca_client),
|
|
):
|
|
return await t.cancel_all_orders(client, params)
|
|
|
|
@r.post("/tools/close_position")
|
|
async def _close_position(
|
|
params: t.ClosePositionReq,
|
|
client: AlpacaClient = Depends(get_alpaca_client),
|
|
):
|
|
return await t.close_position(client, params)
|
|
|
|
@r.post("/tools/close_all_positions")
|
|
async def _close_all_positions(
|
|
params: t.CloseAllPositionsReq,
|
|
client: AlpacaClient = Depends(get_alpaca_client),
|
|
):
|
|
return await t.close_all_positions(client, params)
|
|
|
|
return r
|