feat(live): loop di esecuzione GATED di TP01 (execution_enabled + --execute, default OFF)
scripts/live/live_execute.py porta il conto reale al target di TP01 (min(0.5*frazione*equity, cap/asset)): apre/riduce/chiude via DeribitTrader.rebalance_to(). DOPPIO GATE: config/live.json execution_enabled=true (master, default false) E flag --execute; senza entrambi e' dry-run. Reconciliation post-ordine + log in data/live/executions.jsonl. TP01 flat -> 0 azioni. - execution.py: rebalance_to() (open/reduce/close al target); MAX_AMOUNT alzato a tetto hard anti-fat-finger (~$630/$430 su conto ~$600), il sizing operativo lo decide config max_notional. - config/live.json: master switch + cap/asset $300 + min ordine $5 + disaster_sl_pct. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-2
@@ -17,9 +17,11 @@ from dataclasses import dataclass
|
||||
|
||||
from src.live.deribit import DeribitRead, notional_to_amount, quantize_price
|
||||
|
||||
# Conto USDC -> perp LINEARE USDC (amount in base-coin). Cap micro-test: ~$13.
|
||||
# Conto USDC -> perp LINEARE USDC (amount in base-coin). MAX_AMOUNT = TETTO HARD anti-fat-finger
|
||||
# (~$630/$430 su un conto ~$600): backstop sopra il sizing di TP01, non il sizing operativo (quello
|
||||
# lo decide config/live.json max_notional_per_asset_usd). Il micro-test invia comunque size fissa minima.
|
||||
ALLOWED = {"BTC_USDC-PERPETUAL", "ETH_USDC-PERPETUAL"}
|
||||
MAX_AMOUNT = {"BTC_USDC-PERPETUAL": 0.0002, "ETH_USDC-PERPETUAL": 0.005}
|
||||
MAX_AMOUNT = {"BTC_USDC-PERPETUAL": 0.01, "ETH_USDC-PERPETUAL": 0.25}
|
||||
FLAT_USD = 1.0 # |notional| < $1 = posizione considerata flat
|
||||
|
||||
|
||||
@@ -115,6 +117,29 @@ class DeribitTrader(DeribitRead):
|
||||
side = "sell" if pos_usd > 0 else "buy"
|
||||
return self._submit(instrument, side, amount, reduce_only=True, label=label)
|
||||
|
||||
# --- RIBILANCIO al target (long-only TP01): apre / riduce / chiude ---
|
||||
def rebalance_to(self, instrument: str, target_notional_usd: float, mark: float,
|
||||
min_usd: float = 5.0) -> list[Fill]:
|
||||
"""Porta la posizione su `instrument` al target (USD notional). Long-only: target>=0.
|
||||
- delta < min_usd -> niente (gia' a target);
|
||||
- target ~0 & posizione -> close() (uscita piena, reduce_only);
|
||||
- delta > 0 -> open buy (aumenta);
|
||||
- delta < 0 (resta long) -> sell reduce_only del delta (riduce).
|
||||
Ritorna i Fill eseguiti."""
|
||||
cur = self.position_usd(instrument)
|
||||
delta = target_notional_usd - cur
|
||||
if abs(delta) < min_usd:
|
||||
return []
|
||||
if target_notional_usd < FLAT_USD and cur > FLAT_USD:
|
||||
f = self.close(instrument, label="tp01-exit")
|
||||
return [f] if f else []
|
||||
amount = notional_to_amount(instrument, abs(delta), price=mark)
|
||||
if amount <= 0:
|
||||
return []
|
||||
if delta > 0:
|
||||
return [self.open(instrument, "buy", amount, label="tp01-buy")]
|
||||
return [self._submit(instrument, "sell", amount, reduce_only=True, label="tp01-reduce")]
|
||||
|
||||
# --- DISASTER BRACKET (assicurazione on-book per outage; da Old) ---
|
||||
def place_disaster_sl(self, instrument: str, side_held: str, amount: float,
|
||||
stop_price: float, label: str = "disaster-sl") -> Fill:
|
||||
|
||||
Reference in New Issue
Block a user