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
+31 -3
View File
@@ -41,8 +41,12 @@ _ML_LOOKBACK_DAYS = 365
def build_worker_for(spec: SleeveSpec, alloc_capital: float, leverage: float,
data_dir: Path = DATA_DIR, position_size: float = 0.15):
"""Costruisce il worker esecutore per uno sleeve con capitale = quota allocata."""
data_dir: Path = DATA_DIR, position_size: float = 0.15,
executor=None, exec_instrument: str | None = None):
"""Costruisce il worker esecutore per uno sleeve con capitale = quota allocata.
executor/exec_instrument: se valorizzati (solo per i fade single-leg abilitati),
lo StrategyWorker affianca al fill simulato un ordine REALE su Deribit (shadow)."""
if spec.kind == "pairs":
return PairsWorker(
asset_a=spec.a, asset_b=spec.b, tf=spec.tf, params=spec.params,
@@ -81,6 +85,7 @@ def build_worker_for(spec: SleeveSpec, alloc_capital: float, leverage: float,
return StrategyWorker(
strategy=strategy, asset=spec.asset, tf=spec.tf, capital=alloc_capital,
position_size=position_size, leverage=leverage, params=spec.params, data_dir=data_dir,
executor=executor, exec_instrument=exec_instrument,
)
@@ -156,11 +161,34 @@ def run(config_path: str = "portfolios.yml"):
ledger = PortfolioLedger(p.code, total_capital=p.total_capital)
client = CerberoClient()
# --- Esecuzione REALE (shadow) su Deribit testnet, solo sui fade abilitati ---
# overrides.execution: {enabled, sleeves:[MR01,...], instruments:{BTC:..,ETH:..}}
_exec_cfg = _ov.get("execution", {}) or {}
exec_enabled = bool(_exec_cfg.get("enabled"))
exec_sleeves = set(_exec_cfg.get("sleeves", []))
exec_instr = _exec_cfg.get("instruments", {}) or {}
executor = None
if exec_enabled:
from src.live.execution import ExecutionClient
executor = ExecutionClient(client=client)
print(f"[runner] ESECUZIONE REALE attiva (shadow) — sleeve={sorted(exec_sleeves)} "
f"strumenti={exec_instr}")
def _exec_for(s):
"""(executor, exec_instrument) per uno sleeve, solo se fade single-leg abilitato."""
if not exec_enabled or s.kind not in ("single",) or s.name not in exec_sleeves:
return None, None
return executor, exec_instr.get(s.asset)
dr = sleeve_returns_df(live_ids)
weights = W.weight_vector(p.weighting, live_ids, dr, weights=p.weights,
caps=p.caps, clusters=clusters, lookback=p.vol_lookback)
alloc = ledger.allocate(weights)
workers = {s.sid: build_worker_for(s, alloc[s.sid], p.leverage) for s in live_specs}
workers = {}
for s in live_specs:
ex, inst = _exec_for(s)
workers[s.sid] = build_worker_for(s, alloc[s.sid], p.leverage,
executor=ex, exec_instrument=inst)
# lookback (giorni) richiesto per ogni asset = max sui worker che lo usano
asset_days: dict[str, int] = {}