Files
PythagorasGoal/Old/scripts/analysis/exit_policies/11_z_overshoot.py
Adriano Dal Pastro 14522262e6 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>
2026-06-19 15:20:59 +00:00

94 lines
3.4 KiB
Python

"""EXIT-11 — z_overshoot: TP OLTRE la media (overshoot di z_off deviazioni std).
PRIOR (vincolante): le fade puntano alla MEDIA. Oggi il TP e' la SMA_n al close[i-1].
Ipotesi di questa famiglia: la reversione spesso NON si ferma esattamente alla media,
ma la attraversa (overshoot). Spostando il TP un po' OLTRE la media nella direzione
del trade catturiamo l'overshoot quando c'e', al prezzo di mancare alcuni TP che si
fermano alla media (che poi rientrano o vengono presi da max_bars/SL).
tp(j) = SMA_n[j-1] + d * z_off * STD_n[j-1]
long (d=+1): si compra sotto la media -> TP = media + z_off*std (sopra la media: piu' lontano).
short (d=-1): si vende sopra la media -> TP = media - z_off*std (sotto la media: piu' lontano).
In ENTRAMBI i casi il TP si allontana di z_off*std OLTRE la media nella direzione
del profitto -> target piu' ambizioso. NB: lo z e' un overshoot ADDIZIONALE rispetto
alla media corrente; la media stessa si muove (come EXIT-10) quindi il target insegue
la media + overshoot.
CAP ONESTO (anti exit-in-perdita): se la media + overshoot finisce contro l'entry
(media e' gia' scappata oltre entry contro di noi), il TP non deve diventare un'uscita
in perdita mascherata. Floor a breakeven+fee come EXIT-10:
long : tp(j) = max(tp(j), entry*(1+0.002))
short: tp(j) = min(tp(j), entry*(1-0.002))
SL FISSO (sl0) invariato; horizon = max_bars invariato.
ANTI-LOOK-AHEAD: prepare() precalcola sma_n = rolling(n).mean() e std_n =
rolling(n).std(ddof=0) su close (causali: ogni valore dipende solo da close <= indice).
In levels(j) si legge sma_n[j-1] e std_n[j-1] -> solo dati <= j-1, mai il bar j.
SL e horizon invariati. OK per costruzione.
GRID: n in {20, 50} x z_off in {0.25, 0.5} (4 celle).
"""
import sys
from pathlib import Path
import numpy as np
import pandas as pd
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from exit_lab import ExitPolicy, evaluate # noqa: E402
class ZOvershoot(ExitPolicy):
name = "z_overshoot"
@classmethod
def prepare(cls, ctx, **params):
n = int(params.get("n", 50))
kmean = f"sma_{n}"
kstd = f"std_{n}"
if kmean not in ctx or kstd not in ctx:
c = pd.Series(ctx["close"])
ctx[kmean] = c.rolling(n).mean().values
ctx[kstd] = c.rolling(n).std(ddof=0).values
def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
n = int(params.get("n", 50))
self.z_off = float(params.get("z_off", 0.5))
self.sma = ctx[f"sma_{n}"]
self.std = ctx[f"std_{n}"]
# cap onesto: il TP non puo' diventare un'uscita in perdita
if d == 1:
self.cap = entry * (1.0 + 0.002)
else:
self.cap = entry * (1.0 - 0.002)
def levels(self, j: int):
# SOLO dati <= j-1
m = self.sma[j - 1]
s = self.std[j - 1]
if not (np.isfinite(m) and np.isfinite(s)):
# warmup non pronto -> ricadi sul TP fisso d'entrata
return self.tp0, self.sl0, 1.0
tp = m + self.d * self.z_off * s
if self.d == 1:
tp = max(tp, self.cap)
else:
tp = min(tp, self.cap)
return tp, self.sl0, 1.0
GRID = [
{"n": 20, "z_off": 0.25},
{"n": 20, "z_off": 0.5},
{"n": 50, "z_off": 0.25},
{"n": 50, "z_off": 0.5},
]
if __name__ == "__main__":
evaluate(ZOvershoot, GRID)