Patch di consolidamento post-diagnosi v3.1, divisa in REGRESSIONI ripristinate
+ MIGLIORAMENTI opportunistici + INVARIANTI permanenti.
Regressioni v3.0 -> v3.1 ripristinate:
1. ASCII-strict: caratteri circa-uguale (U+2248) sostituiti con 'vicino a' in 3 stili
(historian, engineer, psychologist). Critico per encoding robustness.
2. 'Archetipo dominante: <metafora>': chiusa identitaria reintrodotta in
tutte le 7 directive. Ancora semantica resistente a mutate_prompt_llm.
3. 'Lookback consigliato: X-Y barre': hint range differenziato per stile
(physicist 150-300, biologist 80-200, historian 200-500, meteorologist
50-150, engineer 60-120, military 100-200, psychologist 50-120) per
orientare il parametro evoluto lookback_window del genoma.
Miglioramenti opportunistici:
4. Voce attiva rinforzata: +verbi generativi ('costruisci', 'combina',
'cattura', 'diagnostica', 'preferisci') in tutte le directive
5. anti_patterns 5 -> 7 voci: aggiunti (6) chattering crossover same-type
same-lookback, (7) soglie hard senza isteresi entry/exit
6. output_priorities 4 -> 5 voci: aggiunta in cima (#1) 'coerenza con
lente cognitiva' (fondamento del design swarm)
7. domain_warnings: +frase 'seasonality > 0 non significa significativa, gate a 0.05'
8. NEW _design_invariants metadata: documenta gli invarianti che future
versioni DEVONO preservare (utile per chi edita + mutate_prompt_llm)
NEW invariant tests (regression guards permanenti):
- test_strategy_crypto_directives_ascii_safe
- test_strategy_crypto_directives_have_archetype_marker
- test_strategy_crypto_directives_have_lookback_hint
Statistiche v3.2:
- directive: 800-950 char (era 545-614 in v3.1, troppo snellite)
- physicist 890, biologist 867, historian 887, meteorologist 884,
engineer 904, military_strategist 898, psychologist 909
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pulizia contenuto prompts.json risolvendo le 9 debolezze identificate
in diagnosi v3.0 -> v3.1:
- agent_role: + frase swarm awareness ("la diversita e' asset critico,
esplora territori meno ovvi")
- pattern_guidance: riscritto astratto (rimossi tutti i nomi di
indicatori; SMA(short)>SMA(long), RSI>70, ecc -> "trend strutturale",
"compressione vol", "mean reversion strutturale"). Il GA scopre
l'incarnazione, le directive sono BIAS non template.
- domain_warnings: riformulato come "NON assumere" (rimosso hint
inferenziale su funding rate che avrebbe indotto hallucination)
- directive: trimmate tutte sotto 900 char (era 922-975)
- focus_metrics: standardizzati a 4 per stile (era 4-5, asimmetria
estetica); rimosse ridondanze:
* historian: rimosso autocorr_baseline (gia' visibile in
Regime recente -> usare solo autocorr_recent come delta proxy)
* psychologist: kurt/skew (gia' in base statistics) sostituiti
con autocorr_recent + spectral_entropy (piu informativi)
- NEW anti_patterns: lista esplicita (5 voci) per ridurre overfitting
nel design della strategia (no > 4 AND, no singolo evento, no
correlazione=causalita, no stazionarieta perfetta, no temporal
features con seasonality < 0.05)
- NEW output_priorities: trade-off espliciti (robustezza > ottimalita,
semplicita > complessita, selettivita > attivita)
- NEW _focus_metrics_design metadata: documenta l'intento delle scelte
Effetto atteso al prossimo Phase 1/2 run: ipotesi piu robuste
(meno overfitting cross-regime), strategie piu semplici (no 5+ AND),
maggior diversita di esplorazione (swarm awareness nel role), zero
inferenze su feature non accessibili.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Estende il compositor del SYSTEM con 2 sezioni opzionali iniettate
DOPO i VINCOLI core e PRIMA dell'EXAMPLE:
ANTI-PATTERN DA EVITARE: lista esplicita di cose da evitare (overfitting,
correlazione=causalita, > 4 AND, singolo evento estremo, ecc.)
PRIORITA' DI OUTPUT: trade-off come "robustezza > ottimalita su singolo
regime", "semplicita > complessita raffinata", "selettivita > attivita"
Razionale: ridurre la varianza non-utile nelle strategie generate
quando il LLM affronta trade-off, e prevenire overfitting nel design.
Entrambi i campi sono opzionali (skip se "") -> backward-compatible
con prompts.json v3.0.
PromptLibrary v3.1: +2 fields top-level (default "").
_build_system_prompt: 2 sezioni condizionali post-VINCOLI.
Test: +3 unit (compositor inject/skip + from_json parsing).
Tot: 235 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor architetturale: il prompt non e' piu' un template hardcoded ma viene
COMPOSTO at-runtime da scaffold core + contenuto strategy.
CORE scaffold (universal, fisso):
- Grammar JSON spec (legato al protocol/compiler)
- UNITA' regole semantiche
- VINCOLI del validator
- Esempio output
STRATEGY content (tunable in prompts.json):
- agent_role: "Sei un agente generatore di ipotesi ... [crypto/forex/equity]"
- pattern_guidance: sezione di archetipi tecnici, ora crypto-specific
- instruction: frase finale del USER ("Genera una strategia ... [crypto]")
- domain_warnings: NEW opzionale, per disclaimer di dominio (es. crypto 24/7)
Implementazione:
- PromptLibrary v3.0: 4 nuovi campi top-level (agent_role, pattern_guidance,
instruction, domain_warnings), parsati da prompts.json, default fallback in default()
- hypothesis.py: SYSTEM_TEMPLATE constant rimossa, sostituita da
_build_system_prompt(lib, genome) che compone scaffold + content
- USER_TEMPLATE: ultima riga ora ha placeholder {instruction}
- prompts.json v3.0 in strategy_crypto: agent_role + pattern_guidance +
instruction + domain_warnings popolati con flavor crypto-specific
Pattern: "core = framework, strategy = contenuto". Per future strategie
(forex, equity), basta creare un prompts.json con flavor diverso, zero
modifiche al core. Universal scaffold (grammar, vincoli, units) e'
condiviso e mantiene la garanzia di parse/validate.
Test: +5 unit (compositor + PromptLibrary fields).
Tot: 232 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug introdotto in b6f48e4: HypothesisAgent(prompt_library=prompt_library) era
chiamato a riga 109, ma prompt_library veniva definito a riga 123 -> NameError
a runtime quando run_phase1 viene eseguito.
Spostato il blocco di setup prompt_library + set_cognitive_styles PRIMA della
istanziazione di HypothesisAgent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunge al MarketSummary 5 metriche regime-aware:
- efficiency_ratio (Kaufman): discriminatore trending/ranging
- tail_index_left/right (Hill): pesantezza code, robust vs kurtosis
- structural_uptrend (HH/HL Dow-style): trend strutturale senza lag MA
- compression_ratio: vol coil pre-breakout
- spectral_entropy + dominant_cycle (gated): struttura ciclica nel FFT
Architettura "Style-aware focus, no filtering":
- Tutte le 5 metriche restano visibili a tutti gli stili (landscape GA smooth)
- prompts.json v2.2: ogni stile dichiara focus_metrics: list[str]
- USER_TEMPLATE renderizza "Focus per la tua lente: ..." con i valori prioritari
- Mutation cognitive_style preserva accesso a tutte le metriche (no discontinuita)
PromptLibrary esteso con focus field (parsato da JSON entry styles).
HypothesisAgent accetta prompt_library nel costruttore; orchestrator lo passa.
7 directive aggiornate per interpretare i 5 nuovi input attraverso la lente:
- physicist: efficiency_ratio + dominant_cycle (modi armonici)
- engineer: efficiency_ratio < 0.2 = no signal
- psychologist: tail_left/right = paura/euforia ricorrente
- ecc.
Test: +19 unit (metriche + focus rendering), +smoke MarketSummary.
Tot: 216 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunge al USER_TEMPLATE dell'HypothesisAgent 4 metriche calcolate su rolling
window 500 barre (no backward bias del full sample):
- autocorr_lag1_recent vs autocorr_lag1_baseline (AR(1) delta vs storico)
- hurst_recent (R/S analysis, persistenza di scala)
- vol_percentile (0-100, posizione vol corrente nella distribuzione storica)
- seasonality_hour, seasonality_dow (0-1, varianza spiegata da feature temporali)
Razionale: skew/kurt da soli sono ambigui — un AR(1) discrimina momentum vs
mean-reversion meglio di tutta la guidance sui momenti.
NEW funzioni in metrics/basic.py:
- autocorr_lag1(returns)
- hurst_exponent(returns) via R/S a scale multiple
- vol_percentile_historical(returns, current_window=24, ref_window=2000)
- seasonality_strength(returns, by={"hour"|"dow"})
MarketSummary esteso con 6 nuovi campi (con default); build_market_summary calcola
rolling-500 per "recent", full sample per "baseline".
prompts.json v2.1: tutte le 7 directive estese con frase di interpretazione
style-specific dei 4 nuovi input (no style collapse). Es:
- physicist: "AR(1) sopra baseline = sistema con memoria fuori equilibrio"
- engineer: "AR(1) > 0.05 con std contenuta = SNR favorevole"
- psychologist: "AR(1) recente >> baseline = euforia coordinata"
Tests: +16 unit per le metriche, +1 smoke per MarketSummary populated.
Verifica: 207 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- NEW src/multi_swarm_core/multi_swarm_core/dashboard/ con theme.py + data.py + nicegui_app.py
- theme.py condiviso (CSS + colors + _apply_theme + _json_to_html + _build_header parametrico)
- core GUI: pagine /, /convergence, /genomes — legge SOLO runs.db
- strategy GUI slim: solo /, legge SOLO strategy_crypto.db — importa theme dal core
- Aggiunto nicegui+plotly al core pyproject (uv.lock rigenerato)
- docker-compose: nuovo servizio multi-swarm-core-gui su /multi_swarm_core_gui
(Traefik PathPrefix + replacepathregex, NO stripprefix per evitare doppio root_path)
- .env.example: DASHBOARD_ROOT_PATH ora per-servizio
Pattern: ogni modulo possiede la sua GUI, ogni GUI legge solo il proprio DB.
N strategie future = duplica lo scheletro strategy_crypto/frontend/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Il LLM ora genera strategie sapendo distinguere quando usare atr (unità
prezzo) vs atr_pct (frazione del close) per evitare il bug protocol_unit.
agents/hypothesis.py:
- Lista indicatori: aggiunto atr_pct con annotazione unità inline
- NEW sezione "UNITÀ — REGOLA CRITICA": esempi positivi/negativi su
literal frazionali (atr_pct > 0.02 ✓) vs literal in unità prezzo
(atr > 0.02 ✗ sempre TRUE su asset >$1)
- PATTERN GUIDANCE: "Compressione di volatilità" ora suggerisce atr_pct(N) < 0.01
invece di "ATR(N) < soglia bassa" generico
- "Espansione di volatilità": atr_pct(N) > 0.03 OPPURE confronto relativo
genome/mutation_prompt_llm.py:
- _VALID_KEYWORDS: aggiunti atr_pct + realized_vol (per validazione prompt mutato)
Closes open item da commit f875df3. Prima del prossimo Phase 2.x run il
modello qwen-2.5-72b ricevera' il prompt aggiornato e genera strategie
unit-aware. Vedi src/multi_swarm_core/docs/decisions/2026-05-15-atr-pct-fix.md.
Verifica: 188 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NEW src/strategy_crypto/tests/test_imports.py:
- test_backend_imports — verifica re-export PaperExecutor/Portfolio/PaperRepository + schema
- test_frontend_imports — strategy_crypto.frontend.{data,nicegui_app} importabili
- test_strategies_json_loadable — i JSON sono in importlib.resources e ben formati
- test_init_schema_creates_tables — PaperRepository.init_schema() crea 5 tabelle
Fix pytest collection: add --import-mode=importlib agli addopts per evitare
collisione dei due tests/__init__.py (core + strategy_crypto) sotto stesso
nome module 'tests'.
Verifica: uv run pytest → 186 passed (182 core + 4 strategy_crypto)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- git mv strategies/{btc,eth}_*.json → src/strategy_crypto/strategy_crypto/strategies/
- Cancellata directory root strategies/ (ora package data del member strategy_crypto)
- I JSON sono inclusi nella wheel via force-include nel pyproject del member
- scripts/run_paper_trading.py:
* Import paper_trading.* → strategy_crypto.backend
* Default --strategies-dir letto da importlib.resources.files('strategy_crypto') / 'strategies'
* PaperRepository(settings.strategy_crypto_db_path) + init_schema()
* Rimosso Repository(settings.db_path).init_schema() (GA init non e' responsabilita' del paper)
- Verifica: importlib.resources trova i 2 JSON nel package
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- multi_swarm_core/persistence/schema.py: rimuove tabelle paper_trading_*
(5 CREATE TABLE + 3 indici idx_paper_*). Restano solo le tabelle GA universali
(runs, generations, genomes, evaluations, cost_records, adversarial_findings).
- strategy_crypto/backend/schema.py NEW: PAPER_SCHEMA_SQL standalone + init_schema()
con mkdir parent, scrive su state/strategy_crypto.db.
Ownership: ogni strategia possiede il proprio schema, isolato dal core.
Pattern replicabile per strategy_forex, strategy_equity, ecc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- mv src/multi_swarm → src/multi_swarm_core/multi_swarm_core (member layout)
- sed-replace globale degli import: from/import multi_swarm.* → multi_swarm_core.*
- 115 occorrenze aggiornate in src/ scripts/ tests/
- multi_swarm_coevolutive (nome repo) preservato dal word boundary
Pre-condizione per il setup uv workspace della Fase 3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-condizione per la riorganizzazione in uv workspace: crea il layout
member del nuovo pacchetto strategy_crypto con __init__.py vuoti.
Il contenuto (backend, frontend, strategies, tests) arriva nelle fasi
successive del piano di migrazione.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cristallizza la library surface che il nuovo repo figlio
Swarm_Strategy_Crypto consumerà via git submodule:
- multi_swarm.protocol.{compile,parse,validate}_strategy + AST nodes
- multi_swarm.backtest.{Side, Trade, Order, Position, BacktestEngine}
- multi_swarm.cerbero.CerberoClient
- multi_swarm.data.{CerberoOHLCVLoader, OHLCVRequest}
Cambi a queste signature dovranno essere considerati breaking
per il consumer downstream.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Il callback registrato via top_table.on("selection", ...) riceve un
GenericEventArguments di NiceGUI, non un TableSelectionEventArguments,
quindi l'attributo .selection non esiste e il click su una riga della
tabella "Top genomi" generava AttributeError. Le righe selezionate
arrivano nel payload Quasar come e.args["rows"].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nuova pagina NiceGUI "Paper" che legge le tabelle paper_trading_*:
- 4 metric card: Equity, P/L cumulato %, Trades chiusi, Open/Tick count
- Equity curve plotly con hline initial capital
- Tre tabelle: open positions, ultimi 30 tick (ts/bar/symbol/signal/action),
trades chiusi (entry/exit/pnl/fees)
- Run selector dropdown + status badge + auto-refresh REFRESH_INTERVAL_S
dashboard/data.py: aggiunti 6 helper read-only su SQLite (paper_runs_df,
paper_equity_df, paper_positions_df, paper_trades_df, paper_ticks_df,
paper_run_summary). Connessione separata da Repository per usare
direttamente lo schema paper_trading_* senza passare per la classe di
write PaperRepository.
dashboard/nicegui_app.py: aggiunto import pandas (necessario per
to_datetime nell'equity curve), nav link "Paper" nell'header,
@ui.page("/paper") con helper _paper_runs_options + _paper_equity_figure.
Chiude il primo TODO della roadmap sez 10.1 ("Pagina dashboard
paper-trading").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NiceGUI è la dashboard ufficiale (port 8080, dark/neon palette, 3 pagine:
/, /convergence, /genomes). La porta è ora parametrica via env
SWARM_DASHBOARD_PORT, letta in ui.run() — Docker la usa anche per
healthcheck e label Traefik.
docker-compose.yml: entrypoint del servizio dashboard cambiato da
streamlit a python -m multi_swarm.dashboard.nicegui_app. Default porta
8080 ovunque (.env, .env.example, compose, healthcheck).
Rimossi i file legacy della vecchia GUI Streamlit:
- src/multi_swarm/dashboard/streamlit_app.py
- src/multi_swarm/dashboard/aquarium.py (helper canvas HTML5)
- src/multi_swarm/dashboard/pages/{01_overview,02_ga_convergence,
03_genomes,04_aquarium}.py
- tests/integration/test_streamlit_smoke.py
pyproject.toml: rimossa la dep streamlit; uv.lock rigenerato (10 deps
transitive eliminate: pydeck, watchdog, jsonschema, pillow, ecc.).
README aggiornato (architettura, comando dashboard, sezione Dashboard
ora descrive NiceGUI con riferimento al deploy Docker via Traefik).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunta sezione 'PATTERN GUIDANCE' nel SYSTEM_TEMPLATE che guida il LLM a
generare strategie basate non solo su threshold di indicatori isolati, ma
anche su:
- Forme di curva (trend asc/desc, compressione/espansione vol, mean reversion strutturale)
- Ripetibilità dell'andamento (crossover ricorrenti, pattern intraday/weekly, doppio top, range breakout)
La grammar JSON resta invariata (no nuove primitive); il prompt insegna al
LLM a comporre i nodi esistenti per approssimare pattern di chart analysis
classica. Obiettivo: spostare la generazione dalla soglia statica RSI<30
verso pattern shape-aware che si replicano nei dati storici.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunti due meccanismi per selection multi-objective:
1) Helper compute_combined_fitness(fit_train, fit_oos, alpha):
formula = alpha*IS + (1-alpha)*OOS, fallback a IS se OOS è None/NaN.
2) RunConfig.eval_oos_during_loop (default False) + fitness_combined_alpha
(default 0.5). Quando True E wfa_train_split attivo, ogni genome con
fitness IS > 0 viene rivalutato su test_ohlcv DURANTE il loop GA e la
fitness usata per tournament_select/elite_select è quella combinata.
2x costo backtest engine (richiede 2 evaluation per genome).
3) CLI flags --eval-oos-during-loop e --fitness-combined-alpha.
Motivazione: il run phase2-7-max7y-v2-wfa-001 ha mostrato che il top by
fitness_IS (4e1be9fa, ratio OOS 0.31) NON è il top per performance OOS reale
(634111992702, ratio 1.42, ret_OOS +105% / 2.2y). Selezionare durante GA
con combined fitness orienta l'evoluzione verso strategie OOS-robust by
design invece di filtrarle a posteriori.
Backward compat: default eval_oos_during_loop=False → comportamento
invariato per run senza il flag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run phase2-6-flat-wfa-001 è andato in hang per 4h+ a gen 3/10 su una
chiamata OpenRouter (process stuck in do_poll syscall, 0% CPU). Il SDK
OpenAI ha default timeout 600s ma evidentemente il TLS/socket handshake
può bloccarsi senza interrompere; senza timeout esplicito i retry tenacity
non scattano mai e il run resta pending.
Fix: OpenAI(timeout=60.0). Se una chiamata supera 60s, raise
APITimeoutError (già in _RETRYABLE_EXCEPTIONS) e tenacity ritenta fino a 5
volte con backoff esponenziale fino a 30s. Worst case totale: ~5×60s =
5min per call (vs 4h+ hang precedente).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Due fondamenta scientifiche per filtrare overfit e lucky-shot:
1) undertrading_threshold parametrico (era hardcoded 10):
- AdversarialAgent.__init__(undertrading_threshold=10)
- CLI flag --undertrading-threshold
- Aggiunto a hard_kill_findings v2 default
{"no_trades", "degenerate", "undertrading"}: ora un genome con 1 trade
fortunato (es. genome 80be6bcc-1trade-fit-0.21 di fitness-v2-combo) viene
killato anche sotto fitness v2 soft-kill.
- Test parametric: undertrading_threshold=25 → 15 trade triggerano HIGH.
2) Walk-Forward Validation (WFA):
- RunConfig.wfa_train_split (None=off, 0<x<1=on) + wfa_top_k=5
- run_phase1: split ohlcv in train/test; GA usa solo train; a fine GA
i top_k genomi (by fitness in-sample, fitness>0) vengono rivalutati
sul test_ohlcv via falsification+adversarial+compute_fitness.
- Schema migration: evaluations + fitness_oos, sharpe_oos, return_oos,
max_dd_oos, n_trades_oos (ALTER TABLE con try/except per DB pre-2.6).
- Repository.update_evaluation_oos helper per popolare colonne OOS.
- CLI flags --wfa-train-split, --wfa-top-k.
- Test integration: train_split=0.7 → fitness_oos popolato per top_k.
Motivazione: la fase 2.5 ha generato 17 run con fitness fino a 0.36 + DSR
positivo, ma OOS test su 7 anni mostra che flat-ablation top crolla -37%
mentre fitness-v2 top regge (+143%). WFA in-run permette ora di vedere
direttamente il degradation train→test senza eseguire backtest separati,
rendendo possibile filtrare overfit early durante l'ottimizzazione.
Tests (+2 → 193 totale):
- test_undertrading_threshold_parametric
- test_e2e_wfa_populates_fitness_oos
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunto parametro hard_kill_findings opzionale a compute_fitness.
None (default) = v1 backward-compat: ogni HIGH azzera la fitness.
tuple non vuota = v2: solo finding con name nel set azzerano; gli altri
HIGH applicano penalità moltiplicativa
adv_penalty = 1 / (1 + soft_penalty * n_soft_high)
(default soft_penalty=0.4 → 1 HIGH soft = 0.71x, 2 = 0.56x, 3 = 0.45x).
Motivazione: tutti i run Phase 2/2.5 mostrano 55-87 finding HIGH dominanti
da fees_eat_alpha + flat_too_long. La fitness v1 azzera ogni genome con
anche solo 1 HIGH → median sempre 0 perché quasi tutti i genomi sopravvivono
in modo binario (top integro vs zerati). v2 fornisce gradient continuo:
strategie 'quasi-buone' restano valutabili e il GA può evolverle.
Hard kill v2 default: {"no_trades", "degenerate"} (la strategia letteralmente
non funziona — niente da salvare). Tutti gli altri HIGH (fees_eat_alpha,
flat_too_long, time_in_market_too_high, undertrading, overtrading) → soft.
RunConfig: fitness_hard_kill_findings (None = v1, tuple = v2) +
fitness_adversarial_soft_penalty (default 0.4). CLI flag --fitness-v2 imposta
hard_kill_findings = ("no_trades", "degenerate") e
--fitness-soft-penalty per il fattore custom.
+5 test (12 totali in test_fitness, 191 totale suite):
- v2 soft HIGH non azzera
- v2 hard kill ancora azzera
- v2 cumulativo: più soft HIGH = penalty più severa
- v2 senza findings = identico a v1
- backward compat hard_kill_findings=None = v1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Estende AdversarialAgent con flat_too_long_threshold (default 0.95)
configurabile, simmetrico a fees_eat_alpha_threshold. Propagato a
RunConfig.flat_too_long_threshold e flag CLI --flat-too-long-threshold.
Motivazione: pop30-combo ha registrato 75 finding flat_too_long HIGH
(secondo killer dopo fees_eat_alpha 87). Rilassare la soglia 0.95→0.98
ammette strategie più passive ma marginalmente attive — analogo
all'ablation fees già verificata (+23% stabile).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 6 del piano Phase 2.5 (deferito → ora completato):
- CostRecord: nuovo campo call_kind (default "hypothesis")
- CostTracker.record: accetta call_kind opzionale, summary include
by_call_kind breakdown (hypothesis vs mutation)
- Schema cost_records: aggiunta colonna call_kind TEXT NOT NULL DEFAULT
'hypothesis' + migration soft via ALTER TABLE in init_schema (silently
catched per DB pre-Task 6)
- Repository.save_cost_record: nuova arg call_kind opzionale
- mutate_prompt_llm: accetta cost_tracker/repo/run_id opzionali e logga
la call mutator con call_kind="mutation" quando sink presente
- weighted_random_mutate, next_generation: propagano cost sink
- orchestrator.run_phase1: passa cost_tracker+repo+run_id a
next_generation solo se prompt_mutation_weight > 0
Esposto fees_eat_alpha_threshold come parametro AdversarialAgent
(default 0.5 = comportamento Phase 1.5 invariato), propagato via
RunConfig.fees_eat_alpha_threshold e flag CLI
--fees-eat-alpha-threshold. Abilita ablation con soglia 0.7-0.8 senza
modificare codice — adversarial finding dominante in tutti i run
Phase 2/2.5 (50+ HIGH per run).
Tests (+4 → 186 totale):
- test_cost_tracker: default call_kind="hypothesis"; breakdown
by_call_kind con hypothesis+mutation
- test_mutation_prompt_llm: logging mutation cost con sink completo;
backward compat senza sink (no errore)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run phase2-5-qwen25-prompt-mut-003 fallito a gen 5 (76 evals done, $0.061
spesi) per HTTP 429 RateLimit upstream da DeepInfra su qwen-2.5-72b.
RateLimitError NON era in _RETRYABLE_EXCEPTIONS quindi tenacity falliva
subito, propagando il 429 a propose() e all'orchestrator (run failed).
Tre fix:
1) Aggiunto openai.RateLimitError a _RETRYABLE_EXCEPTIONS.
2) Bumpato stop_after_attempt(3) → 5 e wait_exponential max 10s → 30s.
Più tempo per il rate limit upstream di sbloccare prima di rinunciare.
3) hypothesis.py: try/except RateLimitError in propose() come per
EmptyCompletionError — anche se tenacity esaurisce i 5 retry, il genome
viene marcato fitness=0 e il loop esterno continua senza crash totale.
Test: aggiornato test_completion_retries_on_connection_error per
assert call_count == 5 (era 3). Tutti 182 verdi.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OpenRouter ha cominciato a routare qwen/qwen-2.5-72b-instruct attraverso
Novita, che rifiuta /chat/completions con HTTP 400:
"model: qwen/qwen-2.5-72b-instruct does not support endpoint: completions".
Aggiunto LLMClient.DEFAULT_PROVIDER_IGNORE = ("Novita",) e flag opzionale
provider_ignore al constructor. Le request includono ora
extra_body={"provider": {"ignore": ["Novita"]}} per default.
Verificato manualmente: con ignore Novita, qwen-2.5-72b risponde
normalmente via altri provider (DeepInfra, Together, ecc).
Mantenuta backward compat: passando provider_ignore=() si disabilita
l'esclusione (utile per test o se in futuro Novita si sistema).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run phase2-5-qwen25-prompt-mut-001 fallito perché qwen-2.5-72b ha emesso
empty completion ripetutamente; le 3 retry tenacity in LLMClient.complete
si sono esaurite e l'exception è bollata fino a run_phase1, marcando l'intero
run come failed (perso ~$0.017 di tier C).
Fix: HypothesisAgent.propose() ora cattura EmptyCompletionError nel loop
max_attempts trattandola come parse-fail "empty_completion" e ritentando
con max_retries+1 tentativi (cumulativo: max_retries tenacity × max_attempts
loop esterno, default 3×3 = 9 retry effettive). Se TUTTI i tentativi
falliscono con empty, ritorna proposal con strategy=None e
parse_error="empty_completion: ...", lasciando l'orchestrator continuare
con quel genome marcato come "fitness=0" senza crash totale del run.
+2 test: success dopo 1 empty + retry; failure totale dopo 3 empty consecutive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 5 del piano Phase 2.5: nuovo modulo src/multi_swarm/metrics/diversity.py
con population_prompt_diversity(prompts) che ritorna la diversità media
1 - SequenceMatcher.ratio() su tutte le coppie distinte. 0.0 identici,
fino a ~0.9 totalmente diversi (SequenceMatcher considera spazi/lunghezza).
5 test: edge case empty/single, identici, diversi, intermediate, simmetria.
Piano aggiornato a stato "IMPLEMENTATO 2026-05-11": checkbox task 1-5
spuntate, task 6 (cost attribution per call_kind) deferito con motivazione.
Header preambolo aggiornato con trigger verificati e decisione collaterale
rollback tier C.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run controllo phase2-qwen25-control-001 (seed 42, stessa pipeline Phase 2,
solo tier C switched) ha dimostrato che qwen-2.5-72b è qualitativamente
SUPERIORE a qwen3-235b sul nostro workload:
| metrica | qwen3-235b | qwen-2.5-72b | delta |
| ----------------- | ---------- | ------------ | ----- |
| max fitness | 0.0238 | 0.0311 | +30% |
| median > 0 in gen | mai | 4 gen su 10 | -- |
| entropy media | 0.199 | 0.85 | 4.3x |
| genomi fit > 0 | 5 | 10 | 2x |
| parse success | 97.7% | 100% | + |
| durata | 50 min | 28 min | 0.56x |
| LLM calls | 148 | 90 | 0.61x |
| cost USD | 0.0223 | 0.0122 | 0.55x |
Controintuitivo: 235B con context 262k era atteso superiore al 72B legacy.
In pratica qwen3-235b in tier C produce strategie meno diverse,
meno parsabili e meno ottimizzabili dal GA.
Ripristinati prezzi cost_tracker tier C a 0.40/0.40 (qwen-2.5-72b).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: la dashboard mostrava \$0.0000 per Cost (USD) durante i run in corso
perché leggeva runs.total_cost_usd, che viene aggiornato solo dentro
Repository.complete_run a fine run. I record per-call esistevano già in
cost_records (124 record / \$0.019 sul run phase2-qwen3-001 attivo).
Fix: in _snapshot() se il run è status=running uso Repository.total_cost(run_id)
che fa SUM(cost_usd) live su cost_records. Per i run completed/failed continuo
a leggere total_cost_usd dal record runs (storico autoritativo).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Soluzione robusta al bug "chiavi JSON invisibili": invece di tentare
override CSS su Prism.js/highlight.js (che nelle build NiceGUI possono
non essere attivi o variare), il JSON viene serializzato server-side con
span colorati espliciti e renderizzato via ui.html.
Implementato helper _json_to_html(obj) che produce HTML con classi:
- .cb-key (chiavi) → cyan #00D9FF
- .cb-string → green #00E676
- .cb-number → pink #FF2D87
- .cb-bool / .cb-null → amber #FFB800
- .cb-punct (graffe, virgole, due punti) → muted
Stessa logica per system_prompt e raw LLM output in /genomes: sostituito
ui.code(language=text) con ui.html('<pre class="raw-block">') e
html.escape() per safety XSS. Background dedicato #1A1A24 e font
JetBrains Mono 13.5px, line-height 1.7, ottimo contrasto su qualsiasi
browser senza dipendere da syntax highlighter esterno.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Due bug visivi distinti:
1) Icone Material rotte ("arrow_drop_down" come testo nel selector):
Il selettore CSS '*' applicava font Inter a tutti gli elementi inclusi
gli .material-icons che richiedono il loro proprio font ligature.
Sostituito '*' con whitelist esplicita di elementi UI; aggiunto override
esplicito per .material-icons / .q-icon che forza font 'Material Icons'.
2) JSON keys invisibili nel code block config:
NiceGUI ui.code usa Prism.js (non highlight.js) per il syntax highlighting.
Aggiunti selettori .token.* coordinati con la palette neon:
- .token.property / .token.attr-name → cyan (chiavi JSON)
- .token.string → green (valori string)
- .token.number / .token.boolean / .token.null → pink
- .token.punctuation / .token.operator → muted
- .token.keyword / .token.builtin → amber
Più reset text-shadow per pre[class*=language-].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunta route /genomes:
- Run selector + Top K (10/25/50)
- Tabella sortable con fitness, DSR, Sharpe, max DD, trades, style, T, lookback
- Selezione riga → pannello ispezione con 6 metric card e blocchi code per
system_prompt + raw_text + parse_error eventuale
- Auto-refresh ogni 3s (mantiene selezione corrente se ancora valida)
- Header nav aggiornato con link Genomes
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prima fase migrazione da Streamlit a NiceGUI. Pagina indice riproduce
l'Overview con:
- run selector reattivo
- 2 progress bar live (generations, evaluations) con WebSocket push
- 4 metric card (top fitness, median, parse success %, cost)
- timer auto-refresh ogni 3s (no click manuale)
- status badge colorato (running/completed/failed)
- config JSON code block
Avvio: uv run python -m multi_swarm.dashboard.nicegui_app (porta 8080)
Streamlit resta attivo su 8501 durante la migrazione. Backend invariato:
riusa dashboard/data.py senza modifiche.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggiunto blocco "Progresso run" sopra le metriche statiche con:
- progress bar generazioni (gens_done / n_generations)
- progress bar evaluations (evals_done / pop × gen) con percentuale
- metric top fitness / median fitness / cost so far
- pulsante Refresh manuale + timestamp ultimo update
- emoji status (🟢 running / ✅ completed / ❌ failed)
Niente nuove dipendenze: solo st.progress + st.rerun standard Streamlit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Tier S → google/gemini-3-flash-preview ($0.50/$3.00)
- Tier A/B → deepseek/deepseek-v4-flash ($0.14/$0.28)
- Tier C → qwen/qwen3-235b-a22b-2507 ($0.071/$0.10) — Phase 2 target
- Tier D → openai/gpt-oss-20b ($0.03/$0.14)
Aggiornato cost_tracker con prezzi reali per tier. Defaults config.py
ora rispecchiano .env corrente per evitare divergenze dead-code.
Tier S/A/B/D restano cablati ma non ancora invocati nel loop Phase 2
(solo Hypothesis tier C attivo).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>