#!/usr/bin/env python3 """The one evidence-backed non-crypto lever: add a diversified TREND sleeve to 60/40. Research verdict: trend/managed-futures is the only premium genuinely uncorrelated-to-equity AND convex in crises (made money in 2022 when stocks+bonds both fell). We tested trend standalone (marginal) and as a filter (hurt) — never as a DIVERSIFYING SLEEVE on 60/40, the specific thing the evidence supports. Build a long/SHORT diversified time-series-momentum book (vol-targeted), blend with 60/40 at various weights, measure Sharpe + maxDD + crisis-year behavior. Honest question: does 60/40 + trend beat plain 60/40 on risk-adjusted terms (~0.7 -> ~0.8-0.9)? """ 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 # noqa: E402 TVOL = 0.10 def metrics(r): r = np.asarray(r); r = r[np.isfinite(r)] if len(r) < 50 or r.std() == 0: return dict(sr=float("nan"), ann=float("nan"), dd=float("nan"), cal=float("nan")) ann = r.mean() * 252; vol = r.std() * math.sqrt(252) eq = np.cumprod(1 + r); dd = float((eq / np.maximum.accumulate(eq) - 1).min()) return dict(sr=ann / vol, ann=ann, dd=dd, cal=ann / (abs(dd) + 1e-9)) def voltarget(r, win=63): out = np.zeros_like(r) for t in range(win, len(r)): rv = np.std(r[t - win:t]) * math.sqrt(252) out[t] = r[t] * min(3.0, TVOL / (rv + 1e-9)) return out def main(): roots, days, close, op, inst = load_panel()[:5] lc, R, ov, intr = build_returns(close, op, inst) T, N = close.shape year = (1970 + days / 365.25).astype(int) vol63 = np.full_like(lc, np.nan) for t in range(63, T): vol63[t] = np.nanstd(R[t - 63:t], axis=0) iv = 1.0 / np.where(vol63 > 0, vol63, np.nan) avail = np.isfinite(close) & np.isfinite(vol63) & (vol63 > 0) es, zn = roots.index("ES"), roots.index("ZN") r6040 = 0.6 * R[1:, es] + 0.4 * R[1:, zn] # plain 60/40 (daily returns) # diversified long/SHORT time-series-momentum (CTA) sleeve, vol-targeted tsig = np.full_like(lc, np.nan) tsig[252:] = np.sign(lc[252:] - lc[:-252]) # long if 12mo up, short if down w = np.where(avail & np.isfinite(tsig), tsig * iv, 0.0) g = np.sum(np.abs(w), axis=1, keepdims=True); g[g == 0] = 1 w = w / g rtrend_raw = np.sum(w[:-1] * R[1:], axis=1) rtrend = voltarget(rtrend_raw) # the managed-futures sleeve # normalize both legs to ~TVOL so blend weights are meaningful, then blend def norm(r): return voltarget(r) base = norm(r6040) sleeve = rtrend yr = year[1:] print(f"\n===== 60/40 + TREND SLEEVE ({N}-asset long/short CTA, vol-targeted) =====") print(f"{'blend':>22} {'Sharpe':>7} {'ann%':>6} {'maxDD%':>7} {'Calmar':>7}") blends = [("100% 60/40", 1.0, 0.0), ("80/20 +trend", 0.8, 0.2), ("70/30 +trend", 0.7, 0.3), ("60/40 +trend", 0.6, 0.4), ("50/50 +trend", 0.5, 0.5), ("100% trend", 0.0, 1.0)] rows = {} for nm, a, b in blends: r = a * base + b * sleeve rows[nm] = r; m = metrics(r) print(f"{nm:>22} {m['sr']:>+7.2f} {100*m['ann']:>+6.1f} {100*m['dd']:>+7.1f} {m['cal']:>7.2f}") # correlation of the two legs (the diversification engine) mask = np.isfinite(base) & np.isfinite(sleeve) & (base != 0) & (sleeve != 0) corr = np.corrcoef(base[mask], sleeve[mask])[0, 1] print(f"\n corr(60/40, trend) = {corr:+.2f} (negative/low = the diversification that lifts Sharpe + cuts DD)") print(" CRISIS-YEAR check — Sharpe by year (does trend save the equity/bond drawdowns?):") for nm in ["100% 60/40", "70/30 +trend"]: r = rows[nm] py = " ".join(f"{y}:{metrics(r[yr==y])['sr']:+.1f}" for y in [2015, 2018, 2020, 2022, 2025] if (yr == y).sum() > 100) print(f" {nm:>14}: {py}") print("\nVERDICT: if 70/30 or 60/40+trend Sharpe > plain 60/40 AND maxDD smaller (esp 2022) = the evidence-backed") print("lever works; ~0.7 -> ~0.8-0.9 with crisis protection. If not, 60/40 is genuinely the retail ceiling.") if __name__ == "__main__": main()