"""CONFERMA su feed PURO Binance spot — la fade ha edge reale o era artefatto-print? Il clean close-aware ha spliciato barre Binance-spot dentro la serie Deribit-perp: il crollo del backtest potrebbe (a) rivelare la verita' (l'edge era print) o (b) essere un artefatto dello splice (basis perp/spot ai punti di giunzione). Test decisivo: girare lo STESSO engine fade su una serie 100% Binance spot (sorgente coerente, niente splice). Se anche qui la fade e' negativa -> edge confermato finto. """ from __future__ import annotations import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(PROJECT_ROOT)) import numpy as np, pandas as pd, ccxt from scripts.analysis.risk_management import build_trades, strats_for EX = ccxt.binance({"enableRateLimit": True}) SYM = {"BTC": "BTC/USDT", "ETH": "ETH/USDT"} START = "2020-06-01" # warmup per EMA200/ATR; il report usa 2021+ YEARS = [2021, 2022, 2023, 2024, 2025, 2026] def fetch(asset, tf="15m"): start_ms = int(pd.Timestamp(START, tz="UTC").timestamp() * 1000) end_ms = int(pd.Timestamp("2026-05-26", tz="UTC").timestamp() * 1000) tf_ms = 15 * 60 * 1000 rows = [] since = start_ms while since <= end_ms: r = EX.fetch_ohlcv(SYM[asset], tf, since=since, limit=1000) if not r: break rows += r nxt = int(r[-1][0]) + tf_ms if nxt <= since: break since = nxt df = pd.DataFrame(rows, columns=["timestamp", "open", "high", "low", "close", "volume"]) df = df.drop_duplicates("timestamp").sort_values("timestamp").reset_index(drop=True) return df[df["timestamp"] <= end_ms] def yearly(rows_by_year_ret, ts, trades, pos=0.15): # per-anno compound yr = {y: 1000.0 for y in YEARS} # ricostruisco compound per anno separato (reset capitale ogni anno per ret% annuo) by = {y: [] for y in YEARS} for i, j, r in trades: y = ts.iloc[i].year if y in by: by[y].append(r) out = {} for y in YEARS: cap = 1000.0 for r in by[y]: cap = max(cap + cap * pos * r, 10.0) out[y] = (cap / 1000 - 1) * 100 return out def full_oos(ts, trades, pos=0.15, split_date="2024-10-12"): sd = pd.Timestamp(split_date, tz="UTC") def comp(sub): cap = 1000.0; rets = [] for i, j, r in sub: cap = max(cap + cap * pos * r, 10.0); rets.append(r * pos) return cap, rets capF, rF = comp(trades) oos = [(i, j, r) for i, j, r in trades if ts.iloc[i] >= sd] capO, rO = comp(oos) shF = float(np.mean(rF) / np.std(rF) * np.sqrt(len(rF))) if len(rF) > 1 and np.std(rF) > 0 else 0.0 shO = float(np.mean(rO) / np.std(rO) * np.sqrt(len(rO))) if len(rO) > 1 and np.std(rO) > 0 else 0.0 return (capF / 1000 - 1) * 100, shF, (capO / 1000 - 1) * 100, shO def main(): print(f"Fetch Binance 15m (da {START})...\n") data = {a: fetch(a) for a in ("BTC", "ETH")} print("=" * 92) print(" FADE su PURO Binance spot 15m | RET% per anno (pos 0.15, leva 3x, trend 3.0)") print("=" * 92) print(f" {'sleeve':<12s}" + "".join(f"{y:>9d}" for y in YEARS) + " | FULL% Shrp | OOS% Shrp") print(" " + "-" * 88) for asset in ("BTC", "ETH"): df = data[asset].copy() df = df[pd.to_datetime(df["timestamp"], unit="ms", utc=True).dt.year >= 2021].reset_index(drop=True) \ if False else df # tengo il warmup, filtro nei trade ts = pd.to_datetime(df["timestamp"], unit="ms", utc=True) for code in ("MR01", "MR02", "MR07"): fn, params = strats_for(asset)[code] trades = build_trades(fn(df, **params), df, trend_max=3.0) trades = [(i, j, r) for i, j, r in trades if ts.iloc[i].year >= 2021] yr = yearly(None, ts, trades) fF, shF, fO, shO = full_oos(ts, trades) print(f" {code+'_'+asset:<12s}" + "".join(f"{yr[y]:>+9.0f}" for y in YEARS) + f" | {fF:>+8.0f} {shF:>5.2f} | {fO:>+6.0f} {shO:>5.2f}") if __name__ == "__main__": main()