Files
PythagorasGoal/scripts/research/alt/wf_altstrat.js
T
Adriano Dal Pastro 5ac4e16af8 research(alt): sweep 104 strategie alternative su Deribit (153 agenti) + marginal scorer
Ondata di ricerca onesta a largo spettro su BTC/ETH+DVOL certificati: 104 ipotesi
distinte (11 famiglie), un agente-finder per ipotesi, verifica avversariale a 3
scettici sui promettenti, sintesi (153 agenti totali). Esito: NIENTE di nuovo regge
-> conferma del soffitto strutturale ~1.3 BTC/ETH-direzionale; lo stack
TP01+XS01+VRP01 resta imbattuto.

- altlib.py: harness condiviso vettoriale leak-free (eval_weights/study_weights,
  fee-sweep, both-asset + hold-out 2025+). Riproduce i numeri canonici di TP01.
- MARGINAL SCORER (study_marginal/marginal_vs_tp01): Sharpe INCREMENTALE vs baseline
  TP01 (corr, blend uplift OOS, alpha residua) + jackknife OOS (clean-year +
  drop-best-month). earns_slot = abs!=FAIL & ADDS & robust_oos. Smaschera gli overlay
  su TSMOM con PASS assoluti fasulli (CMB04, VOL11, ...) e il falso positivo KAMA
  (ADDS ma muore al jackknife).
- runs/*.py (104) script riproducibili per ipotesi; wf_altstrat.js workflow.
- Verdetto: 0 candidati deployabili; 2 LEAD fragili (VOL08, STA05_LS) da forward-monitor.
- test_marginal_scorer.py blocca baseline + invarianti. Suite: 32 verde.

Diario: docs/diary/2026-06-20-alt-strategies-100agent-sweep.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:50:39 +00:00

327 lines
32 KiB
JavaScript

export const meta = {
name: 'alt-strategies-deribit',
description: 'Study >=100 alternative trading strategies on certified Deribit BTC/ETH (+DVOL): honest backtest each, adversarially verify the promising, synthesize',
phases: [
{ title: 'Find', detail: 'one agent per hypothesis: implement + honest backtest via shared altlib' },
{ title: 'Verify', detail: '3 adversarial skeptics per promising finding' },
{ title: 'Synthesize', detail: 'rank survivors, recommend portfolio roles' },
],
}
// ---------------------------------------------------------------------------
// CATALOG — 104 distinct hypotheses. fam=family, kind hint (w=weights, s=signals)
// ---------------------------------------------------------------------------
const CATALOG = [
// --- BRK: breakout / channel ---
['BRK01','BRK','Donchian 10/20/55 long-short','w','Donchian channel breakout: close breaks prior N-bar high -> long, prior N-bar low -> short (use al.donchian which shifts by 1). Grid N in {10,20,55}. Long-short and long-flat variants.'],
['BRK02','BRK','Donchian55 + Chandelier ATR trailing','w','Enter long on 55-bar high breakout; trail exit with chandelier stop = highest close - 3*ATR(22). Position 0/1.'],
['BRK03','BRK','Keltner channel breakout','w','Long when close > EMA20 + 2*ATR(20); flat when close < EMA20. Try k in {1.5,2,2.5}.'],
['BRK04','BRK','Bollinger breakout (vol expansion)','w','Long-flat when close > upper BB(20,2); exit to flat when close < mid BB. Momentum (NOT reversion) interpretation.'],
['BRK05','BRK','ATR range breakout','s','Discrete: if close[i] > close[i-1] + k*ATR(14) enter long at close[i] with ATR-based SL and max_bars exit. Grid k in {0.5,1,1.5}.'],
['BRK06','BRK','Opening-range breakout (daily)','s','On 1d bars: long when close > prior-day high (gap/expansion); SL prior-day low; max_bars small. Honest, no intrabar extreme entry.'],
['BRK07','BRK','N-day-high momentum','w','Long-flat: position 1 while close is within X% of its rolling 100-bar max, else 0. Trend-persistence proxy.'],
['BRK08','BRK','NR7 range-contraction breakout','s','Bar with narrowest high-low range in last 7 -> next-bar breakout of that bar in the break direction; entry at close on confirmation.'],
['BRK09','BRK','Inside-bar breakout','s','Inside bar (range within prior bar) then breakout of the mother-bar high/low -> enter at close of the breaking bar.'],
['BRK10','BRK','Vol-contraction (squeeze) long','w','When Bollinger bandwidth in its low expanding-percentile, go long-flat on subsequent upside close>mid. Honest entry at close.'],
// --- TRD: trend (non-TSMOM) ---
['TRD01','TRD','EMA cross 20/100 long-flat','w','Long when EMA(fast)>EMA(slow), else flat. Grid (fast,slow) in {(10,50),(20,100),(50,200)}.'],
['TRD02','TRD','EMA cross long-short','w','Same but short when fast<slow. Compare to long-flat.'],
['TRD03','TRD','MACD trend','w','Long when MACD(12,26)>signal(9) AND MACD>0; flat otherwise. Optionally vol-target the direction.'],
['TRD04','TRD','Supertrend(10,3)','w','Classic ATR-band trend flip: long when price above supertrend line, short/flat below. Try (period,mult).'],
['TRD05','TRD','ADX-filtered EMA','w','Take EMA(20,100) cross only when ADX(14)>25 (trending); flat when ADX low (chop filter).'],
['TRD06','TRD','Heikin-Ashi trend streak','w','Build HA candles; long while HA close>HA open (green streak), flat on color flip.'],
['TRD07','TRD','Kaufman AMA cross','w','Adaptive MA (efficiency-ratio smoothing); long when price>AMA and AMA rising.'],
['TRD08','TRD','Hull MA slope','w','HMA(n); long when HMA rising (slope>0), flat when falling. Grid n in {20,50,100}.'],
['TRD09','TRD','Aroon trend','w','Aroon(25): long when AroonUp>AroonDown and AroonUp>70.'],
['TRD10','TRD','Vortex indicator','w','VI+ vs VI- (n=14): long when VI+>VI-. Vol-target optionally.'],
['TRD11','TRD','SMA50 slope momentum','w','Position = sign of slope of SMA(50) over last k bars; long-flat variant too.'],
['TRD12','TRD','Triple-MA alignment','w','Long only when SMA10>SMA50>SMA200 (full bullish alignment); flat otherwise.'],
['TRD13','TRD','SMA200 regime + vol-target','w','Long-flat gated by close>SMA200, sized by al.vol_target. Pure regime-trend.'],
['TRD14','TRD','Turtle midline trend','w','Long above Donchian(20) midline, exit at Donchian(10) opposite. Trend-rider.'],
// --- MRV: mean-reversion (regime-gated to avoid the fade death) ---
['MRV01','MRV','RSI2 Connors','s','Buy when RSI(2)<10 AND close>SMA200 (uptrend filter); exit when RSI(2)>60 or max_bars. Long-only.'],
['MRV02','MRV','BB reversion in calm regime','s','Buy lower BB(20,2) ONLY when realized vol in low expanding-percentile (calm); exit at mid. Gate is the alpha.'],
['MRV03','MRV','Z-score reversion trend-gated','s','Fade |zscore(close,20)|>2 toward mean ONLY when long-horizon trend (SMA200 slope) is flat; skip in strong trend.'],
['MRV04','MRV','IBS reversion','w','Internal Bar Strength = (close-low)/(high-low). Long when IBS<0.2, flat/short when >0.8. Classic daily edge — test honestly.'],
['MRV05','MRV','Williams %R reversion','s','Buy when %R(14) < -90 (oversold) with trend filter; exit on %R>-50.'],
['MRV06','MRV','VWAP deviation reversion','w','On 1h: rolling session VWAP; fade deviations > k*sigma back to VWAP. Regime-gate by daily trend.'],
['MRV07','MRV','Consecutive-down buy','s','After 3+ consecutive lower closes in an uptrend (close>SMA100), buy at close; exit after k bars or on green close.'],
['MRV08','MRV','Daily gap-fill','s','On 1d: if open gaps down vs prior close beyond threshold, long toward prior close (gap fill); SL beyond gap.'],
['MRV09','MRV','CCI reversion','s','Buy CCI(20)<-100, exit CCI>0, with trend gate. Honest entry at close.'],
['MRV10','MRV','Stochastic reversion in range','s','Stochastic(14,3) oversold buy when ADX low (range regime only). Avoid trending chop.'],
['MRV11','MRV','Bollinger %b reversion','w','%b = position within bands; long when %b<0.05, exit at 0.5, with SMA200 trend filter.'],
// --- VOL: DVOL + realized vol (Deribit-specific) ---
['VOL01','VOL','DVOL z-score risk on/off','w','Use al.dvol. Long BTC/ETH (vol-targeted) when DVOL z-score (expanding) < 0 (calm), flat when DVOL high. History 2021+.'],
['VOL02','VOL','IV-RV spread directional','w','Compare DVOL to annualized realized vol. When DVOL >> RV (rich/stressed), de-risk to flat; when DVOL<=RV stay long. Test both directions.'],
['VOL03','VOL','DVOL-gated TSMOM','w','TP01-style multi-horizon TSMOM (vol-targeted, long-flat) but ONLY active when DVOL below its expanding median; flat when DVOL elevated.'],
['VOL04','VOL','DVOL momentum de-risk','w','When DVOL rising over last k days, cut exposure; when falling, add. Overlay on long-flat trend.'],
['VOL05','VOL','Vol-of-vol contrarian','w','std of daily DVOL changes spikes (panic) -> contrarian long after stabilization. History 2021+.'],
['VOL06','VOL','Realized-vol target standalone','w','No trend signal: long-only position = target_vol/realized_vol, capped. Pure inverse-vol risk control — is risk-scaling alone an edge?'],
['VOL07','VOL','DVOL spike contrarian long','s','When DVOL > 90th expanding pct (fear), buy at close, hold ~1 week (max_bars). Capitulation timing.'],
['VOL08','VOL','Realized-vol term structure','w','Ratio short-window vol (5d) / long-window vol (30d). >1 (vol rising) de-risk; <1 risk-on. Overlay on long.'],
['VOL09','VOL','EWMA vol-forecast sizing','w','RiskMetrics EWMA (lambda 0.94) vol forecast -> target-vol long-only sizing. Compare to simple rolling vol target.'],
['VOL10','VOL','DVOL carry/recovery','w','When DVOL high AND falling (post-stress), go long (mean-reversion of fear). Gate long-flat trend by this.'],
['VOL11','VOL','DVOL kill-switch on trend','w','Long-flat TSMOM with a hard flat when DVOL>fixed/percentile threshold (crash avoidance overlay).'],
['VOL12','VOL','Low-vol anomaly timing','w','Enter long after a cluster of low realized-vol bars (compression often precedes up-moves in BTC). Honest entry at close.'],
// --- XAS: cross-asset BTC/ETH ---
['XAS01','XAS','ETH/BTC ratio z-reversion','w','Build ratio = ETH_close/BTC_close on common timestamps. Trade the ratio (long ratio when z<-2, short when z>2). NOTE: this is a 2-leg spread; implement the ratio series yourself from al.get both assets, evaluate the SPREAD return as a synthetic series via al.eval_weights on a constructed df (close=ratio).'],
['XAS02','XAS','ETH/BTC ratio momentum','w','Trend-follow the ETH/BTC ratio (TSMOM on the ratio). Same spread-construction approach.'],
['XAS03','XAS','RS rotation BTC/ETH','w','Hold whichever of BTC/ETH has the stronger 90d momentum, vol-targeted; flat if both negative. Report the rotation portfolio return.'],
['XAS04','XAS','Lead-lag BTC->ETH','w','ETH position = sign of BTC lagged return / trend (does BTC lead ETH?). Evaluate on ETH series with BTC-derived signal (align timestamps).'],
['XAS05','XAS','Lead-lag ETH->BTC','w','BTC position from ETH lagged momentum. Mirror of XAS04.'],
['XAS06','XAS','Beta-hedged spread reversion','w','Residual = ETH_ret - beta*BTC_ret (rolling beta). Trade z of cumulative residual back to 0. Market-neutral.'],
['XAS07','XAS','Rolling-OLS cointegration spread','w','Rolling/expanding OLS hedge ratio of log(ETH) on log(BTC); trade z-score of residual (Engle-Granger style). Causal hedge ratio only.'],
['XAS08','XAS','Correlation-regime spread','w','When rolling BTC/ETH correlation drops below threshold, mean-revert the ratio; when high, stand aside.'],
['XAS09','XAS','Dual-momentum BTC/ETH','w','Absolute+relative momentum: hold the stronger asset only if its abs momentum>0 else flat. Vol-targeted.'],
// --- SEA: seasonality / time-of-day (24/7 crypto) ---
['SEA01','SEA','Hour-of-day expectancy','w','On 1h: estimate per-UTC-hour mean return on an EXPANDING in-sample window; go long during hours with positive expanding expectancy. Strictly causal (no full-sample hour ranking).'],
['SEA02','SEA','Day-of-week effect','w','On 1d: expanding per-weekday expectancy -> long on positive-expectancy weekdays. Causal only.'],
['SEA03','SEA','Weekend effect','w','Long/flat over weekend bars vs weekday, chosen by expanding in-sample sign. Test both.'],
['SEA04','SEA','Turn-of-month','s','On 1d: long the last 1-2 and first 2-3 trading days of each month; flat otherwise.'],
['SEA05','SEA','Intraday momentum','w','On 1h: sign of the morning (00-12 UTC) cumulative return predicts afternoon (12-24) -> position in afternoon. Causal.'],
['SEA06','SEA','Overnight vs intraday','w','Split daily return into US-overnight vs Asia sessions; capture the historically positive session (expanding choice).'],
['SEA07','SEA','Monday effect','s','On 1d: position on Mondays based on expanding Monday expectancy (continuation or reversal of Friday).'],
['SEA08','SEA','US-session momentum','w','On 1h: long during 13-21 UTC when prior session up; captures US risk-on drift. Causal.'],
['SEA09','SEA','Asia-session reversion','w','On 1h: fade extreme moves during 00-08 UTC back toward open. Session mean-reversion.'],
// --- RSK: risk overlays / defensive (TP01-adjacent but distinct mechanism) ---
['RSK01','RSK','Vol-target B&H + DD breaker','w','Long-only vol-targeted (no trend) with a circuit breaker: go flat when strategy equity drawdown>15%, re-enter when recovered. Does the breaker beat passive?'],
['RSK02','RSK','Momentum + fast kill-switch','w','TSMOM long-flat with an extra flat trigger on a sharp short-horizon drawdown (e.g. -10% in 5 bars).'],
['RSK03','RSK','Inverse-vol risk parity','w','Single-asset proxy: scale exposure by inverse realized vol with a long-run cap; compare to fixed exposure. (For 2-asset RP, blend BTC/ETH inverse-vol and report combined.)'],
['RSK04','RSK','Momentum-of-momentum sizing','w','Size the TSMOM position by the STABILITY/agreement of the multi-horizon signals (more agreement = bigger). Long-flat.'],
['RSK05','RSK','Chandelier-exit trend','w','Long on trend (EMA cross or breakout); exit via chandelier ATR stop; flat otherwise.'],
['RSK06','RSK','Time-stop momentum','s','Enter long on a breakout, HARD exit after exactly N bars regardless (no trailing). Tests whether momentum has a fixed horizon.'],
['RSK07','RSK','Drawdown-scaled exposure','w','Exposure proportional to (1 - recent rolling drawdown) on a long-only base; de-risk into weakness.'],
['RSK08','RSK','ATR trailing-stop trend','s','Breakout entry with ATR(14)*k trailing stop as SL; max_bars large. Grid k in {2,3,4}.'],
['RSK09','RSK','Target-vol + floor/cap + trend gate','w','Long-flat TSMOM, vol-targeted but with exposure floor 0.2 and cap 1.5 only when trend up. Smoothness vs raw vol-target.'],
// --- OPT: options structures (premiums MODELED on DVOL -> LEAD ONLY, must flag) ---
['OPT01','OPT','Covered-call overlay','w','Long spot + sell weekly OTM call. Model call premium with Black-Scholes using al.dvol as IV. Net = spot return capped + premium. MARK caveat: modeled, lead-only.'],
['OPT02','OPT','Cash-secured put wheel','w','Sell weekly ~0.25-delta put (BS premium from DVOL); if assigned, hold spot then sell calls. Model assignment via close vs strike. Caveat: modeled.'],
['OPT03','OPT','Calendar spread','w','Sell short-dated, buy longer-dated option; P&L driven by DVOL term proxy (use DVOL level changes). Caveat: modeled, no real term surface.'],
['OPT04','OPT','Iron condor weekly','w','Sell OTM call+put spreads weekly, premium from DVOL, gated on IV-rank>0.3. Defined risk. Caveat: modeled.'],
['OPT05','OPT','Delta-hedged short straddle','w','Sell ATM straddle, daily delta re-hedge; P&L = premium (DVOL) - realized variance. Harvest IV-RV. Caveat: modeled, the realized-stress f is uncertain.'],
['OPT06','OPT','Ratio put spread','w','Defensive short-vol with a long tail hedge; model on DVOL. Caveat: modeled.'],
['OPT07','OPT','Collar overlay','w','Long spot + protective put - covered call (zero-ish cost). Reduces DD; model legs on DVOL. Does it improve risk-adjusted return?'],
['OPT08','OPT','Risk-reversal directional','w','Skew-based directional: model a 25-delta risk reversal sign as a trend tilt (proxy skew from DVOL changes). Caveat: skew not in data, heavy proxy.'],
// --- MIC: microstructure / candle patterns (honest entry, no extreme fills) ---
['MIC01','MIC','Three-bar momentum','s','3 consecutive higher closes -> enter long at the 3rd close, exit after k bars or on a lower close. Continuation test.'],
['MIC02','MIC','Engulfing continuation','s','Bullish engulfing in an uptrend -> long at close; bearish in downtrend -> short. Trend-filtered.'],
['MIC03','MIC','Volume-spike breakout','s','Breakout of prior high CONFIRMED by volume z-score>2 -> enter at close. Volume as filter.'],
['MIC04','MIC','Consecutive-days continuation','w','Position = sign of net of last k closes (streak following) vs streak fading; long-flat. Compare both.'],
['MIC05','MIC','Wide-range-bar follow-through','s','After a wide-range bar (range>2*ATR) closing strong, enter in its direction at close; exit k bars.'],
['MIC06','MIC','Body-ratio momentum','w','Candle body/(range) large and positive -> conviction up bar -> hold long next bars. Long-flat.'],
['MIC07','MIC','Pin-bar rejection reversal','s','Long lower-wick rejection (hammer) at a support (recent low) -> long at close with SL below wick.'],
['MIC08','MIC','OBV trend','w','On-balance-volume trend: long when OBV above its EMA (volume confirms price). Long-flat.'],
// --- STA: statistical / ML (walk-forward, leakage-careful) ---
['STA01','STA','Ridge on lagged returns','w','Walk-forward expanding ridge predicting next-bar return sign from lagged returns (lags 1..10, stop at i-1 to avoid the log-return leak). Position = sign of prediction, vol-targeted. 1d only.'],
['STA02','STA','Logistic on TA features','w','Walk-forward logistic (refit periodically) on features {rsi,zscore,mom,vol} all causal -> P(up); long if P>0.5. 1d, leakage-careful.'],
['STA03','STA','Random forest direction','w','Small RF (few trees, shallow), walk-forward retrain, careful causal features stopping at i-1. Long-flat by predicted prob. 1d.'],
['STA04','STA','K-means regime -> trend','w','Cluster causal (vol, return, range) features expanding; enable TSMOM only in the historically-bullish/trending cluster. No future labels.'],
['STA05','STA','EWMA-cross ensemble vote','w','Vote across many EMA crossovers (pairs from {5..200}); position = net vote / count. Diversified trend.'],
['STA06','STA','Kalman trend (local slope)','w','Kalman local-level+slope filter on log price; long when filtered slope>0. Causal recursion.'],
['STA07','STA','Online SGD logistic','w','Online logistic (partial_fit) updated each bar on causal features; predict next sign. 1d.'],
['STA08','STA','AR(1) residual reversion','w','Fit expanding AR(1) on returns; trade the residual mean-reversion. Causal coefficients only.'],
// --- CMB: combinations / multi-filter ---
['CMB01','CMB','Trend + RSI pullback','s','Uptrend (close>SMA200) + RSI(14) dips below 35 -> buy the dip at close; exit RSI>55 or k bars. Buy-the-dip-in-uptrend.'],
['CMB02','CMB','Breakout + volume + DVOL filter','w','Donchian breakout long-flat, taken only when volume elevated AND DVOL not in panic zone. Triple filter.'],
['CMB03','CMB','Multi-TF trend confirm','w','On a faster TF (4h), long only when the 1d trend (SMA50 or TSMOM) agrees. Construct daily trend causally and map onto 4h bars (use last CLOSED daily value).'],
['CMB04','CMB','Momentum + low-vol filter','w','TSMOM long-flat taken only when realized vol below its median (avoid high-vol whipsaw). Vol-target the rest.'],
['CMB05','CMB','BB squeeze -> breakout','s','Bollinger bandwidth at multi-bar low (squeeze) THEN breakout close>upper -> enter long at that close (honest, not the prior bar). The classic squeeze, done leak-free.'],
['CMB06','CMB','Trend + seasonality combo','w','TSMOM long-flat, but scale exposure up in historically strong calendar windows (expanding day-of-week/month expectancy). Causal seasonality only.'],
]
const ROOT = '/opt/docker/PythagorasGoal'
const CHEAT = `SHARED LIB (already built & validated): ${ROOT}/scripts/research/alt/altlib.py
At the top of your script: import sys; sys.path.insert(0, "${ROOT}/scripts/research/alt"); import altlib as al
DATA (certified Deribit, cached): al.get("BTC"|"ETH", tf) -> df[timestamp,open,high,low,close,volume,datetime]
tf in {"1h","4h","6h","8h","12h","1d","2d","1w"}. DO NOT use 5m/15m (slow on 2 CPUs / fee death).
DVOL (Deribit implied-vol index, DAILY, history from 2021-03): al.dvol(df,"BTC") -> causal float array len(df) in vol points.
INDICATORS (all causal, value at i uses data<=i): al.ema/sma/rsi/atr/zscore/rolling_std/donchian/bbands/realized_vol/simple_returns/log_returns
al.bars_per_day(df), al.bars_per_year(df), al.vol_target(direction_in[-1,1], df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0)
EVAL (the no-look-ahead shift is done FOR YOU; never multiply a weight that used close[i] by r[i] yourself):
CONTINUOUS position -> al.study_weights("NAME", lambda df: target_array(df), tfs=("1d","12h"))
target[i] decided with data<=close[i]; lib holds it during bar i+1; fee on |turnover|.
DISCRETE entry/exit -> al.study_signals("NAME", lambda df: entries_list(df), tfs=("1d",))
entries[i] = {"dir":+1/-1,"tp":px|None,"sl":px|None,"max_bars":int} or None; fills at close[i]. 1d only (Python loop).
Each returns {name,kind,cells:[{tf,per_asset:{BTC,ETH:{full:{sharpe,maxdd,ret,cagr},holdout:{sharpe,ret},fee_sweep,yearly}},
min_asset_full_sharpe,min_asset_holdout_sharpe,fee_survives}], verdict:{grade(PASS/WEAK/FAIL),best_tf,...}}
PRINT BOTH: print(al.fmt(rep)) and print("JSON:", al.as_json(rep))
HONESTY RULES (this project was once destroyed by fake edges): decide direction & price with data<=close[i];
NEVER enter on a candle extreme (high/low); judge net of 0.10% RT fee; require positive HOLD-OUT (2025+) on BOTH
assets and survival of the fee sweep; a single lucky cell/asset is NOT an edge. Report the truth, especially negatives.`
function finderPrompt([id, fam, name, kind, idea]) {
const kh = kind === 's' ? 'al.study_signals (discrete entry/exit, 1d only)' : 'al.study_weights (continuous position)'
return `You are studying ONE alternative trading-strategy hypothesis on certified Deribit BTC/ETH for the PythagorasGoal research project. Implement it HONESTLY using the shared library, backtest it, and report STRUCTURED results.
HYPOTHESIS ${id} [${fam}] — ${name}
IDEA: ${idea}
Suggested style: ${kh} (override if the idea clearly needs the other style).
CHEATSHEET
${CHEAT}
STEPS
1. Write a script at ${ROOT}/scripts/research/alt/runs/${id}.py that imports altlib as al, implements this idea CAUSALLY, optionally tries a SMALL internal grid (<=4 param sets; keep TOTAL backtests <=6 — only 2 CPUs), picks the best config, and prints al.fmt(rep) + "JSON:"+al.as_json(rep).
2. Run it: cd ${ROOT} && uv run python scripts/research/alt/runs/${id}.py (timeout generously; if a run hangs, reduce TFs/grid).
3. If it errors, FIX the script (common: NaN handling, alignment for cross-asset/DVOL ideas, shape mismatch). Re-run until it produces numbers OR you conclude it cannot be implemented on this data (say so).
4. Fill the schema HONESTLY from your BEST config. grade = the lib verdict for that config. Set promising=true ONLY if grade is PASS or WEAK AND hold-out Sharpe is positive on BOTH assets AND the fee sweep survives. Negatives are valuable — report them plainly.
CONSTRAINTS: tf from {1d,12h,8h,4h,1h}; signals use 1d. DVOL ideas: history starts 2021 (note it). Options ideas: premiums are MODELED on DVOL -> set caveat "modeled, lead-only". Do NOT fabricate numbers — every number must come from a real run you executed.
Your final message IS the data row (the schema), not prose for a human.`
}
const FIND_SCHEMA = {
type: 'object',
required: ['id','name','family','kind','implemented','grade','best_tf','btc_full_sharpe','eth_full_sharpe','btc_holdout_sharpe','eth_holdout_sharpe','worst_maxdd','fee_survives','promising','summary'],
properties: {
id: { type: 'string' }, name: { type: 'string' }, family: { type: 'string' },
kind: { type: 'string', enum: ['weights','signals'] },
implemented: { type: 'boolean', description: 'did the backtest actually run and produce numbers' },
grade: { type: 'string', enum: ['PASS','WEAK','FAIL','ERROR'] },
best_tf: { type: 'string' },
btc_full_sharpe: { type: 'number' }, eth_full_sharpe: { type: 'number' },
btc_holdout_sharpe: { type: 'number' }, eth_holdout_sharpe: { type: 'number' },
worst_maxdd: { type: 'number', description: 'max drawdown fraction (worse of BTC/ETH) in best config' },
fee_survives: { type: 'boolean', description: 'still positive at 0.20% RT fee' },
turnover_per_year: { type: 'number' },
promising: { type: 'boolean' },
summary: { type: 'string', description: '1-3 sentences: what was tested + the honest conclusion' },
caveats: { type: 'string' },
script_path: { type: 'string' },
},
}
function verifyPrompt(spec, find, k) {
const [id, fam, name] = spec
const angles = [
'LOOK-AHEAD & ENTRY REALISM: re-read the run script. Does any signal use close[i] (or high/low of i) to both decide AND fill on bar i? Any future-peeking in rolling/zscore/regime/walk-forward (e.g. fitting on full sample, ranking hours/days on the whole history, log-return r[k]=log(c[k+1]/c[k]) used as a feature)? Re-run with the leak removed if you suspect one. Verdict real=false if the edge depends on a leak.',
'ROBUSTNESS & OVERFIT: is this one lucky cell? Re-run neighboring params and a different TF, and check BOTH assets + the fee sweep to 0.20% RT + the per-year table (is it one good year carrying it?). A real edge is a plateau, positive on both assets, and not reliant on a single year. Default real=false if it is fragile.',
'PLAUSIBILITY & ECONOMICS: does the hold-out (2025+) actually hold up, or is FULL Sharpe propped by pre-2021 regime? Is turnover sane (not thousands/yr)? For options/DVOL ideas, is the result an artifact of the MODELED premium rather than a real edge? Is the mechanism economically distinct from TP01 (TSMOM trend) — or just TP01 in disguise? Default real=false if it is redundant with TP01 or an artifact.',
]
return `You are an ADVERSARIAL SKEPTIC (#${k + 1}) for the PythagorasGoal project. A finder agent claims hypothesis ${id} [${fam}] "${name}" is promising. Your job is to REFUTE it. Assume it is a false positive until proven otherwise — this project was once wrecked by fake edges, so the bar is high.
FINDER'S CLAIM:
${JSON.stringify(find)}
The run script is at: ${find.script_path || ROOT + '/scripts/research/alt/runs/' + id + '.py'}
The shared (trusted, leak-free) eval lib is at ${ROOT}/scripts/research/alt/altlib.py — read it to confirm how eval works.
YOUR ANGLE: ${angles[k % 3]}
Read the script, run your own checks (cd ${ROOT} && uv run python ...), and decide. If you find a leak or fragility, quantify the CORRECTED Sharpe (full & hold-out, both assets). Be specific and quote the numbers you produced. Default to real=false when uncertain.
Return ONLY the schema.`
}
const VERIFY_SCHEMA = {
type: 'object',
required: ['id','real','confidence','reason'],
properties: {
id: { type: 'string' },
real: { type: 'boolean', description: 'true only if the edge survives your adversarial check' },
confidence: { type: 'number', description: '0..1' },
leak_suspected: { type: 'boolean' },
single_cell_luck: { type: 'boolean' },
redundant_with_tp01: { type: 'boolean' },
corrected_full_sharpe: { type: 'number' },
corrected_holdout_sharpe: { type: 'number' },
reason: { type: 'string', description: 'specific, with the numbers you produced' },
},
}
// ===========================================================================
// RUN
// ===========================================================================
phase('Find')
log(`Studying ${CATALOG.length} alternative-strategy hypotheses on certified Deribit BTC/ETH (+DVOL), one agent each.`)
const results = await pipeline(
CATALOG,
(spec) => agent(finderPrompt(spec), {
label: `find:${spec[0]}`, phase: 'Find', schema: FIND_SCHEMA,
model: 'sonnet', effort: 'medium',
}),
(find, spec) => {
if (!find) return { id: spec[0], name: spec[2], family: spec[1], grade: 'ERROR', promising: false, verify: [] }
const worthVerifying = find.promising && (find.grade === 'PASS' || find.grade === 'WEAK')
if (!worthVerifying) return { ...find, verify: [] }
return parallel([0, 1, 2].map((k) => () =>
agent(verifyPrompt(spec, find, k), {
label: `verify:${spec[0]}.${k}`, phase: 'Verify', schema: VERIFY_SCHEMA, effort: 'high',
})
)).then((votes) => ({ ...find, verify: votes.filter(Boolean) }))
}
)
phase('Synthesize')
const clean = results.filter(Boolean)
// survivors: promising AND a majority of skeptics could not refute it
const enriched = clean.map((r) => {
const v = r.verify || []
const realVotes = v.filter((x) => x && x.real).length
const survived = r.promising && v.length >= 2 && realVotes >= Math.ceil(v.length / 2)
return { ...r, real_votes: realVotes, n_verify: v.length, survived }
})
const survivors = enriched.filter((r) => r.survived)
const promisingButKilled = enriched.filter((r) => r.promising && !r.survived)
log(`Find done: ${clean.length} studied. Promising: ${enriched.filter(r => r.promising).length}. Survived adversarial verify: ${survivors.length}.`)
// compact table for the synthesizer (drop nothing essential, keep it small)
const compact = enriched.map((r) => ({
id: r.id, name: r.name, family: r.family, grade: r.grade,
btc_full: r.btc_full_sharpe, eth_full: r.eth_full_sharpe,
btc_hold: r.btc_holdout_sharpe, eth_hold: r.eth_holdout_sharpe,
worst_dd: r.worst_maxdd, fee_ok: r.fee_survives, best_tf: r.best_tf,
promising: r.promising, survived: r.survived, real_votes: r.real_votes, n_verify: r.n_verify,
summary: r.summary, caveats: r.caveats,
verify_reasons: (r.verify || []).map((x) => (x ? `[real=${x.real} conf=${x.confidence}] ${x.reason}` : '')).filter(Boolean),
}))
const SYNTH_SCHEMA = {
type: 'object',
required: ['headline', 'survivors', 'ranking', 'recommendations', 'dead_families'],
properties: {
headline: { type: 'string', description: '2-4 sentences: did anything new and robust emerge, and how does it compare to the existing TP01/XS01/VRP01 stack?' },
survivors: {
type: 'array',
items: {
type: 'object',
required: ['id', 'name', 'why', 'suggested_role'],
properties: {
id: { type: 'string' }, name: { type: 'string' },
why: { type: 'string', description: 'what makes it survive: hold-out, both-asset, fee, distinct mechanism' },
suggested_role: { type: 'string', description: 'e.g. new portfolio sleeve / diversifier / overlay / needs more work / lead only' },
correlation_note: { type: 'string', description: 'is it distinct from TP01 trend?' },
},
},
},
ranking: { type: 'array', items: { type: 'string' }, description: 'all promising findings ranked best->worst, "ID name — one-line verdict"' },
recommendations: { type: 'string', description: 'concrete next steps: what to deep-validate, what to add to the portfolio, what to drop' },
dead_families: { type: 'array', items: { type: 'string' }, description: 'families/ideas confirmed dead or redundant, with one-line why (honest negatives)' },
},
}
const synthPrompt = `You are the SYNTHESIZER for a PythagorasGoal research wave that studied ${CATALOG.length} alternative trading strategies on certified Deribit BTC/ETH (+DVOL), then adversarially verified every promising one with 3 skeptics.
The project's EXISTING validated stack (do not re-derive): TP01 (multi-horizon TSMOM, vol-targeted, long-flat, defensive, FULL Sharpe ~1.30 / hold-out ~0.31, DD ~14%); XS01 (cross-sectional momentum on Hyperliquid alts, diversifier, corr ~-0.12 to TP01); VRP01 (modeled options short-vol, lead-only). The structural ceiling for BTC/ETH-DIRECTIONAL Sharpe is ~1.3 — beating it requires a genuinely DIFFERENT mechanism (that is why XS01/VRP01 were added). Honesty is the prime directive: a finding only counts if it is net-of-fee, robust across params + BOTH assets, positive in the 2025+ hold-out, and economically distinct from TP01.
Here is the full result table (each row = one hypothesis; verify_reasons are the skeptics' findings):
${JSON.stringify(compact)}
Survivors that passed adversarial verification: ${JSON.stringify(survivors.map((s) => ({ id: s.id, name: s.name, btc_full: s.btc_full_sharpe, eth_full: s.eth_full_sharpe, btc_hold: s.btc_holdout_sharpe, eth_hold: s.eth_holdout_sharpe, dd: s.worst_maxdd, real_votes: s.real_votes })))}
Promising-but-killed-by-skeptics: ${JSON.stringify(promisingButKilled.map((s) => ({ id: s.id, name: s.name, why_killed: (s.verify || []).map((v) => v && v.reason).filter(Boolean) })))}
Produce the synthesis. Be skeptical and concrete. If nothing truly new survived, SAY SO plainly (a clean set of honest negatives is the expected, scientifically-valuable outcome for a project at its structural ceiling). For anything that did survive, judge whether it is a real candidate sleeve, a weak lead, or likely still an artifact, and whether it is distinct from TP01.`
const synthesis = await agent(synthPrompt, { schema: SYNTH_SCHEMA, effort: 'high', label: 'synthesize' })
return {
n_studied: clean.length,
n_promising: enriched.filter((r) => r.promising).length,
n_survived: survivors.length,
survivors: survivors.map((s) => ({ id: s.id, name: s.name, family: s.family, btc_full: s.btc_full_sharpe, eth_full: s.eth_full_sharpe, btc_hold: s.btc_holdout_sharpe, eth_hold: s.eth_holdout_sharpe, worst_dd: s.worst_maxdd, real_votes: s.real_votes, n_verify: s.n_verify, summary: s.summary })),
promising_killed: promisingButKilled.map((s) => ({ id: s.id, name: s.name })),
all_grades: clean.map((r) => ({ id: r.id, name: r.name, grade: r.grade, btc_full: r.btc_full_sharpe, eth_full: r.eth_full_sharpe, btc_hold: r.btc_holdout_sharpe, eth_hold: r.eth_holdout_sharpe, promising: r.promising })),
synthesis,
}