#!/usr/bin/env python3 """Multi-asset futures test: does breadth (39 roots) unlock an edge, and is a CTA trend book a genuine DIVERSIFIER to the crypto momentum book? (A) XS momentum (was null on 22 roots) + TS trend-following (directional CTA) on the 39-asset daily futures panel — deflated, per-year, realistic cost. (B) The prize: correlation of the futures TS-trend book to the crypto XS-momentum book (overlap 2019-2026) + the combined two-market book. Low correlation + both positive = real diversification. """ import math import os import sys import numpy as np sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from signal_sweep import load_panel, build_returns, xs_weights, pnl_w, validate, sharpe_t # noqa: E402 import pit_sweep # noqa: E402 from surfer_poc import compute_weights, CFG # noqa: E402 import torch # noqa: E402 DEV = "cuda" if torch.cuda.is_available() else "cpu" def roll(fn, X, L): out = np.full_like(X, np.nan) for t in range(L, len(X)): out[t] = fn(X[t - L:t], axis=0) return out def trailing(lc, L): out = np.full_like(lc, np.nan); out[L:] = lc[L:] - lc[:-L]; return out def main(): roots, days, close, op, inst, weekday = load_panel() lc, R, ov, intr = build_returns(close, op, inst) T, N = close.shape vol60 = roll(np.std, R, 60) invvol = np.where(vol60 > 0, 1.0 / vol60, 0.0) year = (1970 + days / 365.25).astype(int) yrs = list(range(2011, 2027)) def wdir(sig): # directional unit-gross (TS trend, net exposure) s = np.nan_to_num(sig) g = np.sum(np.abs(s), axis=1, keepdims=True); g[g == 0] = 1 return s / g def peryear(p, yy): return [sharpe_t(torch.tensor(p[yy[1:] == y], device=DEV, dtype=torch.float64)) if (yy[1:] == y).sum() > 30 else float("nan") for y in yrs] print(f"\n===== (A) MULTI-ASSET FUTURES — {N} roots, {T} days =====") print(f"{'signal':>16} {'full':>6} {'IS':>6} {'OOS':>6} {'CPCVmed':>8} {'DSR':>5}") sigs = {} for L in [20, 60, 120]: sigs[f"XS_mom_{L}"] = pnl_w(xs_weights(trailing(lc, L)), R, cost_bp=1.5) for L in [60, 120, 250]: sigs[f"TS_trend_{L}"] = pnl_w(wdir(np.sign(trailing(lc, L)) * invvol), R, cost_bp=1.5) NT = 40 # cumulative-session deflation (honest) best_ts, best_ts_pnl, best_med = None, None, -9 for nm, p in sigs.items(): v = validate(p, days, NT) print(f"{nm:>16} {v['full']:>+6.2f} {v['is_']:>+6.2f} {v['oos']:>+6.2f} {v['med']:>+8.2f} {v['dsr']:>5.2f}") if nm.startswith("TS_trend") and v["med"] > best_med: best_ts, best_ts_pnl, best_med = nm, p, v["med"] print(f" futures TS-trend per-year ({best_ts}): " + " ".join(f"{x:+.2f}" if not math.isnan(x) else " n/a" for x in peryear(best_ts_pnl, year))) # ---------- (B) DIVERSIFIER: futures trend vs crypto momentum ---------- syms, cdays, cclose, cqv, cfund = pit_sweep.load() cR = np.zeros_like(cclose); cR[1:] = np.log(cclose)[1:] - np.log(cclose)[:-1] cR = np.where(np.isfinite(cR), cR, 0.0) cfund = np.where(np.isfinite(cfund), cfund, 0.0) cw, _ = compute_weights(cclose, cqv, cdays, CFG) cReff = cR - cfund crypto_book = np.sum(cw[:-1] * cReff[1:], axis=1) # crypto momentum daily PnL (cdays[1:]) fut_book = best_ts_pnl # futures trend daily PnL (days[1:]) # align on common epoch-days fd, cd = days[1:], cdays[1:] fmap = {int(d): fut_book[i] for i, d in enumerate(fd)} cmap = {int(d): crypto_book[i] for i, d in enumerate(cd)} common = sorted(set(fmap) & set(cmap)) fa = np.array([fmap[d] for d in common]); ca = np.array([cmap[d] for d in common]) m = np.isfinite(fa) & np.isfinite(ca) corr = float(np.corrcoef(fa[m], ca[m])[0, 1]) sf, sc = float(np.std(fa[m])), float(np.std(ca[m])) comb = ((1 / sf) * fa[m] + (1 / sc) * ca[m]) / (1 / sf + 1 / sc) cyear = (1970 + np.array(common) / 365.25).astype(int)[m] print(f"\n===== (B) DIVERSIFIER CHECK — futures-trend vs crypto-momentum (overlap {len(fa[m])} days) =====") print(f"futures-trend Sharpe {sharpe_t(torch.tensor(fa[m],device=DEV,dtype=torch.float64)):+.2f} " f"crypto-momentum Sharpe {sharpe_t(torch.tensor(ca[m],device=DEV,dtype=torch.float64)):+.2f}") print(f"CORRELATION = {corr:+.2f} combined-book Sharpe = {sharpe_t(torch.tensor(comb,device=DEV,dtype=torch.float64)):+.2f}") print("combined per-year: " + " ".join(f"{y}:{sharpe_t(torch.tensor(comb[cyear==y],device=DEV,dtype=torch.float64)):+.2f}" for y in range(2019, 2027) if (cyear == y).sum() > 30)) print("\nVERDICT: low |corr| + combined Sharpe > each alone = genuine cross-market diversification.") if __name__ == "__main__": main()