feat(surfer): Phase 0 GPU floor + validation harness — first real (weak) OOS trend edge
PyTorch-GPU diversified-trend floor (TSMOM 1/3/12mo + inverse-vol + 10% vol-target) + CPCV/Deflated-Sharpe validation, over 22 CME futures x 19.7y (Databento GLBX ohlcv-1d via budget-capped fetcher, ~$32 credits). Verdict: Sharpe +0.32, CPCV median +0.30, IS +0.36/OOS +0.08 (sign-consistent), Deflated Sharpe ~0.92 at honest n_trials. Real but weak edge; fails deploy-grade gates (correct), passes edge-exists. Includes roll-neutralization fix (max-vol outright + zero roll-day returns) that eliminated 988%/day continuous-contract artifacts (RB vol 550%->31%). Plus install_torch_gpu.sh. Data gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
73
scripts/surfer/fetch_daily.py
Normal file
73
scripts/surfer/fetch_daily.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Download daily OHLCV for the surfer universe — continuous front-month, BUDGET-CAPPED.
|
||||
|
||||
Re-confirms get_cost (free, no download) and ABORTS if over the cap before spending.
|
||||
Saves raw DBN to data/surfer/ (gitignored). Reads DATABENTO_API_KEY from env (never printed).
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import databento as db
|
||||
|
||||
CAP_USD = 40.00 # hard ceiling on cumulative get_cost; covered by $125 free credits ($0 cash)
|
||||
PER_ROOT_CAP = 10.00 # per-root sanity cap
|
||||
DS = "GLBX.MDP3"
|
||||
SCHEMA = "ohlcv-1d"
|
||||
START = "2010-06-06"
|
||||
END = "2026-06-05"
|
||||
ROOTS = ["ES", "NQ", "YM", "RTY", "ZN", "ZB", "ZF", "ZT", "6E", "6J", "6B", "6A",
|
||||
"6C", "GC", "SI", "HG", "CL", "NG", "RB", "ZC", "ZS", "ZW"]
|
||||
# parent symbology = all outright expiries per root (fast; continuous chain resolution 504s).
|
||||
# We build the continuous series ourselves (volume-roll + ratio-adjust) from these.
|
||||
STYPE = "parent"
|
||||
OUT_DIR = "data/surfer"
|
||||
|
||||
|
||||
def main():
|
||||
key = os.environ.get("DATABENTO_API_KEY")
|
||||
if not key:
|
||||
print("DATABENTO_API_KEY not set"); return 2
|
||||
client = db.Historical(key)
|
||||
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
print(f"fetching {SCHEMA} per-root ({len(ROOTS)} roots, {STYPE}) {START}..{END}; "
|
||||
f"per-root get_cost gate (≤${PER_ROOT_CAP}), cumulative cap ${CAP_USD}")
|
||||
import time
|
||||
total_recs = 0
|
||||
spent = 0.0
|
||||
failed = []
|
||||
for r in ROOTS:
|
||||
sym = r + ".FUT"
|
||||
out_r = f"{OUT_DIR}/{r}.dbn"
|
||||
# per-root cost gate (free, no download)
|
||||
try:
|
||||
c_r = client.metadata.get_cost(dataset=DS, symbols=[sym], schema=SCHEMA,
|
||||
start=START, end=END, stype_in=STYPE)
|
||||
except Exception as e:
|
||||
print(f" {r}: get_cost failed ({type(e).__name__}); skipping"); failed.append(r); continue
|
||||
if c_r > PER_ROOT_CAP or spent + c_r > CAP_USD:
|
||||
print(f" {r}: cost ${c_r:.4f} would breach cap (spent ${spent:.2f}) — SKIP"); failed.append(r); continue
|
||||
ok = False
|
||||
for attempt in range(1, 4):
|
||||
try:
|
||||
data = client.timeseries.get_range(dataset=DS, symbols=[sym], schema=SCHEMA,
|
||||
start=START, end=END, stype_in=STYPE)
|
||||
data.to_file(out_r)
|
||||
n = sum(1 for _ in data)
|
||||
total_recs += n; spent += c_r
|
||||
print(f" {r}: ${c_r:.4f} {n:,} recs -> {out_r} (cum ${spent:.2f})")
|
||||
ok = True
|
||||
break
|
||||
except Exception as e:
|
||||
print(f" {r}: attempt {attempt} {type(e).__name__}; retry in 3s")
|
||||
time.sleep(3)
|
||||
if not ok:
|
||||
failed.append(r)
|
||||
print(f"DONE: {total_recs:,} records, get_cost-cum ${spent:.2f}, "
|
||||
f"{len(ROOTS)-len(failed)}/{len(ROOTS)} roots ok"
|
||||
+ (f"; FAILED/SKIPPED: {failed}" if failed else ""))
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user