feat(harvest): premium-harvesting tested — simple 60/40 beats the sophisticated system

The strategy class we never tried: harvest structural premia + risk system, not predict.
Diversified futures + risk-parity/vol-target/trend; clean 5-asset-class version (ES/ZN/GC/CL/BTC).
Result: plain 60/40 (Sharpe +0.72, 2010-2026) BEATS risk-parity+vol-target (+0.46) and RP+trend
(+0.33, trend hurts); 5-asset+crypto RP only ties 60/40 and loses to buy-hold equity. Meta-pattern
now complete in BOTH games: simple beats/equals sophisticated in prediction AND harvesting.
Constructive deliverable: a simple premium harvest (60/40 / risk-parity) IS a real deployable
robust strategy (~0.5-0.72 Sharpe, low DD, no prediction, minimal complexity). The engine's
sophistication was never the return-generator -- deployable path is light (harvest + risk overlay),
engine's value is infra/discipline/product. (Also: CAISO intraday gate blocked by OASIS plumbing.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-06-07 00:42:32 +02:00
parent 7457ef679c
commit 244ccaaf0b
4 changed files with 369 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""The strategy class we never tested: HARVEST structural premia with the risk system, not predict.
Diversified multi-asset futures (equity/rates/metals/energy/ags/FX). Build a ladder:
equal-weight -> inverse-vol (risk-parity) -> + vol-target -> + trend-overlay (long-flat by 252d trend,
the crisis-alpha de-risk). Measure RISK-ADJUSTED (Sharpe, ann-vol, max-DD, Calmar, per-year) vs
benchmarks (equal-weight, equity-only, 60/40). No prediction/alpha needed — just disciplined
harvesting + risk control (what the engine is actually good at). Roll-zeroed returns (understates
carry; fine for comparing the risk-management strategies).
"""
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
TARGET_VOL = 0.10 # 10% annualized portfolio vol
def metrics(r):
r = r[np.isfinite(r)]
if len(r) < 50 or r.std() == 0:
return dict(sr=float("nan"), ann=float("nan"), vol=float("nan"), dd=float("nan"), calmar=float("nan"))
ann = r.mean() * 252; vol = r.std() * math.sqrt(252); sr = ann / vol
eq = np.cumprod(1 + r); dd = float((eq / np.maximum.accumulate(eq) - 1).min())
return dict(sr=sr, ann=ann, vol=vol, dd=dd, calmar=ann / (abs(dd) + 1e-9))
def main():
roots, days, close, op, inst, weekday = load_panel()
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)
def port(weights):
w = np.nan_to_num(weights)
w = np.where(avail, w, 0.0)
g = np.sum(np.abs(w), axis=1, keepdims=True); g[g == 0] = 1
w = w / g # unit gross (fully invested)
return np.sum(w[:-1] * R[1:], axis=1)
def voltarget(r):
out = np.zeros_like(r)
for t in range(63, len(r)):
rv = np.std(r[t - 63:t]) * math.sqrt(252)
out[t] = r[t] * min(3.0, TARGET_VOL / (rv + 1e-9)) # scale to target vol, cap 3x leverage
return out
# weight schemes
eqw = np.where(avail, 1.0, 0.0)
rp = np.where(avail, iv, 0.0) # inverse-vol risk parity
trend = (np.full_like(lc, np.nan)); trend[252:] = lc[252:] - lc[:-252]
rp_trend = np.where(avail & (trend > 0), iv, 0.0) # trend-overlay: hold only uptrending assets
books = {
"equal-weight": port(eqw),
"risk-parity (inv-vol)": port(rp),
"risk-parity + vol-target": voltarget(port(rp)),
"RP + trend + vol-target": voltarget(port(rp_trend)),
}
# benchmarks
es = roots.index("ES") if "ES" in roots else 0
bench = {"equity-only (ES)": R[1:, es]}
if "ZN" in roots:
zn = roots.index("ZN")
w6040 = np.zeros((T, N)); w6040[:, es] = 0.6; w6040[:, zn] = 0.4
bench["60/40 (ES/ZN)"] = port(w6040)
print(f"\n===== PREMIUM HARVEST (diversified futures, {N} assets, {T} days, target {int(TARGET_VOL*100)}% vol) =====")
print(f"{'strategy':>26} {'Sharpe':>7} {'ann%':>6} {'vol%':>6} {'maxDD%':>7} {'Calmar':>7}")
for nm, r in {**bench, **books}.items():
m = metrics(r)
print(f"{nm:>26} {m['sr']:>+7.2f} {100*m['ann']:>+6.1f} {100*m['vol']:>5.1f} {100*m['dd']:>+7.1f} {m['calmar']:>7.2f}")
print("\nper-year Sharpe (RP + trend + vol-target):")
rtv = books["RP + trend + vol-target"]
print(" " + " ".join(f"{y}:{metrics(rtv[year[1:]==y])['sr']:+.1f}" for y in range(2010, 2027) if (year[1:] == y).sum() > 100))
print("\nVERDICT: if risk-managed harvest beats benchmarks on Sharpe/Calmar (esp lower maxDD) = the engine's")
print("real job (risk management of real premia) delivers, no alpha needed. Modest but robust = the honest win.")
if __name__ == "__main__":
main()