Commit Graph

153 Commits

Author SHA1 Message Date
Adriano Dal Pastro 14f130aa5a feat(dashboard): pagina /paper per monitoring forward-test
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>
2026-05-14 19:52:02 +00:00
Adriano Dal Pastro b86dbdc9ee docs(readme): allinea a stato attuale (Phase 3, NiceGUI, Docker deploy)
- "Stato del progetto" riscritto: Phase 3 paper-trading in corso, link al
  report di sintesi del 14 maggio, strategie freezate BTC/ETH e costo
  cumulato $3.74 su 30 run GA.
- Architettura: aggiunti splits.py (WFA), diversity.py, mutation_prompt_llm.py
  (5° operatore), paper_trading/ (portfolio/executor/persistence); commenti
  su fitness.py/adversarial.py aggiornati a v2 soft-kill + 5 check HIGH.
- Nuova lista CLI knobs accumulati Phase 2.5 → 2.7.
- Setup: test count ~180, .env include DOMAIN_NAME e SWARM_DASHBOARD_PORT.
- Comandi: aggiunti backtest_strategy.py e run_paper_trading.py; esempio
  run_phase1.py ora usa --prompt-mutation-weight e --fitness-v2.
- Nuova sezione Deploy: docker-compose con due servizi su rete traefik
  external, bind-mount + chown 1000:1000, override paper via env.
- Costi: da Phase 1 only a cumulato $3.74 + Phase 3 LLM-free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:45:25 +00:00
Adriano Dal Pastro a66f97fb0e docs(reports): chiudi item 10.3 port dashboard a NiceGUI
Aggiorna stato-progetto-e-roadmap riflettendo le modifiche del 14 maggio:
- diagramma architettura (sec 9): dashboard/ ora elenca solo nicegui_app.py
  + data.py; rimosso Streamlit legacy e aquarium.py
- item 10.3 "Port completo dashboard a NiceGUI": marcato [x] con
  riferimenti ai commit 03f723f (cleanup) e 8e5efde (deploy Docker
  via Traefik su swarm.tielogic.xyz). Annotata la scelta esplicita di
  non riportare l'Aquarium su NiceGUI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:30:35 +00:00
Adriano Dal Pastro 073200313c docs(reports): stato progetto e roadmap al 14 maggio 2026
Riepilogo cumulato delle Phase 1 → 3: 30 run GA completate, $3.74 di
costo LLM, paper-trading runner operativo su BTC+ETH con strategie
freezate fb63e851 (BTC, true alpha hour-gated) e facd6af85d5d (ETH,
trend-following long-bias). Documento di sincronizzazione con caveat
aperti (varianza seed, dipendenza qwen-2.5-72b, Cerbero SPOF) e roadmap
suddivisa in completamento Phase 3, estensioni metodologiche, hardening
tecnico e documentazione.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:28:48 +00:00
Adriano Dal Pastro 03f723f7fc refactor(dashboard): switch a NiceGUI, rimuovi legacy Streamlit
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>
2026-05-14 12:15:11 +00:00
Adriano Dal Pastro 8e5efde219 feat(docker): deploy su VPS via traefik con cerbero-mcp interno
Dockerfile multi-stage (python:3.13-slim + uv) e docker-compose con due
servizi che condividono l'immagine:

- multi-swarm-paper: runner long-running scripts/run_paper_trading.py
- multi-swarm-dashboard: Streamlit su https://swarm.${DOMAIN_NAME}

Cerbero raggiunto via rete docker interna (http://cerbero-mcp:9000)
saltando il giro pubblico traefik+TLS. Persistenza via bind mount su
data/, series/, state/ (runs.db con WAL), strategies/ in read-only.

.env.example aggiornato con DOMAIN_NAME, SWARM_DASHBOARD_PORT (porta
interna parametrizzabile) e i PAPER_* per override del command.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:21:10 +00:00
Adriano 45f273f591 feat(phase-3): paper-trading runner BTC+ETH
Modulo paper_trading per forward-test virtuale Phase 3:
- Portfolio multi-asset equal-weight sleeve, fees bp su round-trip
- PaperExecutor compila strategia JSON e applica segnale a bar close
- PaperRepository persiste runs/ticks/trades/equity in runs.db
- CLI scripts/run_paper_trading.py: loop polling Cerbero, exec su nuovo bar

Strategie deployate:
- BTC fb63e851 (Sharpe OOS +0.50, mean rev RSI+ATR+hour gate)
- ETH facd6af85d5d (Sharpe OOS +0.19, trend vol regime + SMA50/200)

Capitale virtuale $1000 (sleeve $500 ciascuno), 2 settimane smoke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 23:34:15 +02:00
Adriano 9d1ef8adcf chore(deps): aggiungi yfinance per test cross-asset non-crypto
Phase 2.7 portabilità: tentativo backtest top genome BTC/ETH su asset
tradFi via yfinance per stress-test universale del setup.
Esito documentato in memoria: yfinance 1h limitato a 730 giorni,
incompatibile con hour-gated strategy su 5+ anni.

Aggiunto .claude/ a .gitignore (scheduled_tasks.lock runtime artifact).
2026-05-13 23:27:53 +02:00
Adriano 67ae6ff74e feat(hypothesis): pattern guidance — forma curve + ripetibilità nel system prompt
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>
2026-05-13 16:49:35 +02:00
Adriano 1a1dfb7a73 feat(fitness): multi-objective combined = alpha*IS + (1-alpha)*OOS opt-in
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>
2026-05-13 16:47:53 +02:00
Adriano 3fcad79f8d fix(llm): timeout esplicito 60s sul client OpenAI (no più hang infinito)
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>
2026-05-12 22:15:31 +02:00
Adriano 242724ba05 feat(phase-2.6): Walk-Forward Validation + min-trades filter parametrico
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>
2026-05-12 17:31:22 +02:00
Adriano 4c184bb5f7 feat(scripts): backtest_strategy.py — esegue una strategia standalone su range esteso
Script utility per validare OOS strategie discovered durante run Phase 2.5.
Carica un JSON strategia (formato Hypothesis output), fetcha OHLCV via
Cerbero, esegue BacktestEngine + FalsificationReport + AdversarialReport,
stampa metriche annualizzate (CAGR, Sharpe, max DD, Calmar).

Esempio:
    uv run python scripts/backtest_strategy.py /tmp/strategy.json \
        --start 2018-09-01 --end 2026-01-01 --label my-strategy

Validato sui top 2 genomi Phase 2.5 (flat-ablation e fitness-v2-combo):
flat-ablation top overfit su 7y (-37%), fitness-v2 top regge (+143% in 7y,
CAGR +12.8%). Conferma che strategie con time gate temporal feature
generalizzano meglio di strategie con SMA crossover hard-tied al regime
del training period.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:56:47 +02:00
Adriano cf42dd85f3 feat(fitness): v2 soft-kill opt-in (HIGH non hard non azzerano, applicano penalty)
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>
2026-05-12 13:52:22 +02:00
Adriano bf70acc322 feat(adversarial): flat_too_long_threshold parametrico (CLI ablation)
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>
2026-05-12 13:45:38 +02:00
Adriano 597815a106 docs(plan): Phase 2.5 task 6 spuntato + status finale tutti i task completati
Mark task 6 (cost attribution) come done dopo commit ba4eb09. Aggiornato
header status con sweet spot empirico weight=0.30 (curva U validata su
run 004 vs control vs weight-0.50).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:43:02 +02:00
Adriano ba4eb09a71 feat(phase-2.5): Task 6 cost_kind attribution + fees_eat_alpha threshold CLI
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>
2026-05-12 10:42:13 +02:00
Adriano 0e01de156f fix(llm): RateLimitError retryable + retry tenacity 3→5 + backoff fino a 30s
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>
2026-05-12 00:44:32 +02:00
Adriano 4c119a109e fix(llm): exclude OpenRouter Novita provider via extra_body (Qwen 2.x rotto)
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>
2026-05-12 00:10:27 +02:00
Adriano a12aead3e5 fix(hypothesis): catch EmptyCompletionError dentro propose() invece di propagare
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>
2026-05-12 00:07:53 +02:00
Adriano ec80af9f26 feat(phase-2.5): population_prompt_diversity metric + piano aggiornato
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>
2026-05-11 23:52:09 +02:00
Adriano c38311e5fa feat(phase-2.5): mutate_prompt_llm operator + weighted dispatcher + GA wiring
Implementazione completa Phase 2.5 (LLM prompt mutator) seguendo il piano in
docs/superpowers/plans/2026-05-11-mutate-prompt-llm-phase-2-5.md.

Nuovo modulo src/multi_swarm/genome/mutation_prompt_llm.py:
- 6 mutation instructions (tighten_threshold, swap_comparator, add_condition,
  remove_condition, change_timeframe, add_temporal_gate)
- mutate_prompt_llm(g, llm, rng, mutator_tier=ModelTier.B): clona genome con
  tier B per la call mutator, costruisce system+user prompt con istruzione
  scelta random, estrae prompt da tag <prompt>...</prompt>, valida
- is_valid_prompt(): 3 check (lunghezza >= 50, keyword tecnica, diff > 5%
  Levenshtein-like via difflib.SequenceMatcher)
- Fallback random_mutate su qualsiasi validation fail O LLM exception

Esteso src/multi_swarm/genome/mutation.py con weighted_random_mutate dispatcher:
con probabilità prompt_mutation_weight invoca mutate_prompt_llm, altrimenti
random_mutate. Backward compat: llm=None oppure weight=0 → solo scalare.

Integrazione GA loop + RunConfig:
- GAConfig.prompt_mutation_weight: float = 0.0 (default off)
- next_generation(llm=...) propagato all'invocazione mutator
- RunConfig.prompt_mutation_weight con stesso default
- run_phase1: passa llm a next_generation solo se weight > 0
- scripts/run_phase1.py: flag CLI --prompt-mutation-weight

Tests (+18, 175 totale):
- tests/unit/test_mutation_prompt_llm.py (12): extract_prompt, is_valid_prompt
  3 check, operator success + fallback su 3 modi (invalid/identical/exception),
  tier B per LLM call, istruzione scelta dal pool
- tests/unit/test_mutation_dispatcher.py (4): weight 0/1/None + distribuzione
  30/70 su 1000 estrazioni con tolleranza ±5%
- tests/integration/test_ga_loop_with_prompt_mutator.py (2): loop con
  weight=1.0 produce prompt evoluti; backward compat weight=0 invariato

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:49:46 +02:00
Adriano 8ec45c5c1b revert(config): rollback tier C a qwen-2.5-72b-instruct (qwen3-235b inferiore)
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>
2026-05-11 23:45:52 +02:00
Adriano 9344395760 fix(dashboard): cost live durante run (era 0 fino a complete_run)
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>
2026-05-11 22:58:03 +02:00
Adriano 6f6fbb30a0 fix(dashboard): JSON config renderizzato come HTML colorato custom (no Prism/hljs)
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>
2026-05-11 22:52:50 +02:00
Adriano 171f554916 fix(dashboard): icone Material rese come testo + JSON keys invisibili
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>
2026-05-11 22:48:53 +02:00
Adriano 9e740cbcbd fix(dashboard): leggibilità code block JSON config con syntax highlighting custom
Background code più chiaro (#1A1A24) e font 13.5px line-height 1.6 per
leggibilità migliore.

Override token highlight.js coordinati con palette neon:
- .hljs-attr (chiavi JSON) → cyan #00D9FF
- .hljs-string (valori stringa) → green #00E676
- .hljs-number, .hljs-literal (numeri/bool/null) → pink #FF2D87
- .hljs-keyword, .hljs-built_in → amber #FFB800
- .hljs-punctuation, .hljs-meta → muted #7A7A8C

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:45:18 +02:00
Adriano 2acba2077b style(dashboard): redesign Neon Trading Dashboard ispirato a screenshot riferimento
Sostituita palette Modern Fintech con look "neon trading" pink+cyan:
- BG #0A0A0F near-black con dual radial glow (pink top-left, cyan bottom-right)
- Surface #13131A card base, hover #1C1C26
- Primary pink #FF2D87 (max fitness, key metrics)
- Secondary cyan #00D9FF (median, entropy)
- Accent amber #FFB800 (p90, warnings)
- Success neon green #00E676, danger neon red #FF3D60

Effetti visivi aggiunti:
- Card top-border glow gradient (linea pink luminosa)
- Card hover: pink shadow esterno + border 0.5 opacity
- Inset shadow per dare profondità (subtle highlight top)
- Brand-dot pulsing animation 2s (glow pink pulsato)
- Header glassmorphic blur(20px) saturate(180%)
- Chart fill area gradient sotto le curve max/entropy
- Linee Plotly spline smoothing 0.6 (curve morbide)
- Marker bordo bianco per pop

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:43:48 +02:00
Adriano 0486e19829 style(dashboard): redesign moderno con palette Modern Fintech Dark
Sostituita palette Inter FC con design language fintech moderno tipo
Linear/Tremor/Plane:

Palette:
- BG #0A0E1A deep blue-black con radial gradient sottile
- Surface #131829 / surface-2 #1A2138 per hover/active
- Primary cyan #06B6D4 (highlight performance)
- Secondary purple #A78BFA (median curves)
- Accent amber #F59E0B (warnings, p90)
- Success #10B981, danger #EF4444

Typography:
- Inter da Google Fonts (peso 400-700)
- JetBrains Mono per code/numeri tabulari
- letter-spacing negativo + tnum font-feature

Component refinement:
- Card: border sottile rgba + hover cyan glow, no gradient pesante
- Header: backdrop-blur 12px + glass effect
- Nav link: underline animato bottom su active
- Brand: dot pulsante cyan con glow
- Progress bar: 8px rounded, track surface-2
- Metric card: layout left-aligned, label uppercase tracked, value 26px
  con accent class (cyan/purple/amber/green) per coding semantico
- Tabella: header uppercase tracked, row hover cyan-tinted
- Code block: surface scuro + border + JetBrains Mono

Plot Plotly:
- paper_bgcolor → surface (no più sfondo full-bleed)
- gridcolor neutral rgba(slate, 0.08)
- Legend bg surface 0.95 opacity

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:39:59 +02:00
Adriano 2f38562e23 feat(dashboard): NiceGUI Genomes page con top-K + ispezione singolo genoma
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>
2026-05-11 22:35:52 +02:00
Adriano 56e22584d9 feat(dashboard): NiceGUI GA Convergence + Inter FC dark theme
Aggiunta route /convergence con due grafici Plotly live:
- Fitness convergence (max, p90, median) — auto-refresh ogni 3s
- Diversity entropy con gate threshold 0.5
- Tabella generazioni ordinabile

Applicata palette Inter FC su entrambe le pagine:
- Sfondo nero #000000
- Surface dark navy #010E80 (heritage Inter)
- Primary blu #1E5BC6, secondary blu #0068A8, accent oro #FFD700

Custom CSS via ui.add_head_html + ui.colors() quasar override.
Header navigazione condiviso con link attivo evidenziato.
Plotly: template plotly_dark + paper/plot bg neri + gridcolor rgba.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:33:53 +02:00
Adriano 5f28884974 feat(dashboard): NiceGUI port — pagina Overview con auto-refresh live
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>
2026-05-11 22:28:19 +02:00
Adriano 7b790b1bc3 feat(dashboard): progress bar live + top fitness sulla pagina Overview
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>
2026-05-11 22:21:04 +02:00
Adriano 41e26cbe5b docs(plan): Phase 2.5 mutate_prompt_llm — operator + dispatcher + integration
Piano in tasca: NON attivare finché Phase 2 (qwen3 + temporal) non raggiunge
una delle condizioni di trigger documentate:
- plateau max fitness < 0.01 per >= 4 gen consecutive
- diversità prompt Levenshtein <= 0.15
- top genome quasi-fit (>= 0.10) con adversarial HIGH >= 2

Sei tipi di mutazione (tighten_threshold, swap_comparator, add/remove_condition,
change_timeframe, add_temporal_gate) via mutator tier B (deepseek-v4-flash),
weighted_random_mutate dispatcher con default 0.0 (opt-in), diversity guard
Levenshtein, fallback random_mutate su validation fail.

Costo stimato +$0.006/run. A/B con baseline B = phase2-qwen3-001 vs
trattamento T = phase2-qwen3-prompt-mut-001 per attribuzione effetto.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:16:30 +02:00
Adriano 9c53995f23 feat(config): align tier defaults to cost-conscious models + qwen3-235b on tier C
- 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>
2026-05-11 22:13:49 +02:00
Adriano 68637d1102 feat(hypothesis): aggiungi feature temporali al prompt con 2 esempi few-shot 2026-05-11 17:04:07 +02:00
Adriano 36cbfadb40 test(protocol): integration test gating temporale + sma 2026-05-11 17:03:05 +02:00
Adriano 2014ed3815 test(protocol): compiler semantica minute_of_hour su 1h 2026-05-11 17:02:02 +02:00
Adriano 22a934a6cf test(protocol): compiler semantica dow + is_weekend 2026-05-11 17:01:02 +02:00
Adriano 9d1f97cff3 feat(protocol): dispatcher temporal features (hour) in compiler 2026-05-11 16:59:26 +02:00
Adriano 0e9489bf88 test(protocol): parametrize temporal feature validator tests + float literals
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:58:08 +02:00
Adriano 3e9a4efcc2 feat(protocol): extend KNOWN_FEATURES with temporal feature names 2026-05-11 16:56:36 +02:00
Adriano 30dbba4d74 docs: piano implementativo feature temporali
7 task TDD-driven: estensione grammar, dispatcher compiler per 4
feature temporali (hour/dow/is_weekend/minute_of_hour), aggiornamento
prompt Hypothesis con few-shot, smoke run end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:54:55 +02:00
Adriano c6cb32325e docs: design spec feature temporali Phase 2
Aggiunge hour/dow/is_weekend/minute_of_hour come FeatureNode nella
grammatica esistente. Universal access (non passa da feature_access),
riuso di FeatureNode (no nuovo tipo AST), few-shot examples nel prompt
Hypothesis. Cinque file toccati, ~120 LOC, backward-compatible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:52:28 +02:00
Adriano 1a171acfb2 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>
2026-05-11 14:46:35 +02:00
Adriano 9d0deb3ae0 fix(llm): handle empty completions + missing usage (nemotron-friendly)
EmptyCompletionError ora retryable. resp.usage può essere None su
provider :free (es. nemotron-3-super-120b-a12b:free) → no assert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:45:49 +02:00
Adriano d3662f6098 feat(adversarial): time_in_market_too_high HIGH (>80% always-in-market)
Simmetrico opposto di flat_too_long: penalizza strategie LONG/SHORT su
piu' dell'80% delle bar. Una sempre-in-market e' leveraged B&H camuffato,
esposto a funding cumulato (perp ogni 8h), tail risk eventi notturni e
nessuna opportunity-cost flexibility. Sweet spot fitness positiva: 5-80%
time in market.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:54:46 +02:00
Adriano 23c9e37f94 docs: aggiungi clone URL Gitea + nota Phase 1.5 in corso
- Sezione 'Repository' con clone SSH ssh://git@git.tielogic.xyz:222/Adriano/Multi_Swarm_Coevolutive.git
- Stato Phase 1.5 (Adversarial hardening) in corso, commit 56a631f gia' applicato.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:48:19 +02:00
Adriano 56a631f38a feat(adversarial): phase 1.5 hardening (tighter thresholds + flat_too_long + fees_eat_alpha)
Stringe le soglie esistenti e aggiunge due check HIGH per killare le
strategie degeneri scoperte nel run v5 (top-1 +2.66% vs BTC B&H +106%,
flat 99.8% del tempo, fees 69% del lordo).

- overtrading: soglia da n_bars/5 a n_bars/20 (MEDIUM)
- undertrading: HIGH se n_trades < 10 (era MEDIUM <5) — sample troppo
  piccolo per distinguere edge da rumore (lucky shot)
- flat_too_long (NEW, HIGH): signal attivo per <5% delle bar — la
  strategia ha mancato il regime, e' una non-strategia
- fees_eat_alpha (NEW, HIGH): gross_pnl > 0 ma fees > 50% del lordo —
  margine sottile non sostenibile in produzione

Test count: 141 -> 145 (+4 nuovi test deterministici via monkeypatch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:36:35 +02:00
Adriano 690da30272 docs: aggiorna README con architettura completa + esito Phase 1
- Stato Phase 1 completata (5/5 hard gate passati).
- Link a decision memo + technical report.
- Architettura modulare aggiornata (cerbero_ohlcv invece di ccxt, JSON
  parser, fitness v1 continua, dashboard aquarium).
- Variabili .env corrette (no ANTHROPIC_API_KEY, modelli per tier).
- Costi tipici reali ($0.07 per run, $0.19 Phase 1 totale).
- Cerbero MCP setup aggiornato (uv run cerbero-mcp, port 9001).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:20:42 +02:00