docs: decision memo Phase 1.5 nemotron run (NO-GO)
Run phase1.5-nemotron-001 completato in 2h26min, costo $0.1244. Max fitness 0.0215 stagnante (15x peggio del baseline qwen 0.3347), DSR=0 universale, Sharpe -1.08/-1.15. Loop non converge. Adversarial Phase 1.5 attivo: 98 finding totali, 35 fees_eat_alpha HIGH + 15 flat_too_long + 8 time_in_market — i 3 nuovi check killano correttamente, ma popolazione non ha materiale sano da cui evolvere. Tre direzioni candidate per Phase 2: A) rollback qwen-2.5-72b, B) prompt re-tuning nemotron, C) promuovere deepseek-v4-flash. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
# Phase 1.5 — Run nemotron tier C — Decision Memo
|
||||||
|
|
||||||
|
**Data**: 11 maggio 2026
|
||||||
|
**Run di riferimento**: `phase1.5-nemotron-001` (id `434c417e2b6f42bb8cf32514e5d0db1d`)
|
||||||
|
**Tier LLM**: C → `nvidia/nemotron-3-super-120b-a12b:free`
|
||||||
|
**Durata wallclock**: 2 h 26 min (08:15 → 10:11 UTC, gen 0 → gen 9)
|
||||||
|
**Spesa totale**: $0.1244 (price-table tier C; il modello effettivo è `:free` su OpenRouter, ma il cost tracker applica la pricing nominale del tier)
|
||||||
|
**Status**: ✅ Completato, ma esito strategico **NO-GO** sulla configurazione corrente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Premessa
|
||||||
|
|
||||||
|
Il run `phase1.5-nemotron-001` è la prima esecuzione end-to-end del loop GA con:
|
||||||
|
|
||||||
|
- l'Adversarial layer aggiornato in Phase 1.5 (commits `56a631f` + `d3662f6`), con tre nuovi check HIGH (`flat_too_long`, `fees_eat_alpha`, `time_in_market_too_high`) più i due esistenti rinforzati;
|
||||||
|
- il tier C ribindato a `nvidia/nemotron-3-super-120b-a12b:free`, modello scelto in benchmark contro sette alternative per stabilità JSON e costo nullo;
|
||||||
|
- il fix `EmptyCompletionError` su `llm/client.py` (commit `9d0deb3`) introdotto durante la stessa sessione per gestire le risposte vuote che alcuni provider `:free` ritornano sporadicamente.
|
||||||
|
|
||||||
|
L'obiettivo dichiarato del run era verificare se il nuovo budget di vincoli adversarial — più stretto del v5 — fosse compatibile con la capacità generativa di nemotron, e se la popolazione riuscisse a esplorare una zona di fitness positiva non degenere.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Hard gate Phase 1 — ripercorrenza
|
||||||
|
|
||||||
|
I 5 hard gate originali (definiti nello spec strategico di Phase 1) sono stati rivalutati su questo run come sanity check, non come passaggio formale di gate.
|
||||||
|
|
||||||
|
| # | Gate | Soglia | Misura | Esito |
|
||||||
|
|---|------|--------|--------|-------|
|
||||||
|
| 1 | Loop converge | mediana cresce ≥3 gen consecutive | Gen 0→8: median oscilla tra 0.0 e 0.0073 senza crescita strutturale | ❌ FAIL |
|
||||||
|
| 2 | Parse success | ≥80% proposte LLM parse-OK | 81/89 = **91.0%** | ✅ PASS |
|
||||||
|
| 3 | Top-5 ratio | top-5 fitness ≥10× mediana | top-5 = 0.0162–0.0215; mediana ≈ 0 → ratio indefinito | ⚠️ N/A |
|
||||||
|
| 4 | Entropy | ≥0.5 a fine run | 0.845 alla gen 9 | ✅ PASS |
|
||||||
|
| 5 | Budget | costo ≤ cap | $0.1244 vs cap $700 (0.02%) | ✅ PASS |
|
||||||
|
|
||||||
|
Il gate critico è il numero 1. La popolazione non converge: il `max_fitness` resta inchiodato a `0.0215` dalla generazione 0 fino alla 9, segnale che l'elite preservation cattura un singolo genoma poco peggiore degli altri ma altrettanto inadatto, mentre il resto della popolazione non riesce a superarlo. La mediana è zero in 9 generazioni su 10 (singolo picco a 0.0073 in gen 8).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Lettura dei top genomi
|
||||||
|
|
||||||
|
I cinque genomi a fitness più alta hanno tutti caratteristiche economicamente disastrose:
|
||||||
|
|
||||||
|
| Genome ID | Fitness | DSR | Sharpe | Total return | n_trades |
|
||||||
|
|-----------|---------|-----|--------|--------------|----------|
|
||||||
|
| `0e1f9d7af25cfd6a` | 0.0215 | 0.000 | −1.083 | −115.9% | 385 |
|
||||||
|
| `85a8116ab2cd2735` | 0.0215 | 0.000 | −1.083 | −115.9% | 385 |
|
||||||
|
| `92aae563277b6f21` | 0.0193 | 0.000 | −1.129 | −131.0% | 597 |
|
||||||
|
| `01d0ca99bbdd7320` | 0.0180 | 0.000 | −1.112 | −131.7% | 602 |
|
||||||
|
| `194b096f7edab53c` | 0.0162 | 0.000 | −1.154 | −150.7% | 369 |
|
||||||
|
|
||||||
|
Il fatto che **DSR sia zero per tutti i top-5** indica che nessuna strategia passa il deflation test di Bailey & López 2014: il loop non sta generando proposte con edge statistico anche solo apparente. Il valore di fitness positivo che li seleziona deriva interamente dal termine `tanh(sharpe) × penalty(dd)` della fitness v1, che resta debolmente non nullo anche per Sharpe negativi grazie alla penalty di drawdown e a saturazioni numeriche. I primi due genomi hanno fitness identico a 0.0215 e total return identico — verosimilmente lo stesso elite riproposto a generazioni adiacenti.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Adversarial findings — il sistema fa il suo lavoro
|
||||||
|
|
||||||
|
Il layer Adversarial Phase 1.5 ha emesso 98 finding sul run:
|
||||||
|
|
||||||
|
| Severità | Check | Conteggio |
|
||||||
|
|----------|-------|-----------|
|
||||||
|
| HIGH | `fees_eat_alpha` (nuovo P1.5) | 35 |
|
||||||
|
| MEDIUM | `overtrading` | 19 |
|
||||||
|
| HIGH | `no_trades` | 16 |
|
||||||
|
| HIGH | `flat_too_long` (nuovo P1.5) | 15 |
|
||||||
|
| HIGH | `time_in_market_too_high` (nuovo P1.5) | 8 |
|
||||||
|
| HIGH | `undertrading` | 4 |
|
||||||
|
| HIGH | `degenerate` | 1 |
|
||||||
|
|
||||||
|
Il dato saliente è che i tre check introdotti in Phase 1.5 — `fees_eat_alpha`, `flat_too_long`, `time_in_market_too_high` — sono effettivamente attivi e killano strategie. In particolare `fees_eat_alpha` è la categoria più popolata: 35 occorrenze HIGH. Esempi tipici dai detail dei finding:
|
||||||
|
|
||||||
|
- `Fees $17073.82 = 2032.6% of gross $840.00`;
|
||||||
|
- `Fees $70646.03 = 12671.9% of gross $557.50`;
|
||||||
|
- `Signal flat for 98.8% of bars (>95% threshold)`.
|
||||||
|
|
||||||
|
Il messaggio è netto: il pool di strategie generato da nemotron, ai prompt e ai gradi di libertà attuali, oscilla tra due estremi degeneri — strategie inattive (flat 98%+) e strategie iperattive (overtrading + fee che divorano l'alpha lordo). Phase 1.5 cattura entrambi gli estremi, ma il loop GA non ha materiale di partenza sano da cui evolvere.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Decisione
|
||||||
|
|
||||||
|
**Esito**: NO-GO sulla combinazione `tier C = nemotron` + `Phase 1.5 adversarial` come configurazione di Phase 2.
|
||||||
|
|
||||||
|
Le ragioni a supporto della decisione sono tre.
|
||||||
|
|
||||||
|
Primo, la convergenza è assente per nove generazioni consecutive, non un plateau di selezione raggiunto dopo una fase di salita. Non si tratta cioè di un loop che ha già trovato il suo ottimo e lo conserva, ma di un loop che non ne ha trovato uno.
|
||||||
|
|
||||||
|
Secondo, la distanza dal baseline Phase 1 v5 è di un ordine di grandezza: max fitness `0.0215` qui contro `0.3347` nel run di gate Phase 1, mediana che oscilla sullo zero contro una mediana attorno a `0.005`–`0.09`. Nemotron, in questa configurazione, sta producendo proposte qualitativamente più povere di qwen-2.5-72b nello stesso schema operativo.
|
||||||
|
|
||||||
|
Terzo, i finding adversarial non puntano a un bug del sistema ma a una mancanza di edge nelle proposte. Il loop sta sanzionando correttamente — il problema è a monte, nella generazione.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Tre direzioni per Phase 2
|
||||||
|
|
||||||
|
Tre opzioni si configurano per il passo successivo. Vanno valutate prima di una nuova esecuzione, non in parallelo a essa.
|
||||||
|
|
||||||
|
**Direzione A — Riportare tier C a `qwen/qwen-2.5-72b-instruct`** (configurazione di gate Phase 1). Il run di riferimento `phase1-real-005` è già un baseline noto: max fitness `0.3347`, top genome problematico (flat 99.8%) ma generato sotto Phase 1 adversarial. Rilanciare lo stesso pool con Phase 1.5 adversarial isolerebbe l'effetto del solo hardening sul medesimo motore generativo, senza confondere variabili. Questo è il percorso più informativo nel breve.
|
||||||
|
|
||||||
|
**Direzione B — Mantenere nemotron ma rilassare i prompt di Hypothesis**. L'ipotesi alternativa è che il prompting attuale, calibrato su qwen, sia troppo terso o troppo vincolato per la modalità di ragionamento di nemotron. Iterare due o tre versioni del prompt — più esempi few-shot, vincoli espliciti su `n_trades` minimo e `time_in_market` target — può cambiare radicalmente la qualità dell'output senza cambiare il modello.
|
||||||
|
|
||||||
|
**Direzione C — Sostituire il tier C con un modello a pagamento di fascia comparabile**. Tra i benchmark precedenti, `deepseek/deepseek-v4-flash` è già usato come tier A/B nel file `.env`; promuoverlo a tier C significa accettare una spesa marginale (stima $1–3 per run di 10 gen × 20 pop) in cambio di una qualità generativa nota.
|
||||||
|
|
||||||
|
La preferenza dell'operatore per modelli cost-conscious orienta verso A o B. La direzione C resta utile come benchmark di controllo se A e B fallissero a loro volta.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Operazioni di pulizia eseguite contestualmente
|
||||||
|
|
||||||
|
- Il run zombie `phase1-real-008` (id `6ebcff9f7f6544c18ced50313cf72ca9`, marcato `running` da 07:11 UTC senza processo associato) è stato chiuso a `status='failed'` direttamente in `runs.db`, per evitare contaminazione delle query di dashboard.
|
||||||
|
- Il commit `9d0deb3` (`fix(llm): handle empty completions + missing usage`) è già su `main`. Il `client.py` ora tratta `resp.choices == []` e `resp.usage is None` come errori retryable invece che assertion failure: precondizione necessaria per qualsiasi run successivo su provider `:free`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Note per chi legge
|
||||||
|
|
||||||
|
Questo memo è un documento di decisione, non un rapporto tecnico completo. Il rapporto tecnico esteso del run può essere ricostruito da `runs.db` interrogando le tabelle `runs`, `generations`, `evaluations`, `adversarial_findings`, `cost_records` con `run_id='434c417e2b6f42bb8cf32514e5d0db1d'`. Il design Phase 1.5 e le motivazioni delle soglie adversarial restano definiti nel commit `56a631f` e nei suoi file di test.
|
||||||
Reference in New Issue
Block a user