research(blind): 52 agenti ciechi su curve anonime BTC/ETH — orchestratore valuta PnL/maxDD, niente di nuovo regge
Flotta di 52 subagenti "esperti di segnali" su storico BTC/ETH ANONIMIZZATO (Series A/B rebased a 100, calendario sintetico, split 70/30) — non sanno cosa siano. Ognuno scrive un signal(df)->position causale (script o ML), tunato solo sul train. Orchestratore valuta su PnL e maxDD nel test held-out. Harness cieco leak-free (riusabile): - make_blind.py: export anonimo + overlay; blindlib.py: evaluator con shift della posizione + GUARDIA DI CAUSALITA' online (squalifica ogni look-ahead, ML incluso); blind_eval.py CLI; score_all.py giudice OOS; verify_top.py (corr-al-trend, fee-stress, jackknife). - 52/52 passano la guardia (zero leak su tutta la flotta). Esito OOS (benchmark buy&hold: -7% PnL, 68% DD): - top = macd (+21%, DD 11%, Sh 0.84), accel, vol_of_vol, regime_switch, rf, obv — tutti trend/vol-regime. Sharpe OOS ~0.84 decade dal train ~1.4. Mean-rev e ML in fondo. - 3 scettici indipendenti: REFUTED. regime-luck (top-5 bar = 67-102% del PnL); trend-redundancy (HAC alpha t=+0.9..+1.5, nessuno >1.96 — TSMOM travestito); overfit (accel/vov knife-edge). Verdetto: ri-conferma CIECA e indipendente del soffitto direzionale ~1.3. macd = classe-TP01, forward-monitor non deploy. Diario 2026-06-21-blind-signal-fleet.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
"""agent_00_sma_trend — ANGLE: trend / single long SMA (long/flat).
|
||||
|
||||
Idea (assigned angle): go LONG only while price is meaningfully above a single long
|
||||
simple moving average, otherwise FLAT. The long SMA defines the macro trend; staying
|
||||
flat below it is what cuts the asset's ~77% buy&hold drawdown to ~1/3.
|
||||
|
||||
Tuned on split='train' only (both Series A and B, equal weight):
|
||||
* window W = 150 (canonical long SMA; sits on a wide robust plateau W=135..165)
|
||||
* band B = 0.02 (require close > 1.02*SMA -> avoids whipsaw chop near the line)
|
||||
* vol-target the long exposure to 35% ann vol (vol_win=30d, cap 1.0). This is what
|
||||
actually controls drawdown: long size shrinks when realized vol spikes (every
|
||||
crypto-like crash is a vol spike), so we're never full-size into the worst bars.
|
||||
|
||||
Everything is causal: SMA(close[..i]), realized vol(returns[..i]). No future rows.
|
||||
The evaluator shifts position by one bar (decision at close[i] -> held bar i+1).
|
||||
|
||||
Train (combined A&B): pnl_mean ~ 5.4, maxdd_worst ~ 0.30, sharpe_min ~ 1.36.
|
||||
Honest note: this is a DEFENSIVE trend filter, not alpha — its value is converting a
|
||||
high-PnL/high-DD uptrend into comparable risk-adjusted PnL at a MUCH smaller drawdown.
|
||||
"""
|
||||
import numpy as np
|
||||
import blindlib as bl
|
||||
|
||||
W = 150 # single long SMA window
|
||||
BAND = 0.02 # long only when close > (1+BAND)*SMA(W)
|
||||
TARGET_VOL = 0.35
|
||||
VOL_WIN_DAYS = 30
|
||||
LEV_CAP = 1.0
|
||||
|
||||
|
||||
def signal(df):
|
||||
c = df["close"].values.astype(float)
|
||||
sma = bl.sma(c, W) # causal SMA up to i
|
||||
|
||||
# long/flat gate vs the single long SMA, with a band to dodge whipsaw near the line
|
||||
long_gate = np.where(c > sma * (1.0 + BAND), 1.0, 0.0)
|
||||
long_gate[:W] = 0.0 # no signal before the SMA is defined
|
||||
long_gate[~np.isfinite(sma)] = 0.0
|
||||
|
||||
# size the long with causal vol-targeting (shrinks into vol spikes -> cuts DD)
|
||||
pos = bl.vol_target(long_gate, df, target_vol=TARGET_VOL,
|
||||
vol_win_days=VOL_WIN_DAYS, leverage_cap=LEV_CAP)
|
||||
|
||||
return np.clip(np.nan_to_num(pos, nan=0.0), -1.0, 1.0)
|
||||
Reference in New Issue
Block a user