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>
138 lines
5.7 KiB
Python
138 lines
5.7 KiB
Python
"""VERIFICA su dati REALI — DIP01 e i 6 pairs hanno edge su prezzi veri?
|
|
|
|
Le 6 fade sono morte su mainnet/Binance (edge = artefatto-print testnet). Restano i
|
|
candidati piu' probabili a sopravvivere: i pairs (market-neutral sul log-ratio -> i
|
|
print di singolo asset si elidono in parte) e DIP01. Test: monkeypatch di load_data /
|
|
get_df -> serie 100% Binance spot (provato ~ mainnet: disc <0.13%), STESSO engine
|
|
canonico (pairs_sim / dip_market_gated). Cache in data/raw/_real_*.parquet (NON tocca
|
|
i canonici).
|
|
"""
|
|
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
|
|
|
|
SYM = {"BTC": "BTC/USDT", "ETH": "ETH/USDT", "LTC": "LTC/USDT", "ADA": "ADA/USDT", "SOL": "SOL/USDT"}
|
|
START, END = "2020-06-01", "2026-05-26"
|
|
YEARS = [2021, 2022, 2023, 2024, 2025, 2026]
|
|
_EX = None
|
|
_CACHE: dict[tuple, pd.DataFrame] = {}
|
|
|
|
|
|
def _ex():
|
|
global _EX
|
|
if _EX is None:
|
|
_EX = ccxt.binance({"enableRateLimit": True})
|
|
return _EX
|
|
|
|
|
|
def fetch(asset, tf):
|
|
key = (asset, tf)
|
|
if key in _CACHE:
|
|
return _CACHE[key]
|
|
cache = PROJECT_ROOT / "data" / "raw" / f"_real_{asset.lower()}_{tf}.parquet"
|
|
if cache.exists():
|
|
df = pd.read_parquet(cache); _CACHE[key] = df; return df
|
|
tf_ms = {"15m": 15, "1h": 60}[tf] * 60 * 1000
|
|
start_ms = int(pd.Timestamp(START, tz="UTC").timestamp() * 1000)
|
|
end_ms = int(pd.Timestamp(END, tz="UTC").timestamp() * 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)
|
|
df = df[df["timestamp"] <= end_ms].reset_index(drop=True)
|
|
df.to_parquet(cache, index=False); _CACHE[key] = df
|
|
return df
|
|
|
|
|
|
# ---- monkeypatch dei loader dei due engine canonici ----
|
|
def _patched_load_data(asset, tf="1h"):
|
|
return fetch(asset, tf)
|
|
|
|
|
|
def _patched_get_df(asset, tf="1h"):
|
|
return fetch(asset, tf)
|
|
|
|
|
|
def daily_from_eq(eq_ts, eq_v):
|
|
idx = pd.date_range("2021-01-01", END, freq="1D", tz="UTC")
|
|
s = pd.Series(eq_v, index=pd.to_datetime(eq_ts, utc=True)).resample("1D").last().reindex(idx).ffill().bfill()
|
|
return s / s.iloc[0]
|
|
|
|
|
|
def metrics_from_daily(s, split_date="2024-10-12"):
|
|
r = s.pct_change().fillna(0.0)
|
|
def m(rr):
|
|
eq = (1 + rr).cumprod(); peak = eq.cummax()
|
|
dd = float(((peak - eq) / peak).max() * 100)
|
|
sh = float(rr.mean() / rr.std() * np.sqrt(365)) if rr.std() > 0 else 0.0
|
|
return (eq.iloc[-1] - 1) * 100, dd, sh
|
|
sd = pd.Timestamp(split_date, tz="UTC")
|
|
fF, ddF, shF = m(r)
|
|
ro = r[r.index >= sd]
|
|
fO, ddO, shO = m(ro)
|
|
yr = {int(y): float(((1 + r[r.index.year == y]).prod() - 1) * 100) for y in YEARS}
|
|
return yr, fF, ddF, shF, fO, ddO, shO
|
|
|
|
|
|
def main():
|
|
print("Fetch Binance spot (1h: BTC/ETH/LTC/ADA/SOL ; 15m: BTC/ETH)...\n")
|
|
for a in SYM:
|
|
fetch(a, "1h")
|
|
for a in ("BTC", "ETH"):
|
|
fetch(a, "15m")
|
|
|
|
import scripts.analysis.pairs_research as PR
|
|
import scripts.analysis.honest_improve2 as HI
|
|
PR.load_data = _patched_load_data
|
|
HI.get_df = _patched_get_df
|
|
from scripts.analysis.pairs_research import pairs_sim, pairs_sim_flat, OOS_FRAC
|
|
from scripts.strategies.PR01_pairs_reversion import PAIRS
|
|
|
|
# ---------- DIP01 ----------
|
|
print("=" * 96)
|
|
print(" DIP01 (BTC 1h dip-buy) su Binance spot REALE | RET% per anno + FULL/OOS (leva 3x)")
|
|
print("=" * 96)
|
|
d = HI.dip_market_gated("BTC", market_n=0, return_equity=True)
|
|
s = daily_from_eq(d["eq_ts"], d["eq_v"])
|
|
yr, fF, ddF, shF, fO, ddO, shO = metrics_from_daily(s)
|
|
print(f" {'':<10s}" + "".join(f"{y:>9d}" for y in YEARS) + " | FULL% DD% Shrp | OOS% Shrp")
|
|
print(f" {'DIP01_BTC':<10s}" + "".join(f"{yr[y]:>+9.0f}" for y in YEARS) +
|
|
f" | {fF:>+7.0f}{ddF:>5.0f}{shF:>6.2f} | {fO:>+6.0f}{shO:>6.2f}")
|
|
|
|
# ---------- PAIRS (5 univ + BLEND 15m) ----------
|
|
print("\n" + "=" * 96)
|
|
print(" PAIRS PR01 su Binance spot REALE | fee 0.20% RT/coppia, leva 3x | (canonico CLAUDE.md fra parentesi)")
|
|
print("=" * 96)
|
|
print(f" {'coppia':<12s}{'trd':>6s}{'win%':>6s}{'CAGR%':>7s}{'DD%':>6s}{'Shrp':>6s}{'oDD%':>6s}{'anni+':>7s}")
|
|
canon = {"ETH/BTC": 4.36, "LTC/ETH": 3.08, "ADA/ETH": 2.69, "BTC/LTC": 2.36, "ETH/SOL": 1.96}
|
|
for a, b, p in PAIRS:
|
|
f = pairs_sim(a, b, **p)
|
|
o = pairs_sim(a, b, **p, split_frac=1 - OOS_FRAC)
|
|
yrs = f["yearly"]; pos_y = sum(1 for v in yrs.values() if v > 0)
|
|
name = f"{a}/{b}"
|
|
print(f" {name:<12s}{f['trades']:>6d}{f['win']:>6.1f}{f['cagr']:>7.0f}{f['dd']:>6.0f}"
|
|
f"{f['sharpe']:>6.2f}{o['dd']:>6.0f}{pos_y:>5d}/{len(yrs)} (canon Sh {canon.get(name,'?')})")
|
|
# BLEND ETH/BTC 15m (mezza size, flat-skip) come nel portafoglio
|
|
r15 = pairs_sim_flat("ETH", "BTC", tf="15m", n=66, z_in=1.674, z_exit=1.0,
|
|
max_bars=35, flat_skip=True, pos=0.075)
|
|
o15 = pairs_sim_flat("ETH", "BTC", tf="15m", n=66, z_in=1.674, z_exit=1.0,
|
|
max_bars=35, flat_skip=True, pos=0.075, split_frac=1 - OOS_FRAC)
|
|
yrs = r15["yearly"]; pos_y = sum(1 for v in yrs.values() if v > 0)
|
|
print(f" {'ETH/BTC 15m':<12s}{r15['trades']:>6d}{r15['win']:>6.1f}{r15['cagr']:>7.0f}{r15['dd']:>6.0f}"
|
|
f"{r15['sharpe']:>6.2f}{o15['dd']:>6.0f}{pos_y:>5d}/{len(yrs)} (BLEND mezza size)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|