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>
78 lines
3.0 KiB
Python
78 lines
3.0 KiB
Python
"""EXIT-08 — SL che si STRINGE linearmente col tempo (time-based stop tighten).
|
|
|
|
Lo stop iniziale sl0 si avvicina linearmente a un livello d'arrivo `target`
|
|
man mano che il trade invecchia. TP fisso (tp0) e horizon=max_bars invariati.
|
|
|
|
frac(j) = clamp( (j - i) / (mb * stretch), 0, 1 )
|
|
sl(j) = sl0 + frac(j) * (target - sl0)
|
|
|
|
target ("arrivo"):
|
|
- "entry" -> entry (a fine corsa lo stop e' a breakeven)
|
|
- "midpoint" -> (entry + sl0)/2 (a fine corsa lo stop e' a meta' strada)
|
|
|
|
Direzioni: il segno e' gestito dalla geometria di sl0 vs entry.
|
|
long (d=1): sl0 < entry -> target >= sl0 -> lo stop SALE verso entry
|
|
short (d=-1): sl0 > entry -> target <= sl0 -> lo stop SCENDE verso entry
|
|
In entrambi i casi lo stop si stringe (avvicina al prezzo d'ingresso) col tempo.
|
|
|
|
stretch:
|
|
- 1.0 : lo stop raggiunge `target` a max_bars (fine horizon).
|
|
- 2.0 : lo stop raggiunge `target` a 2*max_bars -> stringe a META' velocita',
|
|
quindi a max_bars e' solo a meta' del cammino sl0->target (piu' lasco).
|
|
|
|
ANTI-LOOK-AHEAD: sl(j) dipende SOLO da {i, j, mb, stretch, entry, sl0}, tutti
|
|
noti all'ENTRATA (close[i]). Nessun dato del bar j o successivi entra nel livello
|
|
attivo nel bar j. Il TP resta tp0. -> contratto rispettato per costruzione.
|
|
|
|
MECCANISMO ATTESO: stringere lo stop col tempo taglia prima i trade che ristagnano
|
|
(non vanno al TP ne' allo SL iniziale e scadrebbero a max_bars vicino al BE/in
|
|
perdita). Rischio: stoppa fuori trade che sarebbero rientrati verso il TP (la fade
|
|
e' mean-reversion: il movimento contrario e' spesso transitorio) -> puo' alzare lo
|
|
stop-rate effettivo e tagliare winner ritardatari.
|
|
|
|
GRID: stretch in {1.0, 2.0} x target in {entry, midpoint} (4 celle).
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
from exit_lab import ExitPolicy, evaluate # noqa: E402
|
|
|
|
|
|
class SlTighten(ExitPolicy):
|
|
name = "sl_tighten"
|
|
|
|
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
|
|
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
|
|
self.stretch = float(params.get("stretch", 1.0))
|
|
self.target_kind = str(params.get("target", "entry"))
|
|
if self.target_kind == "entry":
|
|
self.target = entry
|
|
elif self.target_kind == "midpoint":
|
|
self.target = 0.5 * (entry + sl0)
|
|
else:
|
|
raise ValueError(f"target sconosciuto: {self.target_kind}")
|
|
self.denom = max(self.mb * self.stretch, 1e-9)
|
|
|
|
def levels(self, j: int):
|
|
# frac dipende solo da i, j, mb, stretch -> noti all'entrata. No look-ahead.
|
|
frac = (j - self.i) / self.denom
|
|
if frac < 0.0:
|
|
frac = 0.0
|
|
elif frac > 1.0:
|
|
frac = 1.0
|
|
sl = self.sl0 + frac * (self.target - self.sl0)
|
|
return self.tp0, sl, 1.0
|
|
|
|
|
|
GRID = [
|
|
{"stretch": 1.0, "target": "entry"},
|
|
{"stretch": 1.0, "target": "midpoint"},
|
|
{"stretch": 2.0, "target": "entry"},
|
|
{"stretch": 2.0, "target": "midpoint"},
|
|
]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
evaluate(SlTighten, GRID)
|