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>
98 lines
4.2 KiB
Python
98 lines
4.2 KiB
Python
"""GATE PORT06 — XS01 (reversione cross-sectional 8 asset), candidato trovato in sessione.
|
|
|
|
XS01: ogni HOLD ore, long i perdenti relativi / short i vincenti su 8 asset (lb LB),
|
|
market-neutral gross 1, fee 0.10% RT/book. Decorrelato (~0) dai pairs. Domanda: aggiunto
|
|
a PORT06 migliora Sharpe/DD? (criterio del progetto: OOS Sharpe non peggiora E DD scende.)
|
|
|
|
uv run python scripts/analysis/xsec_port06_gate.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from src.data.downloader import load_data
|
|
from scripts.analysis.combine_portfolio import port_returns, metrics, SPLIT, OOS_DATE
|
|
from scripts.analysis.report_families import daily_from
|
|
from scripts.portfolios._defs import PORTFOLIOS
|
|
from src.portfolio.sleeves import all_sleeve_equities
|
|
from src.portfolio import weighting as W
|
|
|
|
ASSETS = ["BTC", "ETH", "LTC", "ADA", "SOL", "BNB", "XRP", "DOGE"]
|
|
LB, HOLD, FEE = 48, 12, 0.0005
|
|
|
|
|
|
def xsec_equity(pos=0.15, lev=3.0):
|
|
dfs = {a: load_data(a, "1h")[["timestamp", "close"]].rename(columns={"close": a}).set_index("timestamp")
|
|
for a in ASSETS}
|
|
M = pd.concat(dfs.values(), axis=1, join="inner").sort_index()
|
|
C = M[ASSETS].values
|
|
ts = pd.to_datetime(M.index, unit="ms", utc=True)
|
|
n = len(C); logC = np.log(C)
|
|
cap = 1000.0; eq_ts, eq_v, rets = [], [], []
|
|
last = -1; i = LB
|
|
while i < n - HOLD:
|
|
if i <= last:
|
|
i += 1; continue
|
|
dm = (logC[i] - logC[i - LB]); dm = dm - dm.mean()
|
|
w = -dm; gw = np.sum(np.abs(w))
|
|
if gw < 1e-9:
|
|
i += 1; continue
|
|
w = w / gw
|
|
net = np.sum(w * (logC[i + HOLD] - logC[i])) - FEE * np.sum(np.abs(w)) * 2
|
|
cap = max(cap + cap * pos * lev * net, 10.0)
|
|
rets.append(net); eq_ts.append(ts[i + HOLD]); eq_v.append(cap)
|
|
last = i + HOLD; i += 1
|
|
return daily_from(eq_ts, eq_v), np.array(rets)
|
|
|
|
|
|
def port_metrics(members, ids, clusters, caps):
|
|
dr = pd.DataFrame({i: members[i].pct_change().fillna(0.0) for i in ids})
|
|
w = W.weight_vector("cap", ids, dr, caps=caps, clusters=clusters)
|
|
drp = port_returns({i: members[i] for i in ids}, w)
|
|
return metrics(drp), metrics(drp, lo=SPLIT), w
|
|
|
|
|
|
def main():
|
|
p = PORTFOLIOS["PORT06"]
|
|
eq_base = dict(all_sleeve_equities())
|
|
print("=" * 92)
|
|
print(" GATE PORT06 — XS01 reversione cross-sectional (8 asset) | OOS da", OOS_DATE)
|
|
print("=" * 92)
|
|
|
|
for pos, lbl in [(0.15, "XS01 pos0.15"), (0.075, "XS01 pos0.075 (mezza)")]:
|
|
e, r = xsec_equity(pos=pos)
|
|
# correlazione con i pairs e i fade
|
|
cors = {}
|
|
for ref in ("PR_ETHBTC", "MR02_ETH"):
|
|
j = pd.concat([e.pct_change(), eq_base[ref].pct_change()], axis=1).dropna()
|
|
cors[ref] = round(j.iloc[:, 0].corr(j.iloc[:, 1]), 3)
|
|
ids0 = list(p.sleeve_ids); cl0 = p.clusters; caps = p.caps
|
|
f0, o0, _ = port_metrics(eq_base, ids0, cl0, caps)
|
|
mem = dict(eq_base); mem["XS01"] = e
|
|
ids1 = ids0 + ["XS01"]; cl1 = dict(cl0); cl1["XS01"] = "xsec"
|
|
f1, o1, w1 = port_metrics(mem, ids1, cl1, caps)
|
|
# risk contribution di XS01
|
|
drm = pd.DataFrame({i: mem[i].pct_change().fillna(0.0) for i in ids1})
|
|
cov = drm.cov(); wv = np.array([w1[i] for i in ids1])
|
|
pv = float(wv @ cov.values @ wv)
|
|
rc = {i: float(w1[i] * (cov.values[k] @ wv) / pv * 100) for k, i in enumerate(ids1)}
|
|
print(f"\n[{lbl}] corr XS01 vs {cors} | peso XS01 {w1['XS01']*100:.1f}% | "
|
|
f"risk-contrib XS01 {rc['XS01']:.1f}%")
|
|
print(f" {'config':<16}{'FULL Sh':>8}{'FULL DD%':>9}{'OOS Sh':>8}{'OOS DD%':>8}")
|
|
print(f" {'ATTUALE':<16}{f0['sharpe']:>8.2f}{f0['dd']:>9.2f}{o0['sharpe']:>8.2f}{o0['dd']:>8.2f}")
|
|
print(f" {'+XS01':<16}{f1['sharpe']:>8.2f}{f1['dd']:>9.2f}{o1['sharpe']:>8.2f}{o1['dd']:>8.2f}")
|
|
ok = (o1["sharpe"] >= o0["sharpe"] - 0.02 and o1["dd"] <= o0["dd"] + 1e-9
|
|
and f1["sharpe"] >= f0["sharpe"] - 0.02 and f1["dd"] <= f0["dd"] + 1e-9)
|
|
print(f" => {'PROMOSSO' if ok else 'non passa il criterio stretto (vedi numeri)'}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|