#!/usr/bin/env python3 """Execution-realism stress for point-in-time crypto momentum — is the magnitude believable? Layers on the realistic frictions that could inflate the paper Sharpe: - cost 10→20→30 bp on turnover (smaller coins cost more) - WINSORIZE per-coin daily returns to ±cap (you cannot fill a short into an 80%/day collapse) - EXCLUDE each coin's final K days (delisting death-spiral: untradeable → zero PnL there) - liquidity FLOOR: only trade coins with trailing dollar-vol > floor If mom_20/30 stays positive EVERY year under the combined stress, the edge magnitude is real. """ import math import os import sys import numpy as np sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import pit_sweep as ps # noqa: E402 from signal_sweep import xs_weights, pnl_w, validate, sharpe_t # noqa: E402 import torch # noqa: E402 DEV = "cuda" if torch.cuda.is_available() else "cpu" def trailing(lc, L): out = np.full_like(lc, np.nan); out[L:] = lc[L:] - lc[:-L]; return out def main(): syms, days, close, qv, fund = ps.load() T, N = close.shape lc = np.log(close) R = np.zeros((T, N)); R[1:] = lc[1:] - lc[:-1]; R = np.where(np.isfinite(R), R, 0.0) fund = np.where(np.isfinite(fund), fund, 0.0) qv = np.nan_to_num(qv) dv30 = np.zeros((T, N)) for t in range(30, T): dv30[t] = qv[t - 30:t].mean(axis=0) year = (1970 + days / 365.25).astype(int) # death-spiral mask: each coin's final 5 valid days = untradeable tradeable = np.ones((T, N), bool) for j in range(N): idx = np.where(np.isfinite(close[:, j]))[0] if len(idx): tradeable[max(0, idx[-1] - 4):idx[-1] + 1, j] = False def variant(cost_bp, winsor, excl_death, dv_floor, L=20, TOPK=50): Reff = (R - fund) if winsor is not None: Reff = np.clip(Reff, -winsor, winsor) if excl_death: Reff = np.where(tradeable, Reff, 0.0) univ = np.zeros((T, N), bool) for t in range(T): elig = np.where((dv30[t] > dv_floor) & np.isfinite(close[t]))[0] if len(elig) == 0: continue k = min(TOPK, len(elig)) univ[t, elig[np.argsort(-dv30[t, elig])[:k]]] = True sig = trailing(lc, L).copy(); sig[~univ] = np.nan pnl = pnl_w(xs_weights(sig), Reff, cost_bp=cost_bp) v = validate(pnl, days, 6) py = [sharpe_t(torch.tensor(pnl[year[1:] == y], device=DEV, dtype=torch.float64)) if (year[1:] == y).sum() > 30 else float("nan") for y in range(2020, 2027)] return v, py print(f"\n===== EXECUTION-REALISM STRESS — PIT momentum (mom_20, TOPK=50) — {N} coins =====") print(f"{'variant':>34} {'full':>6} {'IS':>6} {'OOS':>6} {'CPCVmed':>8} | per-year 2020..2026") cfgs = [ ("baseline 10bp", dict(cost_bp=10, winsor=None, excl_death=False, dv_floor=0)), ("20bp cost", dict(cost_bp=20, winsor=None, excl_death=False, dv_floor=0)), ("winsor ±20%", dict(cost_bp=10, winsor=0.20, excl_death=False, dv_floor=0)), ("excl death-spiral (last 5d)", dict(cost_bp=10, winsor=None, excl_death=True, dv_floor=0)), ("dv-floor $5M/day", dict(cost_bp=10, winsor=None, excl_death=False, dv_floor=5e6)), ("COMBINED 20bp+wins20+death+$5M", dict(cost_bp=20, winsor=0.20, excl_death=True, dv_floor=5e6)), ("HARSH 30bp+wins10+death+$10M", dict(cost_bp=30, winsor=0.10, excl_death=True, dv_floor=1e7)), ] for nm, cfg in cfgs: v, py = variant(**cfg) pys = " ".join(f"{p:>+5.2f}" if not math.isnan(p) else " n/a" for p in py) print(f"{nm:>34} {v['full']:>+6.2f} {v['is_']:>+6.2f} {v['oos']:>+6.2f} {v['med']:>+8.2f} | {pys}") print("\nVERDICT: positive every year under COMBINED/HARSH => believable, sizable edge (not paper).") if __name__ == "__main__": main()