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,260 @@
|
||||
"""SH01 EXIT policy 06 — giveback (profit-protection).
|
||||
|
||||
Protezione del profitto via after_bar (mode "close" implicito: uscita sempre al
|
||||
close del bar j). Lo state traccia il PEAK FAVOREVOLE dei close da i (per long il
|
||||
max close; per short il min close, specchiato). Si esce al close del bar j se:
|
||||
|
||||
giveback = (peak_fav - close[j]) * d >= g * ATR14[j-1] (retrace)
|
||||
profit_at_peak = (peak_fav - entry) * d >= m * ATR_ref (era in gain)
|
||||
|
||||
cioe' il trade aveva raggiunto un profitto di almeno m*ATR e poi ha ritracciato
|
||||
di g*ATR dal massimo favorevole. Idea: lascia correre il momentum SH01 finche'
|
||||
sale, ma protegge il guadagno quando rifiata — senza toccare i trade che non sono
|
||||
mai andati in profitto (quelli muoiono a orizzonte come nel baseline, cosi' non
|
||||
si crea un trailing-stop mascherato che taglia i winner-in-drawdown).
|
||||
|
||||
Griglia g in {1.0, 1.5, 2.0, 3.0} x m in {0.5, 1.0}.
|
||||
|
||||
ANTI-LOOK-AHEAD: after_bar(j) decide sul CLOSE del bar j (dato <= j, eseguibile
|
||||
al poll). Il peak favorevole include close[j] (gia' chiuso quando si decide).
|
||||
ATR di riferimento: usiamo ATR14[j-1] per la soglia di giveback (causale, come
|
||||
i livelli) e ATR14[i] per la soglia di profit-at-peak (noto a close[i], cioe'
|
||||
all'apertura del trade). open_trade usa solo close[i]/ATR14[i]. Nessun dato di
|
||||
un bar futuro. OK.
|
||||
|
||||
Profilo SH01: hold a orizzonte (momentum), win ~50%, edge nell'asimmetria dei
|
||||
winner. La famiglia "ride/trailing" sulle fade e' stata SCARTATA; il giveback e'
|
||||
una variante condizionata-al-profitto, pensata per NON toccare i loser-che-
|
||||
recuperano. Pronti a un NO se taglia comunque l'edge.
|
||||
|
||||
PROTOCOLLO: grid (g x m) SOLO sul train (t_hi=OOS_START_MS). Plateau >=3 celle
|
||||
adiacenti migliorative (adiacenza su g, m fisso). Poi OOS una volta sulla config
|
||||
scelta + 2 vicine.
|
||||
|
||||
cd /opt/docker/PythagorasGoal && uv run python scripts/analysis/sh01_exit_policies/06_giveback.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/opt/docker/PythagorasGoal")
|
||||
|
||||
from scripts.analysis.sh01_exit_lab import ( # noqa: E402
|
||||
ExitPolicy, OOS_START_MS, evaluate, load_sleeves, simulate,
|
||||
)
|
||||
|
||||
|
||||
class Giveback(ExitPolicy):
|
||||
def __init__(self, g: float, m: float):
|
||||
self.g = float(g)
|
||||
self.m = float(m)
|
||||
self.name = f"giveback g={g:.1f} m={m:.1f}"
|
||||
|
||||
def open_trade(self, ctx, i, d):
|
||||
entry = ctx["close"][i]
|
||||
a0 = ctx["atr14"][i]
|
||||
a0 = float(a0) if (a0 == a0 and a0 > 0) else 0.0
|
||||
# peak favorevole inizializzato all'entry; atr_ref per il profit-at-peak.
|
||||
return {"entry": entry, "peak": entry, "atr0": a0}
|
||||
|
||||
def levels(self, ctx, i, d, j, st):
|
||||
# nessuno stop a livello: il giveback e' tutto in after_bar.
|
||||
return None, "close"
|
||||
|
||||
def after_bar(self, ctx, i, d, j, st):
|
||||
close = ctx["close"]
|
||||
atr = ctx["atr14"]
|
||||
cj = close[j]
|
||||
# aggiorna il peak FAVOREVOLE con close[j] (gia' chiuso quando decidiamo).
|
||||
# per long: max close; per short: min close (= peak favorevole specchiato).
|
||||
if d == 1:
|
||||
if cj > st["peak"]:
|
||||
st["peak"] = cj
|
||||
else:
|
||||
if cj < st["peak"]:
|
||||
st["peak"] = cj
|
||||
|
||||
a_gb = atr[j - 1]
|
||||
if not (a_gb == a_gb and a_gb > 0):
|
||||
return False
|
||||
a_pk = st["atr0"]
|
||||
if a_pk <= 0:
|
||||
return False
|
||||
|
||||
# profitto raggiunto al peak favorevole (in direzione del trade).
|
||||
profit_at_peak = (st["peak"] - st["entry"]) * d
|
||||
if profit_at_peak < self.m * a_pk:
|
||||
return False
|
||||
# ritracciamento dal peak favorevole fino al close corrente.
|
||||
giveback = (st["peak"] - cj) * d
|
||||
return giveback >= self.g * a_gb
|
||||
|
||||
|
||||
# baseline numbers (exit a orizzonte puro) — dal prompt/harness
|
||||
BASELINE = {
|
||||
"BTC": {"train": dict(ret=127, dd=23, sharpe=2.09, worst=-5.5),
|
||||
"oos": dict(ret=41, dd=8, sharpe=2.18, worst=-3.1)},
|
||||
"ETH": {"train": dict(ret=-26, dd=61, sharpe=-0.16, worst=-14.9),
|
||||
"oos": dict(ret=143, dd=7, sharpe=3.60, worst=-4.6)},
|
||||
}
|
||||
|
||||
GS = [1.0, 1.5, 2.0, 3.0]
|
||||
MS = [0.5, 1.0]
|
||||
|
||||
|
||||
def _row(tag, a, r):
|
||||
print(f" {tag:<10s} {a}: ret={r['ret']:>+7.0f}% dd={r['dd']:>4.0f}% "
|
||||
f"shrp={r['sharpe']:>5.2f} worst={r['worst']:>+5.1f}% "
|
||||
f"stop={r['stop_rate']:>4.1f}% trades={r['trades']}")
|
||||
|
||||
|
||||
def _eth_ok(et, b_eth):
|
||||
return (et["sharpe"] > b_eth["sharpe"] and et["dd"] < b_eth["dd"]
|
||||
and et["worst"] > b_eth["worst"])
|
||||
|
||||
|
||||
def _btc_ok(bt, b_btc):
|
||||
return (bt["sharpe"] >= 0.95 * b_btc["sharpe"]
|
||||
and bt["ret"] >= 0.80 * b_btc["ret"])
|
||||
|
||||
|
||||
def main():
|
||||
sleeves = load_sleeves()
|
||||
b_btc, b_eth = BASELINE["BTC"]["train"], BASELINE["ETH"]["train"]
|
||||
|
||||
print("=" * 78)
|
||||
print("TRAIN GRID (selezione SOLO sul train, t_hi=OOS_START)")
|
||||
print("=" * 78)
|
||||
print(" baseline (orizzonte puro):")
|
||||
evaluate(ExitPolicy(), sleeves=sleeves)
|
||||
print()
|
||||
|
||||
# train[(m,g)] -> {asset: result}
|
||||
train = {}
|
||||
for m in MS:
|
||||
print(f" --- m={m:.1f} (profit-at-peak threshold) ---")
|
||||
for g in GS:
|
||||
pol = Giveback(g, m)
|
||||
row = {}
|
||||
for a in ("BTC", "ETH"):
|
||||
row[a] = simulate(sleeves[a], pol, t_hi=OOS_START_MS)
|
||||
train[(m, g)] = row
|
||||
print(f" g={g:.1f} m={m:.1f}")
|
||||
_row("TRAIN", "BTC", row["BTC"])
|
||||
_row("TRAIN", "ETH", row["ETH"])
|
||||
print()
|
||||
|
||||
print("=" * 78)
|
||||
print("PLATEAU CHECK (train): ETH sharpe up & dd down & worst up,")
|
||||
print(" BTC sharpe>=95% & ret>=80% baseline")
|
||||
print("=" * 78)
|
||||
improving = {} # m -> [g...]
|
||||
for m in MS:
|
||||
imp = []
|
||||
for g in GS:
|
||||
bt, et = train[(m, g)]["BTC"], train[(m, g)]["ETH"]
|
||||
eth_ok = _eth_ok(et, b_eth)
|
||||
btc_ok = _btc_ok(bt, b_btc)
|
||||
ok = eth_ok and btc_ok
|
||||
if ok:
|
||||
imp.append(g)
|
||||
print(f" m={m:.1f} g={g:.1f} ETH_ok={eth_ok} BTC_ok={btc_ok} -> "
|
||||
f"{'IMPROVING' if ok else '-'}")
|
||||
improving[m] = imp
|
||||
print(f" improving cells (m={m:.1f}): {imp}")
|
||||
|
||||
# plateau = >=3 g adiacenti improving in QUALCHE m
|
||||
best_plateau, best_m = [], None
|
||||
for m in MS:
|
||||
imp = improving[m]
|
||||
for idx in range(len(GS)):
|
||||
run = []
|
||||
for j in range(idx, len(GS)):
|
||||
if GS[j] in imp:
|
||||
run.append(GS[j])
|
||||
else:
|
||||
break
|
||||
if len(run) >= 3 and len(run) > len(best_plateau):
|
||||
best_plateau, best_m = run, m
|
||||
print(f" longest adjacent improving run: {best_plateau} (m={best_m}) "
|
||||
f"plateau={'YES' if len(best_plateau) >= 3 else 'NO'}")
|
||||
|
||||
chosen = None
|
||||
if len(best_plateau) >= 3:
|
||||
chosen_g = best_plateau[len(best_plateau) // 2]
|
||||
chosen = (best_m, chosen_g)
|
||||
else:
|
||||
cands = [(m, g) for m in MS for g in improving[m]]
|
||||
if cands:
|
||||
chosen = max(cands, key=lambda mg: train[mg]["ETH"]["sharpe"])
|
||||
|
||||
print()
|
||||
print("=" * 78)
|
||||
if chosen is None:
|
||||
print("NESSUNA cella migliorativa sul train -> verdetto NO (niente OOS).")
|
||||
print("=" * 78)
|
||||
return {"chosen": None, "plateau": best_plateau, "improving": improving,
|
||||
"passes": False, "train": train}
|
||||
|
||||
c_m, c_g = chosen
|
||||
print(f"CHOSEN g={c_g:.1f} m={c_m:.1f} -> OOS (config + 2 vicine g), 1 volta")
|
||||
print("=" * 78)
|
||||
gi = GS.index(c_g)
|
||||
neigh = [GS[x] for x in (gi - 1, gi, gi + 1) if 0 <= x < len(GS)]
|
||||
oos = {}
|
||||
for g in neigh:
|
||||
pol = Giveback(g, c_m)
|
||||
row = {}
|
||||
for a in ("BTC", "ETH"):
|
||||
row[a] = {"train": train[(c_m, g)][a],
|
||||
"oos": simulate(sleeves[a], pol, t_lo=OOS_START_MS)}
|
||||
oos[g] = row
|
||||
print(f" g={g:.1f} m={c_m:.1f}")
|
||||
_row("TRAIN", "BTC", row["BTC"]["train"])
|
||||
_row("OOS", "BTC", row["BTC"]["oos"])
|
||||
_row("TRAIN", "ETH", row["ETH"]["train"])
|
||||
_row("OOS", "ETH", row["ETH"]["oos"])
|
||||
|
||||
print()
|
||||
print("=" * 78)
|
||||
print(f"GATE finale (g={c_g:.1f} m={c_m:.1f}):")
|
||||
bt_tr, et_tr = oos[c_g]["BTC"]["train"], oos[c_g]["ETH"]["train"]
|
||||
bt_oo, et_oo = oos[c_g]["BTC"]["oos"], oos[c_g]["ETH"]["oos"]
|
||||
Bb_o, Be_o = BASELINE["BTC"]["oos"], BASELINE["ETH"]["oos"]
|
||||
|
||||
a_train = _eth_ok(et_tr, b_eth)
|
||||
a_oos = (et_oo["sharpe"] > Be_o["sharpe"] and et_oo["dd"] < Be_o["dd"]
|
||||
and et_oo["worst"] > Be_o["worst"])
|
||||
cond_a = a_train and a_oos
|
||||
b_tr = _btc_ok(bt_tr, b_btc)
|
||||
b_oo = (bt_oo["sharpe"] >= 0.95 * Bb_o["sharpe"]
|
||||
and bt_oo["ret"] >= 0.80 * Bb_o["ret"])
|
||||
cond_b = b_tr and b_oo
|
||||
cond_c = et_oo["ret"] >= 0.80 * Be_o["ret"]
|
||||
cond_d = len(best_plateau) >= 3
|
||||
|
||||
print(f" a) ETH sharpe up & dd down & worst up (train&oos): {cond_a}")
|
||||
print(f" train: shrp {et_tr['sharpe']:.2f} vs {b_eth['sharpe']:.2f} | "
|
||||
f"dd {et_tr['dd']:.0f} vs {b_eth['dd']:.0f} | "
|
||||
f"worst {et_tr['worst']:.1f} vs {b_eth['worst']:.1f}")
|
||||
print(f" oos: shrp {et_oo['sharpe']:.2f} vs {Be_o['sharpe']:.2f} | "
|
||||
f"dd {et_oo['dd']:.0f} vs {Be_o['dd']:.0f} | "
|
||||
f"worst {et_oo['worst']:.1f} vs {Be_o['worst']:.1f}")
|
||||
print(f" b) BTC sharpe>=95% & ret>=80% (train&oos): {cond_b}")
|
||||
print(f" train: shrp {bt_tr['sharpe']:.2f} (>={0.95*b_btc['sharpe']:.2f}) | "
|
||||
f"ret {bt_tr['ret']:.0f} (>={0.80*b_btc['ret']:.0f})")
|
||||
print(f" oos: shrp {bt_oo['sharpe']:.2f} (>={0.95*Bb_o['sharpe']:.2f}) | "
|
||||
f"ret {bt_oo['ret']:.0f} (>={0.80*Bb_o['ret']:.0f})")
|
||||
print(f" c) ETH oos ret>=80% baseline ({0.80*Be_o['ret']:.0f}): {cond_c} "
|
||||
f"(ret={et_oo['ret']:.0f})")
|
||||
print(f" d) plateau: {cond_d} ({best_plateau} m={best_m})")
|
||||
passes = cond_a and cond_b and cond_c and cond_d
|
||||
print(f" PASSES GATE: {passes}")
|
||||
print("=" * 78)
|
||||
|
||||
return {"chosen": chosen, "plateau": best_plateau, "improving": improving,
|
||||
"passes": passes, "oos": oos, "conds": (cond_a, cond_b, cond_c, cond_d)}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user