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>
This commit is contained in:
Adriano Dal Pastro
2026-06-20 19:50:39 +00:00
parent bf84bc91e2
commit 5ac4e16af8
111 changed files with 16924 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
"""MIC03 — Volume-spike breakout
Hypothesis: Breakout of prior high CONFIRMED by volume z-score > threshold -> enter long at close.
Exit: TP, SL, or max_bars timeout.
Implementation:
- Breakout: close[i] > donchian_high(win)[i] (prior win-bar high, shifted by 1 so fully causal)
- Volume confirmation: volume z-score over vol_win bars > vol_thresh
- Entry at close[i], direction = long only (breakouts on the upside)
- TP = entry * (1 + tp_pct), SL = entry * (1 - sl_pct), max_bars timeout
Grid (<=4 param sets, 1d only -> total backtests = 4 * 2 assets = 8 <= 6... actually 8.
Reduce to 2 configs to stay within ~6 backtests and avoid slow fee sweeps):
Config A: donchian 20, vol_win 20, vol_thresh 2.0, tp 3%, sl 1.5%, max_bars 10
Config B: donchian 30, vol_win 30, vol_thresh 1.5, tp 4%, sl 2.0%, max_bars 15
Pick the best config by min_asset_holdout_sharpe.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al
import numpy as np
def make_entries_fn(don_win: int, vol_win: int, vol_thresh: float,
tp_pct: float, sl_pct: float, max_bars: int):
def entries_fn(df):
close = df["close"].values.astype(float)
volume = df["volume"].values.astype(float)
n = len(close)
# Donchian upper channel: prior don_win-bar HIGH (shifted, causal)
# Using high prices for breakout reference (breakout above prior high is more meaningful)
high = df["high"].values.astype(float)
don_hi = np.full(n, np.nan)
# rolling max of high over don_win bars, then shift by 1 (prior bar)
for i in range(don_win, n):
don_hi[i] = np.max(high[i - don_win: i]) # excludes bar i -> causal
# Volume z-score (causal): zscore of current volume vs rolling mean/std
vol_mean = np.full(n, np.nan)
vol_std = np.full(n, np.nan)
for i in range(vol_win, n):
v_window = volume[i - vol_win: i] # excludes current bar
vol_mean[i] = np.mean(v_window)
vol_std[i] = np.std(v_window)
vol_z = np.full(n, np.nan)
mask = (vol_std > 0) & np.isfinite(vol_std)
vol_z[mask] = (volume[mask] - vol_mean[mask]) / vol_std[mask]
# Build entry list
entries = [None] * n
for i in range(don_win + vol_win, n):
# Breakout condition: close breaks above prior don_win-bar high
breakout = (np.isfinite(don_hi[i]) and close[i] > don_hi[i])
# Volume confirmation
vol_confirmed = (np.isfinite(vol_z[i]) and vol_z[i] > vol_thresh)
if breakout and vol_confirmed:
entry_px = close[i] # fill at close[i]
tp_px = entry_px * (1.0 + tp_pct)
sl_px = entry_px * (1.0 - sl_pct)
entries[i] = {
"dir": +1,
"tp": tp_px,
"sl": sl_px,
"max_bars": max_bars,
}
return entries
return entries_fn
# Config A: tighter params
config_a = dict(don_win=20, vol_win=20, vol_thresh=2.0, tp_pct=0.03, sl_pct=0.015, max_bars=10)
# Config B: wider params
config_b = dict(don_win=30, vol_win=30, vol_thresh=1.5, tp_pct=0.04, sl_pct=0.02, max_bars=15)
configs = [
("MIC03-A", config_a),
("MIC03-B", config_b),
]
best_rep = None
best_score = -999.0
for cfg_name, cfg in configs:
print(f"\n--- Running {cfg_name}: {cfg} ---")
fn = make_entries_fn(**cfg)
rep = al.study_signals(cfg_name, fn, tfs=("1d",))
print(al.fmt(rep))
print("JSON:", al.as_json(rep))
score = rep["verdict"].get("best_holdout_sharpe", -999) or -999
if score > best_score:
best_score = score
best_rep = rep
best_rep["_config"] = cfg
best_rep["_config_name"] = cfg_name
print("\n\n=== BEST CONFIG ===")
print(al.fmt(best_rep))
print("JSON:", al.as_json(best_rep))