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>
102 lines
4.4 KiB
Python
102 lines
4.4 KiB
Python
"""GATE — XS01 phase-tranching (K sub-book sfasati di hold/K, capitale comune).
|
|
|
|
`xs01_tranche_research.py` ha misurato timing-luck di fase reale (FULL Sharpe
|
|
1.87-2.87, DD 13.8-33.1% a seconda della fase di partenza, che live e' arbitraria).
|
|
Qui il confronto ONESTO su equity GIORNALIERA (stessa convenzione dei gate del
|
|
progetto: combine_portfolio.metrics) + gate PORT06 swap-sleeve:
|
|
[1] standalone: base (fase canonica) vs K=2 vs K=3, FULL e OOS, daily Sharpe/DD;
|
|
in piu' il range delle 12 fasi singole (il rischio che il tranching elimina).
|
|
[2] PORT06: members canonici con XS01 sostituito dalla variante tranched.
|
|
Criterio (tutti): OOS Sharpe portafoglio non peggiora (>-0.02) E DD non sale;
|
|
K=2 e K=3 devono essere ENTRAMBI >= base (plateau, non best-pick di K).
|
|
|
|
uv run python scripts/analysis/xs01_tranche_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 scripts.analysis.combine_portfolio import port_returns, metrics, SPLIT, OOS_DATE
|
|
from scripts.analysis.report_families import daily_from
|
|
from scripts.analysis.xs01_tranche_research import xsec_trades
|
|
from scripts.strategies.XS01_cross_sectional import aligned_panel, HOLD, POS, LEV
|
|
from scripts.portfolios._defs import PORTFOLIOS
|
|
from src.portfolio.sleeves import all_sleeve_equities
|
|
from src.portfolio import weighting as W
|
|
|
|
|
|
def daily_equity_for(phases, M, ts):
|
|
"""Trade di tutte le fasi su capitale comune (peso 1/K) -> equity daily."""
|
|
K = len(phases)
|
|
allt = sorted([t for ph in phases for t in xsec_trades(phase=ph, M=M)],
|
|
key=lambda t: t[1])
|
|
cap = 1000.0
|
|
eq_ts, eq_v = [], []
|
|
for i, j, net in allt:
|
|
cap = max(cap + cap * POS * LEV * net / K, 10.0)
|
|
eq_ts.append(ts[j])
|
|
eq_v.append(cap)
|
|
return daily_from(eq_ts, eq_v)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def main():
|
|
M = aligned_panel()
|
|
ts = pd.to_datetime(M.index, unit="ms", utc=True)
|
|
p = PORTFOLIOS["PORT06"]
|
|
print("=" * 92)
|
|
print(" GATE — XS01 phase-tranching | equity daily, OOS da", OOS_DATE)
|
|
print("=" * 92)
|
|
|
|
variants = {"base (fase 0)": [0],
|
|
"K=2 (fasi 0,6)": [0, 6],
|
|
"K=3 (fasi 0,4,8)": [0, 4, 8]}
|
|
eqs = {k: daily_equity_for(v, M, ts) for k, v in variants.items()}
|
|
|
|
print(f"\n [1] STANDALONE daily — {'config':<18}{'FULL Sh':>8}{'FULL DD%':>9}{'OOS Sh':>8}{'OOS DD%':>8}")
|
|
for k, e in eqs.items():
|
|
r = e.pct_change().fillna(0.0)
|
|
f, o = metrics(r), metrics(r, lo=SPLIT)
|
|
print(f" {k:<22}{f['sharpe']:>8.2f}{f['dd']:>9.2f}{o['sharpe']:>8.2f}{o['dd']:>8.2f}")
|
|
# range delle 12 fasi singole (daily): il rischio di fase che il tranching elimina
|
|
fs, os_ = [], []
|
|
for ph in range(HOLD):
|
|
r = daily_equity_for([ph], M, ts).pct_change().fillna(0.0)
|
|
fs.append(metrics(r)["sharpe"])
|
|
os_.append(metrics(r, lo=SPLIT)["sharpe"])
|
|
print(f" 12 fasi singole: FULL Sh {min(fs):.2f}-{max(fs):.2f} | OOS Sh {min(os_):.2f}-{max(os_):.2f}")
|
|
|
|
eq_base = dict(all_sleeve_equities())
|
|
ids, cl, caps = list(p.sleeve_ids), p.clusters, p.caps
|
|
print(f"\n [2] PORT06 swap-sleeve — {'config':<18}{'FULL Sh':>8}{'FULL DD%':>9}{'OOS Sh':>8}{'OOS DD%':>8}")
|
|
f0, o0 = port_metrics(eq_base, ids, cl, caps)
|
|
print(f" {'ATTUALE (base)':<22}{f0['sharpe']:>8.2f}{f0['dd']:>9.2f}{o0['sharpe']:>8.2f}{o0['dd']:>8.2f}")
|
|
verdicts = []
|
|
for k in ("K=2 (fasi 0,6)", "K=3 (fasi 0,4,8)"):
|
|
mem = dict(eq_base)
|
|
mem["XS01"] = eqs[k]
|
|
f1, o1 = port_metrics(mem, ids, cl, caps)
|
|
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)
|
|
verdicts.append(ok)
|
|
print(f" {k:<22}{f1['sharpe']:>8.2f}{f1['dd']:>9.2f}{o1['sharpe']:>8.2f}{o1['dd']:>8.2f}"
|
|
f" {'OK' if ok else 'NO'}")
|
|
print(f"\n => {'PROMOSSO (plateau K=2 e K=3)' if all(verdicts) else 'NON promosso (serve plateau su entrambi i K)'}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|