b9e5176a5b
README riscritto: 4 famiglie (FADE/HONEST/PAIRS/TSMOM) con profili netti OOS, portafoglio MASTER e numeri sobri anti-overfit, tabella strategie completa, comandi analisi/validazione, struttura aggiornata (pairs_worker), dati 8 asset + nota naming Deribit (alt = _USDC-PERPETUAL), paper trading a 2 gambe con sezione pairs in YAML. CLAUDE.md: struttura/comandi/sezione paper trader aggiornati (niente piu' "solo MR01"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
230 lines
16 KiB
Markdown
230 lines
16 KiB
Markdown
# PythagorasGoal — Istruzioni per agenti
|
||
|
||
## Panoramica
|
||
|
||
Progetto di ricerca: riconoscimento pattern frattali per trading algoritmico su criptovalute (BTC, ETH). L'obiettivo è arrivare a €50/giorno di profitto partendo da €1.000, entro 6–8 mesi.
|
||
|
||
## Stack
|
||
|
||
- **Linguaggio:** Python 3.11+
|
||
- **Package manager:** uv (dipendenze in `pyproject.toml`, lock in `uv.lock`)
|
||
- **Dati:** Parquet in `data/raw/` (non committati, ~70 MB)
|
||
- **ML:** scikit-learn (GradientBoostingClassifier)
|
||
- **Analisi:** numpy, pandas, scipy
|
||
- **API dati:** Cerbero MCP su `cerbero-mcp.tielogic.xyz` (Deribit, Bybit, Hyperliquid), ccxt/Binance come fallback
|
||
- **Config:** pyyaml per `strategies.yml`
|
||
|
||
## Struttura
|
||
|
||
```
|
||
src/data/ → download e caricamento dati (downloader.py)
|
||
src/fractal/ → indicatori frattali (patterns.py, indicators.py, similarity.py)
|
||
src/backtest/ → engine di backtesting (engine.py)
|
||
src/strategies/ → classe base Strategy ABC + indicatori condivisi
|
||
base.py → Strategy, Signal, BacktestResult, YearlyStats
|
||
indicators.py → keltner_ratio, detect_squeezes, ema, atr, rv, correlation
|
||
src/live/ → paper trading live multi-strategia
|
||
multi_runner.py → orchestratore: carica YAML (strategies + pairs), fetch candele, tick worker
|
||
strategy_worker.py → worker single-leg: capital, trade log, stato persistente.
|
||
Exit guidati da strategia (TP/SL/max_bars via Signal.metadata),
|
||
fallback hold_bars/stop -2%. Usa fee_rt della strategia.
|
||
pairs_worker.py → worker a 2 GAMBE per PR01 (market-neutral): long A / short B sullo
|
||
z-score del log-ratio, exit |z|<=z_exit o max_bars, fee su 2 gambe.
|
||
strategy_loader.py → import dinamico classi Strategy da scripts/strategies/
|
||
cerbero_client.py → client HTTP per Cerbero MCP (Deribit testnet)
|
||
signal_engine.py → squeeze + ML real-time (legacy ML01, ora in waste) + validazione OOS
|
||
telegram_notifier.py → notifiche Telegram per trade
|
||
scripts/strategies/ → strategie con edge validato OOS: FADE (MR01/MR02/MR07),
|
||
HONEST (DIP01/TR01/ROT02), PAIRS (PR01), TSMOM + portafogli (PORT01/02/03)
|
||
scripts/waste/ → strategie scartate (W01-W28 + famiglia squeeze SQ/MT/ML/AD/CM/PD)
|
||
scripts/analysis/ → ricerca/validazione OOS fee-aware (strategy_research, oos_validation, ...)
|
||
strategies.yml → config multi-strategy paper trader
|
||
docs/diary/ → diario di ricerca giornaliero
|
||
docs/specs/ → specifiche di design
|
||
data/raw/ → file .parquet OHLCV (gitignored)
|
||
```
|
||
|
||
## Comandi
|
||
|
||
```bash
|
||
uv sync # installa dipendenze
|
||
uv run python -m src.data.downloader # scarica dati storici
|
||
uv run python scripts/strategies/MR01_bollinger_fade.py # backtest una strategia (es. fade)
|
||
uv run python scripts/strategies/PR01_pairs_reversion.py # backtest pairs market-neutral
|
||
uv run python scripts/analysis/strategy_research.py # ricerca strategie fee-aware OOS
|
||
uv run python scripts/analysis/oos_validation.py # perche' la famiglia squeeze e' scartata
|
||
uv run python scripts/analysis/report_families.py # report per anno di tutte le famiglie
|
||
uv run python scripts/analysis/validate_worker_pairs.py # replay worker 2 gambe == backtest
|
||
uv run python -m src.live.multi_runner # paper trading live multi-strategia (strategie + pairs)
|
||
docker compose up -d # deploy Docker
|
||
uv run pytest # test
|
||
```
|
||
|
||
## Dati storici
|
||
|
||
Scaricati e salvati localmente in Parquet. Per rigenerarli:
|
||
|
||
```python
|
||
from src.data.downloader import download_all, load_data
|
||
download_all() # scarica BTC + ETH su 5m/15m/1h dal 2018
|
||
df = load_data("ETH", "15m") # carica un asset/timeframe
|
||
```
|
||
|
||
Fonte primaria: Cerbero MCP (endpoint `/mcp-deribit/tools/get_historical`).
|
||
Token observer: nel file `secrets/observer.token` del progetto CerberoSuite.
|
||
|
||
## Strategie attive
|
||
|
||
> **LEZIONE CRITICA (2026-05-28).** L'intera famiglia squeeze-breakout (SQ01-04,
|
||
> MT01, ML01, AD01, CM01, PD01) è stata **scartata in `scripts/waste/`**: le
|
||
> accuratezze storiche 76-82% erano un **artefatto di look-ahead**. Quei backtest
|
||
> decidono la direzione con `sign(close[i]-close[i-1])` (la candela di breakout `i`)
|
||
> ma entrano a `close[i-1]` — cioè comprano *prima* della candela che usano per
|
||
> scegliere la direzione. Dal vivo il worker scopre il breakout solo a `close[i]`
|
||
> ed entra lì: l'edge sparisce (win-rate ~47%, lancio di moneta). Sotto ingresso
|
||
> onesto e fee reali **tutte perdono, anche a fee zero**. Inoltre i breakout
|
||
> *rientrano* (mean-reversion > continuation). Vedi `scripts/analysis/oos_validation.py`
|
||
> e `intrabar_test.py`.
|
||
|
||
Tutte le strategie estendono `src.strategies.base.Strategy`
|
||
(`generate_signals() → backtest()`). Le strategie mean-reversion condividono
|
||
`src.strategies.fade_base.FadeStrategy` (backtest intrabar TP/SL/max_bars).
|
||
**Strategie con edge netto validato OOS fee-aware (tutte fade/mean-reversion):**
|
||
|
||
| Codice | Nome | Meccanismo | Edge OOS netto (1h, fee 0.10% RT) | DD | Note |
|
||
|--------|------|-----------|-----------------------------------|----|------|
|
||
| **MR01** | Bollinger Fade | banda std attorno a SMA | BTC +201% / ETH +1238% | 15-72% | Fada la banda, TP alla media, SL ad ATR |
|
||
| **MR02** | Donchian Fade | estremi canale H/L | BTC +172% / ETH enorme | 30-42% | Fada la rottura del canale, TP al centro |
|
||
| **MR07** | Return Reversal | z dei rendimenti di barra | BTC +105% / ETH +195% | 25-46% | Fada il movimento estremo, exit in ATR; esposizione ~8% |
|
||
|
||
> **MR03 Keltner Fade** spostata in `scripts/waste/`: era la fade più debole
|
||
> (BTC Sharpe 1.22, il filtro trend la peggiorava) e ridondante con MR01 (stessa
|
||
> idea di banda). Rimuoverla dal portafoglio ne ha *migliorato* le metriche.
|
||
> La funzione `keltner_fade` resta in `strategy_research_v2.py` come record.
|
||
|
||
**Lezione confermata:** l'edge è sempre *mean-reversion* (i breakout rientrano).
|
||
Il trend-following (Donchian trend, RSI cross) e gli oscillatori senza filtro
|
||
(RSI revert, ADX-filtered fade) perdono netti → restano scartati.
|
||
|
||
Ogni strategia è robusta su **tutta** la sua griglia parametri (entrambi gli asset
|
||
→ tutte positive OOS) e su **tutte** le fee 0.00-0.20% RT (margine ampio).
|
||
MR01 validato col worker reale: BTC +196% / ETH +251% OOS (nov 2023→mag 2026).
|
||
Ricerca completa: `scripts/analysis/strategy_research.py` (MR01) e
|
||
`scripts/analysis/strategy_research_v2.py` (MR02/MR03/MR07).
|
||
Validazione live-path: `scripts/analysis/oos_validation.py`.
|
||
|
||
**Filtro trend (riduzione DD + aumento Acc).** Tutte le fade accettano i parametri
|
||
opzionali `trend_max` / `ema_long`: saltano i segnali quando il prezzo è troppo
|
||
esteso rispetto al trend di fondo (`|close − EMA(ema_long)| / ATR(14) > trend_max`),
|
||
cioè quando si starebbe fadando un trend/crollo estremo. Con `trend_max=3.0`,
|
||
`ema_long=200` (default in `strategies.yml`): accuratezza su tutti gli sleeve
|
||
e DD giù drasticamente su ETH (MR01 71%→26%, MR02 42%→25%, MR03 66%→34%,
|
||
MR07 46%→21%), edge OOS confermato (vedi `scripts/analysis/risk_management.py`).
|
||
Unica eccezione: MR03 BTC, dove il filtro peggiora entrambe → lasciato disattivo.
|
||
Leva non robusta scartate: vol-target sizing e skip-alta-volatilità (peggiorano).
|
||
|
||
**Portafoglio.** Diversificare su sotto-conti indipendenti equipesati (le 4 strategie
|
||
× BTC/ETH, pos 0.15 ciascuno) abbatte il DD aggregato: ~14% full / ~10% OOS sul
|
||
paniere di 8 sleeve, contro il 20-70% del singolo. È la vera leva anti-drawdown.
|
||
|
||
**Combinare le due famiglie (fade + honest).** Le fade (reversione intraday 1h) e le
|
||
honest (DIP/TR/ROT trend+rotazione multi-crypto) sono **quasi scorrelate**
|
||
(correlazione cross-famiglia ~0.05). Combinarle in un unico portafoglio migliora il
|
||
rischio/rendimento rispetto a ciascuna famiglia da sola: equal-weight dei 9 sleeve
|
||
→ DD 5.2% full / 4.7% OOS e Sharpe 4.23 full / 4.33 OOS (vs honest-only 12.6% DD /
|
||
2.20 Sharpe e fade-only 8.2% DD / 4.09 Sharpe), CAGR ~47% mantenuta. Studio in
|
||
`scripts/analysis/combine_portfolio.py`.
|
||
|
||
**ROT02 — riduzione DD (top_k 2→3).** La rotazione dual-momentum honest concentrava
|
||
il book su 2 soli asset (DD 40%). Diversificare su 3 (`top_k=3`) dimezza quasi il DD
|
||
(40%→26%) e *alza* pure il ritorno full (+1095%→+1303%, ret/DD da 27 a 50); il
|
||
vol-target abbassa il DD ma sacrifica ritorno, quindi si tiene top_k=3 senza VT.
|
||
Applicato a `ROT02_dual_momentum.py` e a `_rot_daily_equity` (usata dai portafogli).
|
||
|
||
**Portafogli pronti (artefatti accorpati e migliorati).** Oltre a `PORT01` (solo
|
||
honest), due script in `scripts/strategies/`:
|
||
- `PORT02_fade_master.py` — le 3 fade × BTC/ETH accorpate (6 sleeve, filtro trend),
|
||
equal-weight daily: DD ~8.2% full / 5.9% OOS, Sharpe 3.95/4.09, CAGR ~46%.
|
||
- `PORT03_all_master.py` — portafoglio MASTER (fade + honest, 9 sleeve). Due varianti:
|
||
`equal` (massimo Sharpe: DD 5.2%/4.7% OOS, Sharpe 4.23/4.33) e `5050` fra le due
|
||
famiglie (minimo DD: 5.0% full / 4.5% OOS). È la configurazione consigliata.
|
||
Come `PORT01`, sono meta-portafogli (script `run()` di report), non `Strategy` con
|
||
`generate_signals`, quindi non nel `strategy_loader`.
|
||
|
||
**Esplorazione famiglie alternative (branch `strategy_explore`, 2026-05-29).** Esplorate
|
||
9 famiglie nuove con agenti paralleli su harness onesto condiviso
|
||
(`scripts/analysis/explore_lab.py`). 7 sono rumore (rifiutate: stagionalità oraria/mensile,
|
||
cross-sectional reversal, opening-range breakout, lead-lag BTC→alt, continuation intraday —
|
||
quest'ultima riconferma la dominanza mean-reversion). Due edge reali:
|
||
- **PR01 Pairs** (`scripts/strategies/PR01_pairs_reversion.py`): spread reversion
|
||
market-neutral sul log-ratio z-score, **config UNIVERSALE** `n=50 z_in=2.0 z_exit=0.75
|
||
max_bars=72` (anti-overfit, niente tuning per-coppia). **5 coppie robuste**: ETH/BTC
|
||
(Sharpe 4.36), LTC/ETH (3.08), ADA/ETH (2.69), BTC/LTC (2.36, robusta anche 4h), ETH/SOL
|
||
(1.96, la più debole). Pattern: sempre alt-liquido vs major. Plateau confermato
|
||
(heatmap 20/20 Sharpe>1) + walk-forward (ETH/BTC 11/12 finestre+). **BNB/ETH scartata**
|
||
(overfit). Corr col mercato ~0.02-0.08. Fee su **2 gambe**: worker live implementato
|
||
(`src/live/pairs_worker.py`, sezione `pairs:` in `strategies.yml`). LOGICA validata
|
||
(`validate_worker_pairs.py`: replay == backtest ESATTO). LIVE (`live_smoke_pairs.py`,
|
||
smoke reale Cerbero): **tutte e 5 le coppie con feed live fresco**. Naming Deribit:
|
||
BTC/ETH = `<COIN>-PERPETUAL` (inverse); alt = `<COIN>_USDC-PERPETUAL` (lineari USDC,
|
||
storia dal 2022). Trappola: `LTC-PERPETUAL`/`SOL-PERPETUAL` danno vuoto/dati errati →
|
||
usare sempre `_USDC-PERPETUAL`. Resta da verificare solo liquidità/fill in esecuzione.
|
||
Verifica edge: `pairs_research.py`.
|
||
- **TSM01** (`scripts/analysis/tsmom_research.py`): TSMOM multi-orizzonte 3/6/12m + risk-off,
|
||
**gross 0.30**, distinto da ROT02 (corr 0.62), DD 15-22%, mai un anno negativo. Robusto
|
||
(36/36 config OOS+) ma diversificatore, non motore di ritorno (rende meno di ROT02).
|
||
|
||
Aggiungere i **5 pairs** al MASTER (quasi scorrelati, ~0.02-0.09) è il free-lunch più
|
||
grande (`scripts/analysis/combine_v2.py`). **Numeri sobri onesti** (l'OOS singolo 2024-25
|
||
è regime calmo → ottimistico ~50%): worst-DD su 90g rolling **~6%** (non 2.3%), Sharpe
|
||
atteso **~5** (mediana semestrale), ogni anno positivo dal 2021, regge **leva 2x +
|
||
slippage doppio** (CAGR 36%, Sharpe 5.1). Config robusta raccomandata: **MASTER-esteso
|
||
equal-weight, leva 2x, cap pairs ~30-35%** (i pairs sono ~57% del rischio; worker live a
|
||
2 gambe implementato, validato e con feed live su tutte e 5 le coppie — resta da
|
||
verificare liquidità/fill in esecuzione reale). La confluenza multi-TF è stata SCARTATA (overfit).
|
||
|
||
**Metodologia obbligatoria per ogni nuova strategia** (per non ripetere l'errore squeeze):
|
||
1. Ingresso eseguibile: direzione e prezzo decisi con dati **fino a `close[i]`**, mai `close[i-1]` con direzione da `i`.
|
||
2. Backtest **NETTO** dopo fee realistiche Deribit (**0.10% RT** taker, non 0.20%) + leva.
|
||
3. Validazione **out-of-sample** (held-out) + robustezza su griglia parametri + sweep fee.
|
||
4. Crea script in `scripts/strategies/`, aggiungi a `MODULE_MAP` (`strategy_loader.py`) e a `strategies.yml`.
|
||
|
||
Strategie scartate storiche in `scripts/waste/` (W01-W28 + la famiglia squeeze).
|
||
|
||
**Verso €50/giorno.** Con 4 strategie indipendenti (MR01/MR02/MR03/MR07) × 2 asset
|
||
(BTC/ETH) su €1000 ciascuna, il PnL medio storico aggregato è ben oltre €50/giorno;
|
||
ma quei numeri sono backtest a leva 3x su 8 anni e includono anni eccezionali (es.
|
||
ETH 2024). Stima onesta: il target è *plausibile* su un portafoglio diversificato di
|
||
queste fade, ma va confermato col paper trader live prima di rischiare capitale reale.
|
||
|
||
## Multi-Strategy Paper Trader
|
||
|
||
Orchestratore che esegue N strategie in parallelo su dati live Cerbero, ognuna con €1000 USDC virtuali indipendenti.
|
||
|
||
**Config:** `strategies.yml` — due sezioni: `strategies` (single-leg: fade/honest) e
|
||
`pairs` (a 2 gambe). Attive: 6 fade (MR01/MR02/MR07 × BTC/ETH) + 5 coppie PR01.
|
||
**Due worker:** `strategy_worker.py` (single-leg) e `pairs_worker.py` (2 gambe,
|
||
long A / short B sullo z-score del log-ratio, fee su 2 gambe).
|
||
**Persistenza:** `data/paper_trades/{worker_id}/` con `trades.jsonl` (append-only) + `status.json` (resume al restart).
|
||
**Hot-add:** aggiungi riga YAML → `docker compose restart` → storico intatto.
|
||
**Exit strategia:** se un `Signal` porta `tp`/`sl`/`max_bars` in `metadata` (come le fade), il worker esce su take-profit/stop-loss/time-limit; i pairs escono su |z|≤z_exit o max_bars.
|
||
**Naming Deribit (feed live):** major = `<COIN>-PERPETUAL` (inverse); alt = `<COIN>_USDC-PERPETUAL` (lineari USDC). Vedi INSTRUMENT_MAP in `multi_runner.py`.
|
||
**Notifiche:** Telegram per ogni trade (richiede `.env` con `TELEGRAM_BOT_TOKEN` e `TELEGRAM_CHAT_ID`).
|
||
|
||
## Convenzioni
|
||
|
||
- Strategie in `scripts/strategies/` con codice univoco (MR01, ...).
|
||
- Script scartati in `scripts/waste/` (W01-W28 + famiglia squeeze).
|
||
- Diario in `docs/diary/YYYY-MM-DD.md`. Aggiornare dopo ogni esperimento significativo.
|
||
- Nessun dato sensibile nei commit (token, chiavi API). Usare `.gitignore`.
|
||
- Verificare sempre assenza di data leakage prima di fidarsi dei risultati. In particolare: `returns[i-w : i]` include `close[i]` che è un candle nel futuro — usare `returns[i-w : i-1]`.
|
||
|
||
## Attenzione
|
||
|
||
- **Data leakage:** è stata trovata e corretta nello script 05. Ogni volta che si usano rendimenti logaritmici (`np.diff(np.log(close))`), ricordare che `returns[k]` usa `close[k+1]`. I feature devono fermarsi a `returns[i-2]` se il prezzo corrente è `close[i-1]`.
|
||
- **Fee:** Deribit perp reale = taker ~0.05%/lato (**0.10% round-trip**), maker ~0%. Usare 0.10% RT come baseline (lo 0.20% storico era pessimista 2x). Includere SEMPRE nel backtest: sono vincolo di prim'ordine, molte operazioni = morte per fee. Il worker usa `strategy.fee_rt` (MR01 = 0.001).
|
||
- **Leva:** testato con 3x. Aumentare a 5x migliora i rendimenti ma raddoppia il drawdown.
|
||
- **GBM:** GradientBoostingClassifier di scikit-learn. Ensemble di alberi decisionali sequenziali. Walk-forward per evitare leakage temporale.
|
||
- **Cerbero `get_historical` (fix 2026-05-28):** `end_date` come data nuda è inclusivo dell'intera giornata fino all'ultima candela chiusa (es. `end=oggi` arriva fino ad ora, non più a mezzanotte); accettati anche timestamp con orario (`...T14:00:00`, naive=UTC); nessun cap a ~5000 righe (paginazione interna). Il client passa già `end=oggi`, ora corretto. Prima del fix il paper trader restava a zero trade perché il feed era fermo a mezzanotte.
|
||
- **Dati ETH Deribit 15m:** 14-30%/anno di candele *flat* (O=H=L=C, volume 0, run fino a ~54h) per bassa liquidità del perpetuo. Verificato (2026-05-28): escluderle NON cambia i backtest (Δacc ≤0.5pp) → edge robusto. Resta un caveat operativo (slippage/fill in trading reale, irrilevante per paper). BTC pulito eccetto picco ~8% nel 2024.
|