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 fastsignal(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, }