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>
101 lines
4.0 KiB
Python
101 lines
4.0 KiB
Python
"""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()
|