14522262e6
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>
85 lines
3.3 KiB
Python
85 lines
3.3 KiB
Python
"""EXIT-06 — sar_trail: Parabolic SAR semplificato come trailing stop, TP rimosso.
|
|
|
|
Idea: come EXIT-01 (cavalcata pura senza TP), ma lo stop trailing non e' un
|
|
chandelier a k*ATR fisso bensi' un Parabolic SAR semplificato che ACCELERA: il
|
|
fattore af parte a 0.02 e cresce di af_step ad ogni NUOVO estremo favorevole
|
|
(cap af_max). Lo stop si avvicina sempre piu' in fretta al prezzo man mano che il
|
|
trade corre nella direzione giusta -> protegge i runner stringendo aggressivamente.
|
|
|
|
sar(j) = sar(j-1) + af * (ep - sar(j-1)) con ep = estremo favorevole (high long / low short)
|
|
stop attivo nel bar j = max(sl0, sar) long (min(sl0, sar) short)
|
|
af: 0.02 -> +af_step ad ogni nuovo ep -> cap af_max
|
|
|
|
TP RIMOSSO, horizon = 4*mb (cap HARD_CAP=240).
|
|
|
|
ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1. L'estremo favorevole `ep` e lo
|
|
stato SAR vengono aggiornati incorporando i bar fino a j-1 (gia' chiusi al poll
|
|
del bar j). sar(j-1) -> sar(j) usa ep aggiornato a j-1. Nessun dato del bar j.
|
|
Nessun TP -> nessun fill parziale; after_bar non usato (uscita solo a SL/orizzonte).
|
|
|
|
GRID: af_max in {0.1, 0.2} x af_step in {0.02, 0.04} (4 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
|
|
|
|
|
|
class SarTrail(ExitPolicy):
|
|
name = "sar_trail"
|
|
|
|
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, af_max=0.2, af_step=0.02, **params):
|
|
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
|
self.af_max = float(af_max)
|
|
self.af_step = float(af_step)
|
|
self.horizon = min(4 * mb, HARD_CAP)
|
|
# estremo favorevole (ep) sullo slice [i..j-1]; inizializzato al bar di entrata
|
|
self.ep = ctx["high"][i] if d == 1 else ctx["low"][i]
|
|
self.af = 0.02
|
|
# SAR iniziale: lo stop di partenza (sl0). Il SAR converge verso ep dall'sl0.
|
|
self.sar = sl0
|
|
self._last_seen = i # ultimo indice gia' incorporato nello stato SAR
|
|
self.cur_stop = sl0 # stop monotono (solo si stringe)
|
|
|
|
def _step(self, idx):
|
|
"""Incorpora il bar `idx` (causale, <= j-1) aggiornando ep, af e sar."""
|
|
h = self.ctx["high"][idx]
|
|
l = self.ctx["low"][idx]
|
|
# avanza il SAR di un passo verso l'estremo favorevole corrente
|
|
self.sar = self.sar + self.af * (self.ep - self.sar)
|
|
# nuovo estremo favorevole? -> accelera
|
|
if self.d == 1:
|
|
if h > self.ep:
|
|
self.ep = h
|
|
self.af = min(self.af + self.af_step, self.af_max)
|
|
else:
|
|
if l < self.ep:
|
|
self.ep = l
|
|
self.af = min(self.af + self.af_step, self.af_max)
|
|
|
|
def levels(self, j):
|
|
# incorpora i bar fino a j-1 (gia' chiusi al poll del bar j)
|
|
while self._last_seen < j - 1:
|
|
self._last_seen += 1
|
|
self._step(self._last_seen)
|
|
if self.d == 1:
|
|
if self.sar > self.cur_stop: # lo stop long puo' solo SALIRE (stringersi)
|
|
self.cur_stop = self.sar
|
|
else:
|
|
if self.sar < self.cur_stop: # lo stop short puo' solo SCENDERE
|
|
self.cur_stop = self.sar
|
|
return None, self.cur_stop, 1.0 # TP rimosso
|
|
|
|
|
|
GRID = [
|
|
{"af_max": am, "af_step": st}
|
|
for am in (0.1, 0.2)
|
|
for st in (0.02, 0.04)
|
|
]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
evaluate(SarTrail, GRID)
|