feat(live): arma il BOOK DERIBIT (TP01+SKH01 nettati in software)
Estende l'esecuzione live da TP01-only al book Deribit-only completo. TP01 e SKH01 tradano lo STESSO strumento (una sola posizione netta per conto su Deribit) -> netting in software: un solo ordine/asset verso il target netto. - src/live/book.py: target NETTO per asset = clamp(0.5*E*(0.75*tp_frac + 0.25*skh_sign), ±cap). Riusa current_target (TP01, causale) + _skyhook_positions (segno L/S, book 230m) + conto reale. - src/live/execution.py: rebalance_signed() — reconcile CON SEGNO (long/short, flip via close+open, reduce reduce_only). La close resta sempre permessa (si esce da qualunque posizione). - src/live/livefeed.py: fresh_5m() — feed 5m certificato + coda recente EFFIMERA da Deribit pubblico (stesso simbolo inverse, in-memory, NON scrive su disco -> dati certificati intatti; fallback al certificato su errore). Solo SKH01 ne ha bisogno (e' a 230m); TP01 e' giornaliero. - scripts/live/book_execute.py: executor doppio-gate (config + --execute), disaster-SL on-book sulla posizione netta, log in data/live/book_executions.jsonl. Feed SKH fresco (live_feed=True). - scripts/cron_book.sh + crontab ORARIO: book idempotente ogni ora (riconcilia al netto corrente); rimossa la riga live_execute.py (TP01-only) dal cron daily per non far collidere i due. - config/live.json: ARMATO (execution_enabled=true). cap/asset $300 split 75/25, disaster-SL -30%. Tutto flat all'arming -> nessun ordine finche' un segnale non arma. - tests/test_book_live.py: 20 test (sizing 75/25, cap, flip/close/reduce, parita' pesi backtest, gate-safety, reconcile con trader fittizio, merge/dedup feed + fallback, loader onorato). 76/76 pass. CAVEAT: exit SKH SOFTWARE (latenza fino a fine barra 230m; solo disaster-SL on-book); TP01 scende a peso 0.75 (max $225/asset); SKH01 resta research-grade (equity daily-step, margine DD ETH sottile). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -140,6 +140,47 @@ class DeribitTrader(DeribitRead):
|
||||
return [self.open(instrument, "buy", amount, label="tp01-buy")]
|
||||
return [self._submit(instrument, "sell", amount, reduce_only=True, label="tp01-reduce")]
|
||||
|
||||
# --- RIBILANCIO al target CON SEGNO (book TP01+SKH01: long / short / flip) ---
|
||||
def rebalance_signed(self, instrument: str, target_notional_usd: float, mark: float,
|
||||
min_usd: float = 5.0) -> list[Fill]:
|
||||
"""Porta la posizione su `instrument` al target NETTO con SEGNO (long-short, a differenza di
|
||||
rebalance_to che e' long-only). Gestisce i flip chiudendo prima e riaprendo dall'altro lato.
|
||||
- |delta| < min_usd -> niente;
|
||||
- flip di segno -> close() (sempre permessa) poi apre dall'altra parte;
|
||||
- target ~0 -> close();
|
||||
- stesso segno, |target|<|cur| -> REDUCE reduce_only;
|
||||
- apertura/aumento -> open buy/sell (capped dal guardrail apertura).
|
||||
Ritorna i Fill eseguiti."""
|
||||
cur = self.position_usd(instrument)
|
||||
if abs(target_notional_usd - cur) < min_usd:
|
||||
return []
|
||||
fills: list[Fill] = []
|
||||
crossing = (cur > FLAT_USD and target_notional_usd < -FLAT_USD) or \
|
||||
(cur < -FLAT_USD and target_notional_usd > FLAT_USD)
|
||||
if crossing: # flip: flatta, poi riparti da zero
|
||||
f = self.close(instrument, label="book-flip-close")
|
||||
if f:
|
||||
fills.append(f)
|
||||
cur = 0.0
|
||||
if abs(target_notional_usd) < FLAT_USD: # target flat -> esci (sempre permessa)
|
||||
if abs(cur) > FLAT_USD:
|
||||
f = self.close(instrument, label="book-exit")
|
||||
if f:
|
||||
fills.append(f)
|
||||
return fills
|
||||
delta = target_notional_usd - cur
|
||||
amount = notional_to_amount(instrument, abs(delta), price=mark)
|
||||
if amount <= 0:
|
||||
return fills
|
||||
same_sign = (target_notional_usd > 0) == (cur > 0)
|
||||
if cur != 0.0 and same_sign and abs(target_notional_usd) < abs(cur):
|
||||
side = "sell" if cur > 0 else "buy" # riduci nello stesso verso, reduce_only
|
||||
fills.append(self._submit(instrument, side, amount, reduce_only=True, label="book-reduce"))
|
||||
else:
|
||||
side = "buy" if target_notional_usd > 0 else "sell" # apri/aumenta verso il target
|
||||
fills.append(self.open(instrument, side, amount, label="book-open"))
|
||||
return fills
|
||||
|
||||
# --- 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