feat(V2): migrazione macro completa (read-only, env ignored)

- exchanges/macro: cot.py + cot_contracts.py + fetchers.py copiati 1:1 con
  rewrite import mcp_common -> cerbero_mcp.common, mcp_macro -> cerbero_mcp.exchanges.macro
- nuovo MacroClient stateless wrapper: trasporta solo fred_api_key/finnhub_api_key,
  niente HTTP session (i fetchers usano async_client ad-hoc)
- tools.py: 11 tool (get_treasury_yields, get_yield_curve_slope,
  get_breakeven_inflation, get_economic_indicators, get_macro_calendar,
  get_market_overview, get_equity_futures, get_asset_price, get_cot_tff,
  get_cot_disaggregated, get_cot_extreme_positioning) — niente write,
  niente leverage_cap
- routers/macro.py: prefix /mcp-macro, 11 route POST /tools/*
- builder branch macro: stesse credenziali per testnet/mainnet (env ignorato);
  registry istanzia 2 entry, costo trascurabile (wrapper stateless)
- test migrati: test_cot.py + test_fetchers.py (test_server_acl.py skippato V1-only)
- nuovo test test_build_client_macro_no_env_distinction in test_exchanges_builder.py

Suite: 224 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
AdrianoDev
2026-04-30 18:42:55 +02:00
parent 1b8ba0ef9c
commit 88bd4e7bde
12 changed files with 1735 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
"""Router /mcp-macro/* — read-only data provider.
Macro non distingue testnet/mainnet (FRED/Finnhub sono endpoint pubblici unici),
ma manteniamo la signature `Environment` per uniformità con gli altri router.
Tutti i tool sono READ — niente write, niente leverage_cap.
"""
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.macro import tools as t
from cerbero_mcp.exchanges.macro.client import MacroClient
Environment = Literal["testnet", "mainnet"]
def get_environment(request: Request) -> Environment:
return request.state.environment
async def get_macro_client(
request: Request, env: Environment = Depends(get_environment)
) -> MacroClient:
registry: ClientRegistry = request.app.state.registry
return await registry.get("macro", env)
def make_router() -> APIRouter:
r = APIRouter(prefix="/mcp-macro", tags=["macro"])
@r.post("/tools/get_economic_indicators")
async def _get_economic_indicators(
params: t.GetEconomicIndicatorsReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_economic_indicators(client, params)
@r.post("/tools/get_macro_calendar")
async def _get_macro_calendar(
params: t.GetMacroCalendarReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_macro_calendar(client, params)
@r.post("/tools/get_market_overview")
async def _get_market_overview(
params: t.GetMarketOverviewReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_market_overview(client, params)
@r.post("/tools/get_asset_price")
async def _get_asset_price(
params: t.GetAssetPriceReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_asset_price(client, params)
@r.post("/tools/get_treasury_yields")
async def _get_treasury_yields(
params: t.GetTreasuryYieldsReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_treasury_yields(client, params)
@r.post("/tools/get_equity_futures")
async def _get_equity_futures(
params: t.GetEquityFuturesReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_equity_futures(client, params)
@r.post("/tools/get_yield_curve_slope")
async def _get_yield_curve_slope(
params: t.GetYieldCurveSlopeReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_yield_curve_slope(client, params)
@r.post("/tools/get_breakeven_inflation")
async def _get_breakeven_inflation(
params: t.GetBreakevenInflationReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_breakeven_inflation(client, params)
@r.post("/tools/get_cot_tff")
async def _get_cot_tff(
params: t.GetCotTffReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_cot_tff(client, params)
@r.post("/tools/get_cot_disaggregated")
async def _get_cot_disaggregated(
params: t.GetCotDisaggregatedReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_cot_disaggregated(client, params)
@r.post("/tools/get_cot_extreme_positioning")
async def _get_cot_extreme_positioning(
params: t.GetCotExtremeReq,
client: MacroClient = Depends(get_macro_client),
):
return await t.get_cot_extreme_positioning(client, params)
return r