#!/usr/bin/env python3 """Side-by-side comparison of the wealth candidates (the CAGR reference plan, re-runnable). Backtest (2006-2026, incl 2008) + the $360k+$8k/mo 20y trajectory for: adaptive multi-strat book, SPY buy-hold, SPY 1.3x (cheap futures financing), 60/40, 70/30 SPY+book, SPY+adaptive overlay. Honest: CAGR builds wealth; more CAGR = more drawdown. See docs/.../2026-06-08-cagr-reference-plan.md. python3 strategy_compare.py # full table python3 strategy_compare.py --p0 100000 --pmt 2000 --fin 0.03 --years 20 """ import datetime import json import math import os import sys import urllib.request import numpy as np sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from multistrat_paper import book_series # noqa: E402 def arg(flag, default, cast=float): return cast(sys.argv[sys.argv.index(flag) + 1]) if flag in sys.argv else default def yh(s): r = json.loads(urllib.request.urlopen(urllib.request.Request( f"https://query1.finance.yahoo.com/v8/finance/chart/{s}?interval=1d&range=25y", headers={"User-Agent": "Mozilla/5.0"}), timeout=30).read())["chart"]["result"][0] a = r["indicators"].get("adjclose", [{}])[0].get("adjclose") or r["indicators"]["quote"][0]["close"] ts = r["timestamp"] return {datetime.datetime.utcfromtimestamp(t).strftime("%Y-%m-%d"): float(c) for t, c in zip(ts, a) if c is not None} def stats(r): r = np.nan_to_num(r) eq = np.cumprod(1 + r); cagr = eq[-1] ** (252 / len(r)) - 1 vol = r.std() * math.sqrt(252) dd = float((eq / np.maximum.accumulate(eq) - 1).min()) rf = 0.03; sharpe = (r.mean() * 252 - rf) / vol if vol > 0 else 0 # excess-over-financing Sharpe return cagr, vol, sharpe, dd def main(): P0 = arg("--p0", 360000.0); PMT = arg("--pmt", 8000.0); FIN = arg("--fin", 0.03); YEARS = int(arg("--years", 20)) syms = ["SPY", "IEF", "GLD", "DBC"] data = {s: yh(s) for s in syms} dates = sorted(set.intersection(*[set(d) for d in data.values()])) T = len(dates) Pm = np.column_stack([np.array([data[s][d] for d in dates]) for s in syms]) R = np.zeros((T, 4)); R[1:] = Pm[1:] / Pm[:-1] - 1 spy, ief = R[:, 0], R[:, 1] # multi-strat book (proxy: SPY/IEF/GLD/DBC + DIY trend sleeve) lc = np.log(Pm); sg = np.zeros((T, 4)); sg[252:] = np.sign(lc[252:] - lc[:-252]) vol = np.full((T, 4), np.nan) for t in range(63, T): vol[t] = R[t - 63:t].std(0) iv = 1 / np.where(vol > 0, vol, np.nan); w = np.nan_to_num(sg * iv) g = np.abs(w).sum(1, keepdims=True); g[g == 0] = 1; w = w / g trend = np.zeros(T); trend[1:] = np.sum(w[:-1] * R[1:], axis=1) book, _, _ = book_series(np.column_stack([R, trend])) # SPY + adaptive overlay (vol-target 15% + 200d trend + drawdown de-lever) lp = np.log(Pm[:, 0]); ma = np.array([lp[max(0, t - 200):t].mean() if t >= 200 else lp[t] for t in range(T)]) ovr = np.zeros(T); eqo = pk = 1.0 for t in range(63, T - 1): rv = spy[t - 63:t].std() * math.sqrt(252); vs = min(1.0, 0.15 / (rv + 1e-9)) tr = 1.0 if lp[t] > ma[t] else 0.3 dd = eqo / pk - 1; dds = float(np.clip(1 + 3 * min(0, dd + 0.07), 0.4, 1.0)) e = float(np.clip(vs * tr * dds, 0, 1.0)); ovr[t + 1] = e * spy[t + 1]; eqo *= (1 + ovr[t + 1]); pk = max(pk, eqo) cands = { "SPY buy-hold (1.0x)": spy, "SPY 1.3x (cheap fin)": 1.3 * spy - 0.3 * FIN / 252, "60/40 (SPY/IEF)": 0.6 * spy + 0.4 * ief, "70/30 SPY+book": 0.7 * spy + 0.3 * book, "SPY+adaptive overlay": ovr, "adaptive multi-strat book": book, } def fv(cagr): rm = cagr / 12; N = YEARS * 12; gg = (1 + rm) ** N return P0 * gg + PMT * ((gg - 1) / rm) print(f"STRATEGY COMPARISON — {dates[0]}..{dates[-1]} | ${P0:,.0f} + ${PMT:,.0f}/mo, {YEARS}y, fin {100*FIN:.0f}%") print(f" {'strategy':>26} | CAGR | vol | Sharpe* | maxDD | {YEARS}y wealth") for nm, r in cands.items(): c, v, sh, dd = stats(r) print(f" {nm:>26} | {100*c:>4.1f}% | {100*v:>3.0f}% | {sh:>+5.2f} | {100*dd:>+5.0f}% | ${fv(c):>13,.0f}") print(" *Sharpe = excess over financing (the leverage-relevant one). More CAGR ALWAYS = deeper maxDD.") print(" Reference plan: docs/superpowers/specs/2026-06-08-cagr-reference-plan.md") if __name__ == "__main__": main()