14522262e6
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera libreria "validata OOS" era artefatto di feed contaminato (print fantasma del feed Cerbero TESTNET + storico Binance/USDT). - Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE 50-82% barre flat; XRP/BNB non certificabili). - Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST con segnale residuo, da ri-validare in isolamento. - Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio, runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/ portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/ (preservati, non cancellati). Diario consolidato in un unico documento. - Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal + src/backtest/engine + load_data; tool dati certificati (rebuild_history, certify_feed, audit_feed, multi_source_check). - Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
121 lines
8.1 KiB
JavaScript
121 lines
8.1 KiB
JavaScript
export const meta = {
|
|
name: 'fade-lossguard',
|
|
description: 'Sistema anti-perdite per le fade in regime trending/low-vol: test meccanismi su MR01/02/07',
|
|
phases: [
|
|
{ title: 'Test', detail: 'agenti: ogni meccanismo di filtro applicato alle fade reali (BTC+ETH)' },
|
|
{ title: 'Synth', detail: 'classifica + miglior loss-guard, gate: riduce DD senza uccidere edge' },
|
|
],
|
|
}
|
|
|
|
const API = `
|
|
=== Harness (gia pronto) ===
|
|
import sys; sys.path.insert(0,'.')
|
|
import numpy as np, pandas as pd, importlib
|
|
from scripts.analysis.regime_lab import load_features, report
|
|
from scripts.analysis.explore_lab import robust, atr
|
|
|
|
def load_strat(mod):
|
|
m=importlib.import_module(mod)
|
|
return next(v() for k,v in vars(m).items() if isinstance(v,type) and hasattr(v,'generate_signals') and getattr(v,'__module__','')==m.__name__)
|
|
FADES={'MR01':('scripts.strategies.MR01_bollinger_fade',dict(bb_window=50,k=2.5,sl_atr=2.0,max_bars=24,trend_max=3.0)),
|
|
'MR02':('scripts.strategies.MR02_donchian_fade',dict(n=20,sl_atr=2.0,max_bars=24,trend_max=3.0)),
|
|
'MR07':('scripts.strategies.MR07_return_reversal',dict(n=50,k=3.5,tp_atr=2.0,sl_atr=1.5,max_bars=24,trend_max=3.0))}
|
|
|
|
# colonne regime_lab (causali): dvol, dvol_pct, vrp, funding_z, dvol_chg, hurst, higuchi, vratio, frac_up/dn
|
|
# ADX (se ti serve) calcolalo causale da OHLC; efficiency-ratio Kaufman = |c[i]-c[i-n]| / sum|diff| su [i-n,i].
|
|
|
|
# PATTERN: genera i segnali fade, poi APPLICA IL TUO FILTRO scartando le entries in regime sfavorevole,
|
|
# confronta BASELINE vs FILTRATA su ogni fade x asset:
|
|
def entries_from(strat, df, par, keep=lambda df,i: True):
|
|
ts=pd.to_datetime(df['timestamp'],unit='ms',utc=True)
|
|
out=[]
|
|
for s in strat.generate_signals(df,ts,**par):
|
|
if keep(df, s.idx): # keep=False -> filtro scarta (loss-guard)
|
|
out.append({'i':s.idx,'d':s.direction,'tp':s.metadata['tp'],'sl':s.metadata['sl'],'max_bars':s.metadata['max_bars']})
|
|
return out
|
|
# per ogni (fade,asset): res_base=report(.., entries_from(..., keep=tutto)); res_filt=report(.., col tuo keep)
|
|
# confronta: Sharpe OOS, DD full/oos, ret, #trade (quanti scartati), e robust(). Aggrega sulle 6 combo.
|
|
`
|
|
|
|
const CONTEXT = `
|
|
PROBLEMA: le fade (MR01 Bollinger, MR02 Donchian, MR07 return-reversal) sono mean-reversion 1h con
|
|
filtro trend EMA200 (trend_max=3.0). DIAGNOSI EMPIRICA (3022 trade, 2021+): le PERDITE e gli STOP
|
|
si concentrano nel regime PERSISTENTE/TRENDING, NON nella bassa vol:
|
|
- somma perdite per cella (Hurst x DVOL): la cella peggiore e' hurst>0.55 (-2695% in low-vol,
|
|
dominante in ogni terzile vol). I peggiori 1% trade hanno hurst medio 0.61 (77% con hurst>0.55).
|
|
- tasso STOP-LOSS: 43% quando hurst>0.55 vs 21% quando hurst<0.45 (anti-persistente). 2x.
|
|
- net: le celle restano positive (i winner battono), quindi filtrare toglie anche winner -> il
|
|
loss-guard e' utile SOLO se riduce DD/coda SENZA uccidere l'edge netto.
|
|
RICERCA ESTERNA (confermata): (a) Hurst regime filter: MR solo H<0.45, in 0.45-0.55 ridurre size,
|
|
evitare H>0.55. (b) ADX: MR profit factor 1.62 con ADX<20 vs -0.74 con ADX>30 (switch di regime piu'
|
|
importante). (c) ATR/vol-EXPANSION ratio>1.5 disabilita MR -> ha prevenuto il 72% delle perdite
|
|
maggiori. (d) time-stop: se non rientra in ~15 barre e' un trend, esci.
|
|
|
|
OBIETTIVO: trovare il MIGLIOR meccanismo (o combo) che, applicato alle fade reali, RIDUCE DD/coda/
|
|
stop-rate MANTENENDO l'edge netto OOS. Metodologia: causale no-look-ahead (le colonne regime_lab
|
|
sono causali; i filtri usano solo dati <= i), netto fee (report() fa OOS+sweep). LEZIONE FR01: un
|
|
filtro che riduce le perdite ma anche i winner spesso NON migliora -> il gate vero e' DD giu' a
|
|
parita' (o quasi) di Sharpe/ret, idealmente Sharpe SU e DD GIU'.
|
|
|
|
` + API
|
|
|
|
const SCHEMA = {
|
|
type: 'object',
|
|
properties: {
|
|
meccanismo: { type: 'string' },
|
|
descrizione: { type: 'string' },
|
|
base_oos_sharpe: { type: 'number' }, filt_oos_sharpe: { type: 'number' },
|
|
base_dd_full: { type: 'number' }, filt_dd_full: { type: 'number' },
|
|
base_oos_ret: { type: 'number' }, filt_oos_ret: { type: 'number' },
|
|
trade_scartati_pct: { type: 'number' },
|
|
riduce_perdite: { type: 'boolean', description: 'riduce DD/coda/stop-rate' },
|
|
preserva_edge: { type: 'boolean', description: 'edge netto OOS preservato (Sharpe non crolla, robust resta)' },
|
|
buon_lossguard: { type: 'boolean', description: 'riduce perdite SENZA uccidere edge -> candidato' },
|
|
verdetto: { type: 'string', description: 'numeri base vs filtrato aggregati sulle 6 combo fade x asset' },
|
|
},
|
|
required: ['meccanismo', 'buon_lossguard', 'riduce_perdite', 'preserva_edge', 'verdetto'],
|
|
}
|
|
|
|
const MECHS = [
|
|
['Hurst-skip H>0.55', 'scarta le fade quando rolling-hurst(window=100) >= 0.55 (regime persistente). Test anche soglia 0.50 e 0.60, riporta la migliore.'],
|
|
['Hurst-size transition', 'NON scartare ma RIDURRE: tieni tutte le entries ma pesa size 1.0 se hurst<0.45, 0.5 se 0.45-0.55, 0.25 se >0.55. (Per testare la riduzione size col report attuale: approssima scartando il 50%/75% delle entries nelle bin alte in modo deterministico per indice, oppure confronta solo le bin.)'],
|
|
['ADX-skip', 'calcola ADX(14) causale; scarta le fade quando ADX>25 (trend). Test soglie 20/25/30.'],
|
|
['vol-expansion vratio', 'scarta le fade quando vratio (vol breve/lunga, colonna regime_lab) > 1.5 (vol in espansione = breakout, non range). Test 1.3/1.5/1.8. (la ricerca dice -72% perdite maggiori)'],
|
|
['efficiency-ratio Kaufman', 'ER = |c[i]-c[i-n]|/sum(|diff|) su finestra n=20; scarta quando ER>0.5 (moto efficiente/trending). Test 0.4/0.5/0.6.'],
|
|
['time-stop piu corto', 'riduci max_bars da 24 a 12 o 15 (esci prima se non rientra = probabile trend). Confronta DD/edge.'],
|
|
['Hurst + vol-expansion combo', 'scarta se hurst>0.55 OPPURE vratio>1.5. Verifica se la combo riduce piu DD del singolo senza perdere piu edge.'],
|
|
['Hurst + ADX combo', 'scarta se hurst>0.55 E ADX>25 (doppia conferma di trend) -> piu selettivo, scarta meno winner.'],
|
|
['vol-target sizing', 'scala la size per 1/realized_vol (target vol costante): approssima tenendo solo le entries in vol moderata, riporta effetto su DD/coda.'],
|
|
['DVOL-rising skip', 'scarta le fade quando dvol_chg>0 forte (DVOL in salita = stress/espansione vol imminente). Test soglie su dvol_chg.'],
|
|
]
|
|
const ASSETS_NOTE = 'Applica a tutte e 3 le fade (MR01,MR02,MR07) su BTC E ETH (6 combo), aggrega base vs filtrato.'
|
|
|
|
phase('Test')
|
|
// ogni meccanismo = 1 agente che testa su tutte le 6 combo; piu' 4 agenti che esplorano combo/parametri fini
|
|
const tasks = MECHS.map(([nm, desc]) => () => agent(
|
|
CONTEXT + `\n\nMECCANISMO DA TESTARE: ${nm}\n${desc}\n\n${ASSETS_NOTE}\n` +
|
|
`Scrivi uno script in /tmp (cd /opt/docker/PythagorasGoal && uv run python /tmp/<file>.py), confronta ` +
|
|
`BASELINE (fade senza filtro) vs FILTRATA su ogni combo, AGGREGA (media o somma equity) e riporta. ` +
|
|
`Il filtro deve essere CAUSALE. Decidi buon_lossguard=true SOLO se riduce DD/coda/stop-rate MANTENENDO ` +
|
|
`l'edge netto OOS (Sharpe non crolla, ret OOS resta ampiamente positivo). Cita i numeri base vs filtrato.`,
|
|
{ label: `mech:${nm.slice(0, 18)}`, phase: 'Test', schema: SCHEMA }))
|
|
|
|
const results = (await parallel(tasks)).filter(Boolean)
|
|
|
|
phase('Synth')
|
|
const good = results.filter(r => r.buon_lossguard)
|
|
const synthesis = await agent(
|
|
CONTEXT +
|
|
`\n\nRisultati di ${results.length} meccanismi testati:\n${JSON.stringify(results, null, 1)}\n\n` +
|
|
`SINTESI FINALE (italiano) per il decisore:
|
|
1) Esiste un loss-guard che riduce le perdite/DD delle fade in regime trending SENZA uccidere l'edge?
|
|
2) Tabella: meccanismo | base vs filtrato (OOS Sharpe, DD, ret, %trade scartati) | buon_lossguard?
|
|
3) Il MIGLIORE (e l'eventuale combo) con i numeri. Quanto DD/coda si risparmia e a che costo di ret.
|
|
4) Coerenza con la ricerca esterna (Hurst<0.45 / ADX / vol-expansion / time-stop).
|
|
5) Raccomandazione: quale filtro applicare alle fade live, con che soglia, e il caveat (serve feed
|
|
DVOL/regime live? il filtro va validato a livello PORT06 = riduce il DD del portafoglio?).
|
|
Onesta: se nessuno migliora davvero (riduce solo ret), dillo. Cita NUMERI reali.`,
|
|
{ label: 'synth-lossguard', phase: 'Synth' })
|
|
|
|
return { results, good, synthesis }
|