# Phase 1 Lean Spike — Rapporto Tecnico **Autore**: Adriano Dal Pastro **Data**: 10 maggio 2026 **Versione**: 1.0 (finalizzato) **Status**: ✅ Phase 1 chiusa, tutti 5 hard gate passati **Documenti correlati**: - `docs/superpowers/specs/2026-05-09-decisione-strategica-design.md` (decisione strategica B3) - `docs/superpowers/plans/2026-05-09-phase1-lean-spike.md` (piano implementativo) - `docs/decisions/2026-05-10-gate-phase1.md` (decision memo finale) --- ## 1. Setup sperimentale L'obiettivo della Phase 1 lean spike è dimostrare che il loop tecnico (LLM hypothesis → backtest falsification → adversarial check → GA selection) funziona end-to-end e produce output formalizzabile. I cinque hard gate definiti nello spec sez. 4.4 misurano feasibility, non alpha edge — quella è valutazione di Phase 2. ### 1.1 Configurazione del run di riferimento Il run `phase1-real-005` (id `1c526996160446b18c0fb57d94874975`) è il primo a superare tutti i gate dopo 4 iterazioni di bug-fix (vedi sez. 3 del decision memo). | Parametro | Valore | |---|---| | Population size (K) | 20 | | Generazioni | 10 | | Elite k | 2 | | Tournament k | 3 | | Crossover probability | 0.5 | | Random seed | 42 | | Symbol | BTC-PERPETUAL (Deribit) | | Timeframe | 1h | | Range storico | 2024-01-01 → 2026-01-01 (2 anni, 17545 candele) | | Fees backtest | 5 basis points | | n_trials_dsr | 50 | | Tier LLM dominante | C (qwen3-235b-a22b-2507 via OpenRouter) | | Cerbero MCP endpoint | http://localhost:9001 (locale) | | Durata wall-clock | 29 minuti | | Costo LLM | $0.069 | ### 1.2 Stack tecnologico Python 3.13, uv 0.10.9. Test framework: pytest + pytest-mock + responses. Persistence: sqlite3 + sqlmodel. Parsing strategia: `json.loads` con dataclass-based AST. Analytics: pandas + numpy + scipy. LLM: openai SDK con base URL OpenRouter (route unica per tutti i tier S/A/B/C/D). HTTP: requests + tenacity. Dashboard: streamlit + plotly + canvas HTML5 custom. ### 1.3 Architettura del run L'orchestrator (`src/multi_swarm/orchestrator/run.py`, 184 righe) coordina la pipeline end-to-end: 1. **OHLCV loading**: `CerberoOHLCVLoader` chiama `mcp-deribit/tools/get_historical` paginando in chunk da 4500 barre (cap soft Deribit ~5000). Cache parquet su sha1 della query — il run v5 ha riusato cache popolata dai run precedenti, fetch istantaneo. 2. **Market summary**: statistiche return (mean, std, skew, kurt) + classificazione regime volatilità. 3. **Initial population**: 20 genomi distribuiti uniformemente sui 6 cognitive style (physicist, biologist, historian, meteorologist, ecologist, engineer), temperature random in [0.7, 1.2], lookback random in {100, 150, 200, 300}. 4. **Per ogni generazione (10 totali)**: - **Hypothesis**: chiamata LLM con prompt SYSTEM (regole grammar) + USER (market summary). Output JSON estratto via regex fence ```json. Se parse/validation fallisce: retry 1x con error message nel prompt utente. - **Falsification**: AST compilato in `Callable[[df], Series[Side]]`, backtest event-driven con 1-bar exec delay, calcolo Sharpe + Deflated Sharpe (Bailey & López 2014, n_trials=50). - **Adversarial**: 4 check euristici (no_trades, degenerate, overtrading, undertrading). - **Fitness**: `0.5*dsr + 0.25*(tanh(sharpe)+1)` × `1/(1+max_dd)`, range [0, ~1]. Kill (=0) su zero trade o HIGH adversarial finding. - **Next generation**: elitism 2 + tournament 3 + 50% crossover / 50% mutation. 5. **Persistence SQLite**: ogni genome, evaluation, cost_record, adversarial_finding, generation summary persistito con indici per query rapide della dashboard. ### 1.4 Caveat metodologici noti - **In-sample**: il backtest in Phase 1 lean spike non usa walk-forward; tutto il range 2024-2026 viene usato sia per la generazione delle ipotesi sia per la loro valutazione. La sopravvivenza out-of-sample è esplicitamente fuori scope di Phase 1 (gate Phase 2 #2). - **Compiler con indicatori built-in**: il compiler JSON-based (`src/multi_swarm/protocol/compiler.py`) calcola RSI, SMA, ATR, MACD, realized_vol localmente con pandas. `CerberoTools` è plumbed ma non chiamato durante l'esecuzione delle strategie — è disponibile per agenti future-tense ma il fitness Phase 1 dipende solo dagli indicatori locali. - **RSI epsilon-floor**: il compiler ha un epsilon sul `roll_down` per evitare RSI=100 esatto su serie monotonicamente crescenti (artefatto matematico irrilevante su dati reali ma documentato). - **Top-1 strategia con DSR marginale**: vedi sez. 3. --- ## 2. Loop convergence ### 2.1 Fitness per generazione | Gen | Median | Max | P90 | Entropy | |---|---|---|---|---| | 0 | 0.0001 | 0.0601 | 0.0165 | 0.588 | | 1 | 0.0042 | 0.1893 | 0.0731 | 1.261 | | 2 | 0.0188 | 0.3347 | 0.2039 | 1.333 | | 3 | 0.0069 | 0.3347 | 0.3347 | 1.347 | | 4 | 0.0910 | 0.3347 | 0.3347 | 1.415 | | 5 | 0.0016 | 0.3347 | 0.3347 | 0.611 | | 6 | 0.0040 | 0.3347 | 0.3347 | 0.886 | | 7 | 0.0151 | 0.3347 | 0.3347 | 0.982 | | 8 | 0.0066 | 0.3347 | 0.3347 | 0.746 | | 9 | 0.0061 | 0.3347 | 0.3347 | 0.914 | ### 2.2 Lettura **Convergenza tre-step iniziale**: gen 0→1→2 mostra crescita mediana 4x-50x (0.0001 → 0.0042 → 0.0188) e crescita max 3x-6x (0.06 → 0.19 → 0.33). Gate 1 PASS su questa finestra. **Plateau dell'elite da gen 2**: max stabile a 0.3347 per le restanti 7 generazioni — comportamento atteso con `elite_k=2` che preserva il top performer attraverso le generazioni. P90 si allinea al max da gen 3, segno che almeno 2 elite mantengono la top fitness. **Median oscillante**: dopo il picco a gen 4 (0.091), la median fluttua fra 0.0016 e 0.0151 nelle generazioni successive. Causa: turnover stocastico della popolazione (mutation + crossover) introduce genomi nuovi, alcuni dei quali parse correctly ma falliscono Adversarial (no_trades) e si attestano a fitness 0, abbassando la median. Non è regressione strutturale del GA. **Entropy**: oscilla 0.6-1.4 dopo gen 0, sempre sopra soglia 0.5 → diversità di fitness preservata anche durante plateau dell'elite. --- ## 3. Top-5 genomi: ispezione qualitativa | Rank | Genome ID | Gen | Style | Fitness | DSR | Sharpe | Max DD | Trades | Temp | |---|---|---|---|---|---|---|---|---|---| | 1 | `696052b8...` | 2 | physicist | 0.3347 | 0.0021 | 0.381 | 0.0215 | 33 | 0.68 | | 2 | `169376a2...` | 1 | engineer | 0.3347 | 0.0021 | 0.381 | 0.0215 | 33 | 0.78 | | 3 | `eb0265ad...` | 3 | ecologist | 0.2453 | 0.0006 | −0.019 | 0.0011 | 1 | 1.14 | | 4 | `38d4c1d9...` | 1 | engineer | 0.1893 | 0.0001 | −0.245 | 0.0028 | 1 | 0.82 | | 5 | `3e355975...` | 1 | physicist | 0.1893 | 0.0001 | −0.245 | 0.0028 | 1 | 0.78 | ### 3.1 Top-1 strategia (ispezione approfondita) **System prompt** (engineer): *"Cerca segnali con rapporto S/N favorevole, filtri causali, robustezza a perturbazioni di calibrazione."* **Strategia JSON** (3 regole, evaluation in ordine): - **LONG**: `SMA(10) crossover SMA(30)` AND `realized_vol(20) > 0.3%` AND `RSI(14) < 45`. - **SHORT**: `SMA(10) crossunder SMA(30)` AND `realized_vol(20) > 0.3%` AND `RSI(14) > 55`. - **EXIT**: (`RSI(14) > 70` AND `close crossover SMA(50)`) OR `realized_vol(20) < 0.1%`. **Lettura economica**: trend-following SMA-cross fast/slow modulato da filtro volatilità (entra solo quando il regime è abbastanza mosso, esce quando è troppo calmo) e filtro RSI come momentum confirmation (long solo se non già ipercomprato; short solo se non già ipervenduto). L'EXIT è sofisticato: esce su overbought confermato da break sopra MA50, OPPURE su collasso di volatilità. **Performance**: 33 trade su 17545 candele (1 trade ogni 532 candele = 1 ogni 22 giorni). Sharpe positivo modesto, max drawdown 2.15% (basso). DSR praticamente zero (0.0021) — il segnale non è statisticamente significativo dopo correzione multiple testing, perché 33 trade su 2 anni è sample piccolo. **Plausibilità**: pattern economicamente sensato, non casuale. Reminiscente di strategie trend-following classiche (Donchian, turtle-style) con filtri di regime. Lo stile cognitivo "engineer" (S/N favorable, filtri causali) si riflette nella struttura. ### 3.2 Top-2/3/4/5 brevemente - Top-2 è una replica funzionale di Top-1 con metriche identiche. Plausibile elite duplicato o convergenza indipendente sulla stessa strategia (verifica per Phase 2: signal correlation fra duplicati). - Top-3, 4, 5 hanno **1 trade ciascuno** su 2 anni. Sono "lucky shot": una posizione tenuta a lungo che casualmente termina con leggera vincita. Adversarial flagga MEDIUM `undertrading` ma non HIGH, quindi sopravvivono. La fitness function continua dà loro valore non-zero perché `tanh(sharpe)` è leggermente sopra 0.5 e penalty drawdown è quasi 1.0 (max_dd <0.5%). ### 3.3 Ratio top-1 / median Median fitness su 98 evals: 0.0003. Top-1 fitness: 0.3347. **Ratio**: 1116x — Gate 3 soddisfatto con margine drammatico (soglia 1.5x). --- ## 4. Parser failure modes ### 4.1 Statistiche aggregate v5 - Evaluations totali: 98 - Parse success: **98 (100.0%)** - Parse failure: **0 (0.0%)** ### 4.2 Confronto con iterazioni precedenti | Run | Grammar | Parse success | Note | |---|---|---|---| | v1 | S-expression | 33% | LLM nesta indicators non supportati | | v4 | S-expression (con arity check post-fix) | 36% | 89 di 98 errori = `indicator nested` | | v5 | **JSON Schema** | **100%** | Refactor commit `44eb643` | Il salto da 36% a 100% deriva interamente dal cambio di grammar. JSON è natively supported dal training dei modelli LLM moderni; S-expression è esotica e induce hallucination di sintassi creative. ### 4.3 Retry-with-feedback (commit `d4fcb42`) Il sistema accetta 1 retry con error feedback. Nel run v5 il retry **non è mai stato usato** (zero retry per parse, dato il 100% di success). Il retry rimane comunque architetturalmente presente per Phase 2 / casi edge. --- ## 5. Costi reali vs preventivo ### 5.1 Breakdown costi LLM v5 | Tier | Calls | Input tokens | Output tokens | Cost USD | |---|---|---|---|---| | C (qwen3-235b) | 113 | 112369 | 60060 | $0.069 | ### 5.2 Costo cumulativo Phase 1 (5 run, inclusi bug-fix iterations) | Run | Cost | Note | |---|---|---| | v1 (aborted) | $0.034 | 67% parse_error, max_dd bug | | v2 (aborted) | $0.018 | macd 3 args, OHLCV cap discovery | | v3 (aborted) | $0.015 | crash su indicator arity | | v4 (completed FAIL) | $0.057 | 36% parse, fitness tutti 0 | | v5 (completed PASS) | $0.069 | tutti gate passati | | **Totale Phase 1** | **$0.193** | — | ### 5.3 Confronto con preventivo - Preventivo originale (basato su pricing Anthropic Sonnet): $500-700. - Spesa reale Phase 1 totale: **$0.19**. - Deviazione: −99.97%. La differenza non è dovuta a underuse — il run v5 ha fatto 113 chiamate LLM = full saturazione del budget previsto di calls. È un cambio di ordine di grandezza nei prezzi dovuto al pricing aggressivo di OpenRouter per modelli open-weights (qwen3-235b è 7.5x più economico di Sonnet su input, 37x su output). Il preventivo originale era calibrato su Sonnet 4.6. ### 5.4 Implicazioni per Phase 2 Il margine economico permette di pianificare Phase 2 con maggiore aggressività senza superare il cap ($700-1100): - K=40 (×2), gen=15 (×1.5), tier mix 30% B / 70% C, ablation runs multiple. - Estrapolazione lineare conservativa: $0.07 × 2 × 1.5 × ~3 (tier B factor) × 5 (ablation) = ~$3 totali. Possibile spingere a $30-50 senza preoccupazioni se serve per ablation più ricche. **Rischio cost-trap inverso**: tentazione di sovra-dimensionare Phase 2 perché "tanto costa nulla". Mantenere disciplina budget invariata — investire i $700 cap in PIÙ ablation, non in run più grandi. --- ## 6. Diversity metrics ### 6.1 Entropy fitness per generazione Vedi tabella sez. 2.1 colonna entropy. Mai sotto 0.5, picco a gen 4 (1.415). ### 6.2 Cognitive style sopravvissuti gen 9 | Stile | Count gen 9 | Avg fitness | Note | |---|---|---|---| | engineer | 3 | 0.0 | Dominante numericamente ma fitness 0 (genomi recent, non valutati su elite) | | physicist | 1 | 0.0598 | Solo presente nel top-K | | historian | 1 | 0.0002 | — | | biologist | 0 | — | Estinto | | meteorologist | 0 | — | Estinto | | ecologist | 0 | — | Estinto | **Lettura**: pressione selettiva ha eliminato 3 di 6 stili cognitivi alla generazione finale. Engineer è dominante numericamente, physicist domina nel valore (l'unico con fitness >0 della popolazione "live" gen 9). Phase 2 deve introdurre speciation esplicita per evitare questo collasso (minimum 2-3 specie protette). ### 6.3 Trade distribution sui 98 evals | Categoria | n | % | |---|---|---| | Zero trade (HIGH no_trades, kill) | 42 | 42.9% | | Undertrading (1-4 trade, MEDIUM) | 5 | 5.1% | | Normal (5-100 trade) | 9 | 9.2% | | Overtrading (>100 trade, NON flaggato) | 42 | 42.9% | **Issue identificato**: il 42.9% di overtrading non viene catturato dall'Adversarial perché la soglia attuale è `n_trades > n_bars/5 = 3509` — troppo alta per essere triggerata su 1000-2000 trade. Phase 2 dovrebbe abbassare a `n_bars/20 = 877` o usare metrica relativa al regime. ### 6.4 Adversarial findings totali | Finding | Severity | Count | |---|---|---| | no_trades | HIGH | 42 | | undertrading | MEDIUM | 5 | Niente `degenerate` né `overtrading` flaggato. Il primo è raro (richiede strategia sempre-LONG o sempre-SHORT puro), il secondo soffre della soglia troppo alta. --- ## 7. Threats to validity Lista esplicita dei limiti metodologici da non sovra-interpretare: 1. **In-sample fitting**: tutto il backtest è in-sample. Il top-1 ha Sharpe 0.38 ottenuto guardando i dati su cui è stato selezionato. Phase 2 (walk-forward + hold-out Q1-Q2 2026 intoccabile) misura overfitting reale. 2. **Tier C unico**: nessun confronto contro tier B/S. Possibile underperformance del LLM economico vs Sonnet/Opus. Phase 2 introduce ablation multi-tier. 3. **Adversarial hand-crafted**: 4 check euristici (no_trades, degenerate, overtrading, undertrading). Phase 2 introduce 5 prompt LLM-driven dedicati (data snooping, lookahead, regime fragility, crowding, transaction cost erosion). 4. **Fitness function v1**: lineare in DSR + tanh(Sharpe) normalizzato + drawdown moltiplicativa. Non multi-livello (per-team, anti-collusion). Phase 2 introduce. 5. **No speciation, no novelty bonus**: cognitive style scendono da 6 a 3 a gen 9. Phase 2 deve mitigare. 6. **DSR del top-1 = 0.0021**: il "successo" del Gate 3 è guidato da Sharpe (positivo modesto), non da significatività statistica vera. Senza walk-forward + multiple testing rigoroso, non si può affermare alpha edge. 7. **Top-3/4/5 sono "lucky shot" 1-trade**: la fitness function continua li promuove perché drawdown bassissimo + sharpe leggermente negativo, ma sono artefatti. Phase 2 promuove undertrading a HIGH se `n_trades < 10`. 8. **Cerbero/Deribit data quality**: nessuna detection di gap, outlier, exchange downtime. Da affrontare prima di forward-test (Phase 3). 9. **Cost predictability inverso**: Phase 2 deve resistere alla tentazione di sovra-dimensionare perché Phase 1 è costata $0.19. --- ## 8. Conclusioni e implicazioni per Phase 2 **Hard gate sintesi**: ✅ 5 su 5 passati. **Decisione finale**: **GO Phase 2** (formalizzata nel decision memo). **Apprendimenti chiave per Phase 2**: 1. **JSON >> S-expression** per grammar LLM-generated. Phase 2 non rivisita. 2. **Fitness continua è essenziale** per dare gradient al GA, ma può promuovere strategie degeneri (1-trade) che vanno killate diversamente. 3. **OpenRouter qwen3-235b** è sorprendentemente capace per generare strategie strutturate, dato un prompt schema-rigoroso. Tier B (Sonnet) potrebbe non essere necessario al 30% come pianificato; ablation Phase 2 misurerà il vero contributo. 4. **Cerbero MCP come single source of truth** funziona: paginazione, cache parquet, audit log integrati senza fragility. 5. **Bug-fix discovery via run reale** è efficiente: 4 cicli, ognuno ha esposto un problema specifico (max_dd math, macd arity, validator arity, fitness clamp, grammar choice). Phase 2 può aspettarsi pattern simile per nuove componenti (speciation edge cases, OOS overfitting, multi-tier dispatch). **Riusabilità del codebase Phase 1**: il design modulare (data, backtest, metrics, cerbero, protocol, genome, llm, agents, ga, persistence, orchestrator, dashboard) è riusabile direttamente. Estensioni Phase 2: - `ga/speciation.py` (nuovo) — clustering cosine similarity prompt, quota tournament per specie. - `ga/fitness.py` — versione v2 con novelty bonus + per-team aggregation. - `orchestrator/run.py` — integrazione walk-forward. - `agents/adversarial_llm.py` (nuovo) — 5 prompt LLM-driven. - `baseline/random_forest.py` (nuovo) — RF baseline per benchmark. **Costo stimato Phase 2**: $3-15 (estrapolazione molto conservativa). Cap rimane $700-1100 invariato per disciplina. **Tempo stimato Phase 2**: 4-6 settimane di lavoro calendar, includendo i 3 aggiustamenti del decision memo (Adversarial soglie, speciation, walk-forward). --- *Documento finalizzato 10 maggio 2026. Versione 1.0.*