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,108 @@
|
||||
"""EXIT-18 — SL STRUTTURALE su swing low/high (structural / chandelier-by-structure).
|
||||
|
||||
Idea: invece di uno stop FISSO a sl0 (ATR dall'entrata), uno stop ancorato alla
|
||||
STRUTTURA recente del prezzo. Per un long: il livello "naturale" sotto cui la tesi
|
||||
mean-reversion e' invalidata e' il minimo dello swing recente; se il prezzo rompe
|
||||
sotto il minimo degli ultimi n bar la fade ha torto. Simmetrico per lo short sul
|
||||
massimo recente.
|
||||
|
||||
long (d=1): level(j) = min(low[j-n .. j-1]) - buf
|
||||
short (d=-1): level(j) = max(high[j-n .. j-1]) + buf
|
||||
buf = 0.25 * atr14[j-1]
|
||||
|
||||
SOLO-STRINGENTE: lo stop parte da sl0 (protezione iniziale invariata) e si aggiorna
|
||||
SOLO se il livello swing e' PIU' PROTETTIVO (cricchetto):
|
||||
long : cur_stop = max(cur_stop, level) (lo stop puo' solo SALIRE)
|
||||
short: cur_stop = min(cur_stop, level) (lo stop puo' solo SCENDERE)
|
||||
Cosi' man mano che il prezzo (per una fade vincente) torna verso la media, lo swing
|
||||
low/high sale/scende e lo stop segue, bloccando profitto. Non si allenta MAI.
|
||||
|
||||
TP fisso (tp0) e horizon=max_bars invariati: questa famiglia cambia SOLO lo stop.
|
||||
|
||||
ANTI-LOOK-AHEAD (contratto): levels(j) usa SOLO dati con indice <= j-1:
|
||||
- massimi/minimi sullo slice [j-n .. j-1] (lookback chiuso a j-1);
|
||||
- buffer da atr14[j-1] (indicatore causale, valore del bar precedente).
|
||||
Mantengo cur_stop in modo incrementale ma lo aggiorno con i bar fino a j-1, mai j.
|
||||
Nessun dato del bar j o successivi entra nel livello attivo nel bar j.
|
||||
|
||||
MECCANISMO ATTESO: lo stop strutturale e' tipicamente PIU' LASCO di sl0 all'inizio
|
||||
(il minimo recente puo' stare oltre sl0): in quel caso il cricchetto lo IGNORA e
|
||||
resta a sl0 (non allentiamo mai). Quando invece la struttura si stringe (lo swing
|
||||
low risale verso il prezzo dopo che la fade comincia a funzionare) lo stop SALE,
|
||||
proteggendo i guadagni di un trade che poi rintraccia prima del TP.
|
||||
Prior dal repo (ladder scartato): il TP della fade sta alla MEDIA, dove il movimento
|
||||
e' esaurito -> oltre il TP non c'e' runner. Qui non tocchiamo il TP, quindi non
|
||||
puntiamo a cavalcare oltre: cerchiamo SOLO di tagliare meglio i loser/ristagni
|
||||
proteggendo profitto. RISCHIO (fade = mean-reversion): stringere lo stop su un
|
||||
pullback transitorio stoppa fuori trade che sarebbero rientrati al TP -> winner
|
||||
tagliati e stop-rate su. Lo misuriamo.
|
||||
|
||||
GRID: n in {5, 10, 20} (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 SwingStop(ExitPolicy):
|
||||
name = "swing_stop"
|
||||
|
||||
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, n=10, **params):
|
||||
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
||||
self.n = int(n)
|
||||
self.low = ctx["low"]
|
||||
self.high = ctx["high"]
|
||||
self.atr14 = ctx["atr14"]
|
||||
# stop a cricchetto: parte dalla protezione iniziale, puo' solo stringersi
|
||||
self.cur_stop = sl0
|
||||
# indice fino a cui ho gia' incorporato i bar nel cricchetto (escluso)
|
||||
self._last_seen = i
|
||||
|
||||
def _swing_level(self, j: int):
|
||||
"""Livello swing causale usando SOLO low/high su [j-n .. j-1] e atr14[j-1]."""
|
||||
lo = max(self.i, j - self.n) # non andare prima dell'entrata
|
||||
hi = j # slice [lo:hi] => indici <= j-1
|
||||
if hi <= lo:
|
||||
return None
|
||||
a = self.atr14[j - 1]
|
||||
if not np.isfinite(a):
|
||||
a = 0.0
|
||||
buf = 0.25 * a
|
||||
if self.d == 1:
|
||||
return float(np.min(self.low[lo:hi])) - buf
|
||||
else:
|
||||
return float(np.max(self.high[lo:hi])) + buf
|
||||
|
||||
def levels(self, j: int):
|
||||
d = self.d
|
||||
# aggiorna il cricchetto bar-per-bar fino a j-1 (causale). Per ogni bar k
|
||||
# passato calcolo il livello swing attivo "a quel momento" e stringo lo stop.
|
||||
while self._last_seen < j:
|
||||
self._last_seen += 1
|
||||
k = self._last_seen
|
||||
lvl = self._swing_level(k)
|
||||
if lvl is None:
|
||||
continue
|
||||
if d == 1:
|
||||
if lvl > self.cur_stop: # cricchetto long: lo stop solo SALE
|
||||
self.cur_stop = lvl
|
||||
else:
|
||||
if lvl < self.cur_stop: # cricchetto short: lo stop solo SCENDE
|
||||
self.cur_stop = lvl
|
||||
return self.tp0, self.cur_stop, 1.0
|
||||
|
||||
|
||||
GRID = [
|
||||
{"n": 5},
|
||||
{"n": 10},
|
||||
{"n": 20},
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
evaluate(SwingStop, GRID)
|
||||
Reference in New Issue
Block a user