feat(live): esecuzione REALE su Deribit testnet (shadow) per i 6 fade sui lineari USDC

- ExecutionClient: notional->amount (lineare USDC + inverse), open/close_amount
  reduce-only, verifica sul trade (order_id), fee reali lette dai trades[]
- CerberoClient: place_order market + reduce_only, get_trade_history
- StrategyWorker: shadow (REAL_OPEN/REAL_CLOSE accanto al sim), ledger reale
  parallelo persistito, confronto slippage/fee sim-vs-reale
- runner+portfolios.yml: config execution (6 fade MR01/MR02/MR07 x BTC/ETH su
  BTC_USDC/ETH_USDC-PERPETUAL), capitale 2000
- smoke: live_exec_smoke (layer) + live_shadow_smoke (catena worker), provati su testnet

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-03 10:11:26 +00:00
parent 1f0c1ab02a
commit cb1b6ea46a
7 changed files with 563 additions and 6 deletions
+20 -1
View File
@@ -89,9 +89,17 @@ class CerberoClient:
amount: float,
order_type: str = "market",
price: float | None = None,
leverage: int | None = 3,
leverage: int | None = None,
label: str | None = None,
reduce_only: bool = False,
) -> dict:
"""Piazza un ordine REALE su Deribit. `amount`: per i perp inverse
(BTC/ETH-PERPETUAL) e' in USD notional (step BTC $10, ETH $1); per i lineari
USDC (BTC_USDC/ETH_USDC-PERPETUAL) e' nel base-coin (step 0.0001/0.001).
`reduce_only=True` per chiudere solo la propria quota su uno strumento
condiviso (le posizioni si nettano per conto). Ritorna il `result` grezzo
Deribit: {"order": {...}, "trades": [{price, amount, fee, ...}]} → le fee
REALI sono in trades[]."""
payload: dict[str, Any] = {
"instrument_name": instrument,
"side": side,
@@ -104,11 +112,22 @@ class CerberoClient:
payload["leverage"] = leverage
if label:
payload["label"] = label
if reduce_only:
payload["reduce_only"] = True
return self._post("/mcp-deribit/tools/place_order", payload)
def close_position(self, instrument: str) -> dict:
return self._post("/mcp-deribit/tools/close_position", {"instrument_name": instrument})
def get_trade_history(self, limit: int = 100, instrument_name: str | None = None) -> list[dict]:
"""Trade ESEGUITI sul conto (fonte autorevole delle fee reali). Ogni voce:
{instrument, direction, price, amount, fee, timestamp, order_id}."""
payload: dict[str, Any] = {"limit": limit}
if instrument_name:
payload["instrument_name"] = instrument_name
out = self._post("/mcp-deribit/tools/get_trade_history", payload)
return out if isinstance(out, list) else out.get("trades", [])
def set_stop_loss(self, order_id: str, stop_price: float) -> dict:
return self._post("/mcp-deribit/tools/set_stop_loss", {"order_id": order_id, "stop_price": stop_price})