refactor: telegram + portfolio in-process (drop shared MCP)
Each bot now manages its own notification + portfolio aggregation: * TelegramClient calls the public Bot API directly via httpx, reading CERBERO_BITE_TELEGRAM_BOT_TOKEN / CERBERO_BITE_TELEGRAM_CHAT_ID from env. No credentials → silent disabled mode. * PortfolioClient composes DeribitClient + HyperliquidClient + the new MacroClient.get_asset_price/eur_usd_rate to expose equity (EUR) and per-asset exposure as the bot's own slice (no cross-bot view). * mcp-telegram and mcp-portfolio removed from MCP_SERVICES / McpEndpoints and the cerbero-bite ping CLI; health_check no longer probes portfolio. Docs (02/04/06/07) and docker-compose updated to reflect the new architecture. 353/353 tests pass; ruff clean; mypy src clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -154,18 +154,39 @@ def _wire_market_snapshot(
|
||||
json={"events": macro_events or []},
|
||||
is_reusable=True,
|
||||
)
|
||||
# In-process portfolio aggregator: wire the underlying exchange and
|
||||
# macro endpoints so total_equity_eur and asset_pct_of_portfolio
|
||||
# produce the requested ``portfolio_eur`` and ``eth_pct``.
|
||||
# FX rate fixed at 1.0 → EUR amount equals USD amount in tests.
|
||||
portfolio_eur_f = float(portfolio_eur)
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-portfolio:9018/tools/get_holdings",
|
||||
url="http://mcp-macro:9013/tools/get_asset_price",
|
||||
json={"ticker": "EURUSD", "price": 1.0},
|
||||
is_reusable=True,
|
||||
)
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-deribit:9011/tools/get_account_summary",
|
||||
json={"equity": portfolio_eur_f, "currency": "USDC"},
|
||||
is_reusable=True,
|
||||
)
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-deribit:9011/tools/get_positions",
|
||||
json=[
|
||||
{"ticker": "AAPL", "current_value_eur": portfolio_eur_f * (1 - eth_pct)},
|
||||
{"ticker": "ETH-USD", "current_value_eur": portfolio_eur_f * eth_pct},
|
||||
{
|
||||
"instrument_name": "ETH-15MAY26-2475-P",
|
||||
"notional_usd": portfolio_eur_f * eth_pct,
|
||||
}
|
||||
],
|
||||
is_reusable=True,
|
||||
)
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-portfolio:9018/tools/get_total_portfolio_value",
|
||||
json={"total_value_eur": portfolio_eur_f},
|
||||
url="http://mcp-hyperliquid:9012/tools/get_account_summary",
|
||||
json={"equity": 0.0},
|
||||
is_reusable=True,
|
||||
)
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-hyperliquid:9012/tools/get_positions",
|
||||
json=[],
|
||||
is_reusable=True,
|
||||
)
|
||||
|
||||
@@ -262,11 +283,12 @@ def _wire_combo_order(
|
||||
|
||||
|
||||
def _wire_telegram_notify_position_opened(httpx_mock: HTTPXMock) -> None:
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-telegram:9017/tools/notify_position_opened",
|
||||
json={"ok": True},
|
||||
is_reusable=True,
|
||||
)
|
||||
"""No-op: Telegram is now an in-process client with disabled mode in tests.
|
||||
|
||||
Kept for call-site compatibility; the function used to register an MCP
|
||||
notify mock but post-refactor there is no HTTP endpoint to mock when
|
||||
the bot has no Telegram credentials configured.
|
||||
"""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -355,11 +377,6 @@ async def test_below_capital_minimum_returns_no_entry(
|
||||
now: datetime,
|
||||
httpx_mock: HTTPXMock,
|
||||
) -> None:
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-telegram:9017/tools/notify",
|
||||
json={"ok": True},
|
||||
is_reusable=True,
|
||||
)
|
||||
# 500 EUR × 1.075 = 537 USD < 720 cfg minimum
|
||||
_wire_market_snapshot(httpx_mock, portfolio_eur=500.0)
|
||||
ctx = _ctx(cfg, runtime_paths, now)
|
||||
@@ -377,11 +394,6 @@ async def test_macro_event_within_dte_blocks_entry(
|
||||
now: datetime,
|
||||
httpx_mock: HTTPXMock,
|
||||
) -> None:
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-telegram:9017/tools/notify",
|
||||
json={"ok": True},
|
||||
is_reusable=True,
|
||||
)
|
||||
macro_events = [
|
||||
{
|
||||
"name": "FOMC",
|
||||
@@ -406,11 +418,6 @@ async def test_no_bias_returns_no_entry(
|
||||
now: datetime,
|
||||
httpx_mock: HTTPXMock,
|
||||
) -> None:
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-telegram:9017/tools/notify",
|
||||
json={"ok": True},
|
||||
is_reusable=True,
|
||||
)
|
||||
# Funding cross neutral (=0) and DVOL 40 → no IC, no directional;
|
||||
# entry validates clean otherwise.
|
||||
_wire_market_snapshot(
|
||||
@@ -507,11 +514,6 @@ async def test_broker_reject_marks_position_cancelled(
|
||||
},
|
||||
is_reusable=True,
|
||||
)
|
||||
httpx_mock.add_response(
|
||||
url="http://mcp-telegram:9017/tools/notify_alert",
|
||||
json={"ok": True},
|
||||
is_reusable=True,
|
||||
)
|
||||
bull_cfg = golden_config(
|
||||
entry=type(cfg.entry)(
|
||||
**{**cfg.entry.model_dump(), "trend_bull_threshold_pct": Decimal("0")}
|
||||
|
||||
Reference in New Issue
Block a user