feat(V2): migrazione bybit completa (client, tools, router, test, builder)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
AdrianoDev
2026-04-30 18:31:51 +02:00
parent a8d970233e
commit 5e42ce9c69
11 changed files with 2126 additions and 0 deletions
+261
View File
@@ -0,0 +1,261 @@
"""Router /mcp-bybit/* — DI per env, client e (write) creds.
Mappa 1:1 i tool di `cerbero_mcp.exchanges.bybit.tools` a endpoint
`POST /mcp-bybit/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.bybit import tools as t
from cerbero_mcp.exchanges.bybit.client import BybitClient
Environment = Literal["testnet", "mainnet"]
def get_environment(request: Request) -> Environment:
return request.state.environment
async def get_bybit_client(
request: Request, env: Environment = Depends(get_environment)
) -> BybitClient:
registry: ClientRegistry = request.app.state.registry
return await registry.get("bybit", env)
def _build_creds(request: Request) -> dict:
"""Costruisce dict `creds` minimale per leverage cap / metadata.
Le credenziali vere sono già iniettate nel client da ClientRegistry;
qui passiamo solo il cap di leverage e l'api_key (metadata audit).
"""
settings = request.app.state.settings
return {
"max_leverage": settings.bybit.max_leverage,
"api_key": settings.bybit.api_key,
}
def make_router() -> APIRouter:
r = APIRouter(prefix="/mcp-bybit", tags=["bybit"])
# === READ tools ===
@r.post("/tools/environment_info")
async def _environment_info(
request: Request,
client: BybitClient = Depends(get_bybit_client),
):
creds = _build_creds(request)
return await t.environment_info(client, creds=creds)
@r.post("/tools/get_ticker")
async def _get_ticker(
params: t.TickerReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_ticker(client, params)
@r.post("/tools/get_ticker_batch")
async def _get_ticker_batch(
params: t.TickerBatchReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_ticker_batch(client, params)
@r.post("/tools/get_orderbook")
async def _get_orderbook(
params: t.OrderbookReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_orderbook(client, params)
@r.post("/tools/get_historical")
async def _get_historical(
params: t.HistoricalReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_historical(client, params)
@r.post("/tools/get_indicators")
async def _get_indicators(
params: t.IndicatorsReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_indicators(client, params)
@r.post("/tools/get_funding_rate")
async def _get_funding_rate(
params: t.FundingRateReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_funding_rate(client, params)
@r.post("/tools/get_funding_history")
async def _get_funding_history(
params: t.FundingHistoryReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_funding_history(client, params)
@r.post("/tools/get_open_interest")
async def _get_open_interest(
params: t.OpenInterestReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_open_interest(client, params)
@r.post("/tools/get_instruments")
async def _get_instruments(
params: t.InstrumentsReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_instruments(client, params)
@r.post("/tools/get_option_chain")
async def _get_option_chain(
params: t.OptionChainReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_option_chain(client, params)
@r.post("/tools/get_positions")
async def _get_positions(
params: t.PositionsReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_positions(client, params)
@r.post("/tools/get_account_summary")
async def _get_account_summary(
params: t.AccountSummaryReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_account_summary(client, params)
@r.post("/tools/get_trade_history")
async def _get_trade_history(
params: t.TradeHistoryReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_trade_history(client, params)
@r.post("/tools/get_open_orders")
async def _get_open_orders(
params: t.OpenOrdersReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_open_orders(client, params)
@r.post("/tools/get_basis_spot_perp")
async def _get_basis_spot_perp(
params: t.BasisSpotPerpReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_basis_spot_perp(client, params)
@r.post("/tools/get_orderbook_imbalance")
async def _get_orderbook_imbalance(
params: t.OrderbookImbalanceReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_orderbook_imbalance(client, params)
@r.post("/tools/get_basis_term_structure")
async def _get_basis_term_structure(
params: t.BasisTermStructureReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.get_basis_term_structure(client, params)
# === WRITE tools (richiedono creds per leverage cap / audit) ===
@r.post("/tools/place_order")
async def _place_order(
params: t.PlaceOrderReq,
request: Request,
client: BybitClient = Depends(get_bybit_client),
):
creds = _build_creds(request)
return await t.place_order(client, params, creds=creds)
@r.post("/tools/place_combo_order")
async def _place_combo_order(
params: t.PlaceComboOrderReq,
request: Request,
client: BybitClient = Depends(get_bybit_client),
):
creds = _build_creds(request)
return await t.place_combo_order(client, params, creds=creds)
@r.post("/tools/amend_order")
async def _amend_order(
params: t.AmendOrderReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.amend_order(client, params)
@r.post("/tools/cancel_order")
async def _cancel_order(
params: t.CancelOrderReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.cancel_order(client, params)
@r.post("/tools/cancel_all_orders")
async def _cancel_all_orders(
params: t.CancelAllReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.cancel_all_orders(client, params)
@r.post("/tools/set_stop_loss")
async def _set_stop_loss(
params: t.SetStopLossReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.set_stop_loss(client, params)
@r.post("/tools/set_take_profit")
async def _set_take_profit(
params: t.SetTakeProfitReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.set_take_profit(client, params)
@r.post("/tools/close_position")
async def _close_position(
params: t.ClosePositionReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.close_position(client, params)
@r.post("/tools/set_leverage")
async def _set_leverage(
params: t.SetLeverageReq,
request: Request,
client: BybitClient = Depends(get_bybit_client),
):
creds = _build_creds(request)
return await t.set_leverage(client, params, creds=creds)
@r.post("/tools/switch_position_mode")
async def _switch_position_mode(
params: t.SwitchModeReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.switch_position_mode(client, params)
@r.post("/tools/transfer_asset")
async def _transfer_asset(
params: t.TransferReq,
client: BybitClient = Depends(get_bybit_client),
):
return await t.transfer_asset(client, params)
return r