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>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user