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:
@@ -0,0 +1,67 @@
|
||||
"""Smoke della catena SHADOW dentro lo StrategyWorker (testnet, ordini reali minimi).
|
||||
|
||||
Apre e chiude la quota di UN worker fade come farebbe il runner:
|
||||
_open_position (sim + REAL_OPEN reale su BTC_USDC) → _close_position (sim +
|
||||
REAL_CLOSE reduce-only) → controlla che real_capital sia aggiornato dal fill reale.
|
||||
|
||||
Non tocca lo stato di produzione (data_dir temporanea). Costo testnet = €0.
|
||||
|
||||
uv run python scripts/analysis/live_shadow_smoke.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from src.live.cerbero_client import CerberoClient
|
||||
from src.live.execution import ExecutionClient
|
||||
from src.live.strategy_loader import load_strategy
|
||||
from src.live.strategy_worker import StrategyWorker
|
||||
from src.strategies.base import Signal
|
||||
|
||||
|
||||
def main() -> None:
|
||||
client = CerberoClient()
|
||||
acct = client.get_account_summary(currency="USDC")
|
||||
print(f"Account testnet equity={acct.get('equity')} USDC testnet={acct.get('testnet')}")
|
||||
if not acct.get("testnet"):
|
||||
raise SystemExit("ABORT: non testnet")
|
||||
|
||||
ex = ExecutionClient(client=client)
|
||||
instrument = "BTC_USDC-PERPETUAL"
|
||||
price = ex._mark_price(instrument)
|
||||
print(f"{instrument} mark={price}")
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
w = StrategyWorker(
|
||||
strategy=load_strategy("MR01_bollinger_fade"),
|
||||
asset="BTC", tf="1h", capital=100.0, position_size=0.15, leverage=2.0,
|
||||
data_dir=Path(tmp), executor=ex, exec_instrument=instrument,
|
||||
)
|
||||
print(f"execution_enabled={w.execution_enabled} notional atteso=${100*0.15*2:.0f}")
|
||||
|
||||
# OPEN long (sim + reale)
|
||||
sig = Signal(idx=0, direction=1, entry_price=price, metadata={})
|
||||
w._open_position(sig, price)
|
||||
print(f" real_in_position={w.real_in_position} side={w.real_side} "
|
||||
f"amount={w.real_amount} entry={w.real_entry_price} "
|
||||
f"entry_fee=${w.real_entry_fee_usd:.5f} notional=${w.real_entry_notional:.2f}")
|
||||
assert w.real_in_position, "OPEN reale non verificato"
|
||||
|
||||
# CLOSE (sim + reale reduce-only) a un prezzo leggermente diverso
|
||||
exit_price = (w.entry_price or price) * 1.001
|
||||
cap_before = w.real_capital
|
||||
w._close_position(exit_price, "smoke_close")
|
||||
print(f" real_capital {cap_before:.4f} -> {w.real_capital:.4f} "
|
||||
f"(Δ {w.real_capital - cap_before:+.4f}) real_trades={w.real_trades}")
|
||||
assert not w.real_in_position, "posizione reale non chiusa"
|
||||
|
||||
# verifica finale: il conto e' flat sullo strumento (nessuna quota residua del worker)
|
||||
pos = ex._position_size(instrument)
|
||||
print(f" posizione netta {instrument}: {pos}")
|
||||
print("✓ catena shadow OK — ordine reale aperto, verificato, chiuso reduce-only, "
|
||||
"fee reali nel ledger reale")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user