Files
Adriano Dal Pastro 14522262e6 chore(reset): v2.0.0 — storico certificato Deribit mainnet, ripartenza pulita
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera
libreria "validata OOS" era artefatto di feed contaminato (print fantasma del
feed Cerbero TESTNET + storico Binance/USDT).

- Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e
  CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia
  (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample
  (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE
  50-82% barre flat; XRP/BNB non certificabili).
- Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni
  portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST
  con segnale residuo, da ri-validare in isolamento.
- Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio,
  runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/
  portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/
  (preservati, non cancellati). Diario consolidato in un unico documento.
- Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal +
  src/backtest/engine + load_data; tool dati certificati (rebuild_history,
  certify_feed, audit_feed, multi_source_check).
- Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico
  (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:20:59 +00:00

94 lines
3.8 KiB
Python

"""EXIT-05 — ratchet: stop a cricchetto sul profitto su close (high-water-mark).
Idea: una volta che il trade ha messo a segno profitto (su CLOSE), proteggiamo
una FRAZIONE f del profitto migliore raggiunto. hwm = miglior close a favore visto
finora; quando hwm e' a favore dell'entrata, lo stop si porta a
long : stop = entry + f*(hwm - entry)
short: stop = entry - f*(entry - hwm)
Lo stop e' SOLO-STRINGENTE (cricchetto): puo' solo avvicinarsi al prezzo, mai
allentarsi. Parte da sl0 (protezione iniziale invariata finche' il ratchet non
supera sl0).
Differenza dal trailing chandelier (EXIT-01): qui lo stop e' ancorato all'ENTRY
+ frazione del profitto su close (non al massimo high - k*ATR). Cattura profitto
in % del guadagno realizzato; con f<1 lascia un cuscinetto sotto l'hwm.
VARIANTI:
- keep_tp=True : il TP fisso tp0 resta (exit standard alla media) + ratchet di
backup sullo SL. horizon = max_bars.
- keep_tp=False: TP RIMOSSO -> ride puro protetto dal cricchetto, horizon=2*mb
(cap HARD_CAP). Qui il ratchet e' l'unica uscita oltre l'orizzonte.
ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1:
- hwm calcolato sui close[i .. j-1] (mantenuto incrementalmente, aggiornato col
bar j-1 prima di calcolare lo stop attivo nel bar j; close[i]=entry e' incluso
cosi' hwm>=entry sempre, lo stop non scende mai sotto entry una volta armato).
Nessun TP nella variante ride -> nessun fill parziale. after_bar non usato.
GRID: f in {0.3, 0.5, 0.7} x keep_tp in {True, False} (6 celle).
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from exit_lab import ExitPolicy, evaluate, HARD_CAP # noqa: E402
class Ratchet(ExitPolicy):
name = "ratchet"
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, f=0.5, keep_tp=True, **params):
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
self.f = float(f)
self.keep_tp = bool(keep_tp)
self.close = ctx["close"]
if not self.keep_tp:
self.horizon = min(2 * mb, HARD_CAP)
# high-water-mark del close a favore sullo slice [i..j-1]; il primo bar
# valutato e' j=i+1 -> slice [i..i] = close[i] = entry (gia' incorporato).
self.hwm = self.close[i] # = entry
self._last_seen = i
self.armed = False # diventa True quando hwm > entry (a favore)
# stop a cricchetto: parte da sl0, puo' solo stringersi
self.cur_stop = sl0
def levels(self, j: int):
c = self.close
d = self.d
# incorpora i close fino a j-1 (causali, gia' chiusi al poll del bar j)
while self._last_seen < j - 1:
self._last_seen += 1
cv = c[self._last_seen]
if d == 1:
if cv > self.hwm:
self.hwm = cv
else:
if cv < self.hwm:
self.hwm = cv
tp = self.tp0 if self.keep_tp else None
# profitto su close del miglior bar a favore (>=0 perche' hwm parte da entry)
prof = (self.hwm - self.entry) * d
if prof > 0: # il trade e' andato a favore: arma il ratchet
self.armed = True
if d == 1:
cand = self.entry + self.f * (self.hwm - self.entry)
if cand > self.cur_stop: # cricchetto: lo stop long solo SALE
self.cur_stop = cand
else:
cand = self.entry - self.f * (self.entry - self.hwm)
if cand < self.cur_stop: # cricchetto: lo stop short solo SCENDE
self.cur_stop = cand
return tp, self.cur_stop, 1.0
GRID = [
{"f": f, "keep_tp": keep_tp}
for f in (0.3, 0.5, 0.7)
for keep_tp in (True, False)
]
if __name__ == "__main__":
evaluate(Ratchet, GRID)