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,88 @@
|
||||
"""EXIT-19 — TP RIMOSSO, exit al CANALE DONCHIAN OPPOSTO (donchian_trail).
|
||||
|
||||
Idea: la fade attuale esce al TP (sulla media) + SL fisso (ATR dall'entrata) +
|
||||
max_bars. Qui TOGLIAMO il TP e usiamo come unica uscita di prezzo uno STOP
|
||||
DINAMICO ancorato al canale Donchian OPPOSTO alla direzione del trade:
|
||||
|
||||
long (d=1): stop(j) = min(low[j-n .. j-1]) (canale inferiore)
|
||||
short (d=-1): stop(j) = max(high[j-n .. j-1]) (canale superiore)
|
||||
|
||||
Per un long che funziona (il prezzo risale verso la media) il canale inferiore
|
||||
SALE bar dopo bar -> lo stop segue e blocca profitto; usciamo quando il prezzo
|
||||
ritraccia sotto il minimo recente. Simmetrico short.
|
||||
|
||||
FLOOR a sl0 (mai PEGGIO dello SL originale): il livello attivo e' floorato alla
|
||||
protezione iniziale -> non si allenta mai oltre sl0.
|
||||
long : stop = max(channel_low, sl0)
|
||||
short: stop = min(channel_high, sl0)
|
||||
|
||||
HORIZON = 4*mb (cap HARD_CAP=240): senza TP la posizione puo' restare a lungo,
|
||||
quindi diamo molto piu' respiro al time-stop; l'uscita "naturale" e' il canale.
|
||||
|
||||
DIFFERENZA da EXIT-18 (swing_stop): qui (a) NON c'e' TP affatto (li' tp0 restava),
|
||||
(b) niente cricchetto persistente: lo stop e' il canale RICALCOLATO ogni bar (puo'
|
||||
anche allentarsi rispetto al bar prima, ma mai sotto sl0 grazie al floor),
|
||||
(c) horizon esteso 4x. E' una uscita puramente trend-following/Donchian innestata
|
||||
su un ingresso mean-reversion.
|
||||
|
||||
ANTI-LOOK-AHEAD (contratto): levels(j) usa SOLO dati con indice <= j-1:
|
||||
- min/max sullo slice [j-n .. j-1] (lookback chiuso a j-1, lo[lo:hi] con hi=j);
|
||||
- nessun dato del bar j entra nel livello attivo nel bar j;
|
||||
- non si guarda mai high/low[j] per decidere lo stop attivo nel bar j.
|
||||
|
||||
PRIOR dal repo (ladder scartato): il TP della fade sta alla MEDIA, dove il
|
||||
movimento e' esaurito; "il runner non corre". Quindi togliendo il TP rischiamo di
|
||||
restare in posizione MENTRE il prezzo ristagna/rientra, pagando giveback e fee.
|
||||
Il canale opposto dovrebbe limitare il giveback, ma la mean-reversion fa rientrare
|
||||
il prezzo prima che il canale si stringa -> probabile uscita PEGGIORE del TP.
|
||||
Lo misuriamo senza pregiudizio.
|
||||
|
||||
GRID: n in {10, 20, 30} (3 celle).
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from exit_lab import ExitPolicy, evaluate # noqa: E402
|
||||
|
||||
|
||||
class DonchianTrail(ExitPolicy):
|
||||
name = "donchian_trail"
|
||||
|
||||
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, n=20, **params):
|
||||
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
||||
self.n = int(n)
|
||||
self.low = ctx["low"]
|
||||
self.high = ctx["high"]
|
||||
# TP rimosso, horizon esteso 4x (il cap a HARD_CAP lo applica l'engine)
|
||||
self.horizon = 4 * mb
|
||||
|
||||
def levels(self, j: int):
|
||||
d = self.d
|
||||
# canale opposto causale: slice [lo : j] => indici <= j-1
|
||||
lo = max(self.i, j - self.n)
|
||||
hi = j
|
||||
if hi <= lo:
|
||||
# primo bar dopo l'entrata: nessuna finestra -> usa solo sl0 (no TP)
|
||||
return None, self.sl0, 1.0
|
||||
if d == 1:
|
||||
ch = float(np.min(self.low[lo:hi]))
|
||||
stop = max(ch, self.sl0) # floor: mai sotto sl0
|
||||
else:
|
||||
ch = float(np.max(self.high[lo:hi]))
|
||||
stop = min(ch, self.sl0) # floor: mai sopra sl0
|
||||
return None, stop, 1.0 # TP = None (rimosso)
|
||||
|
||||
|
||||
GRID = [
|
||||
{"n": 10},
|
||||
{"n": 20},
|
||||
{"n": 30},
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
evaluate(DonchianTrail, GRID)
|
||||
Reference in New Issue
Block a user