Commit Graph

131 Commits

Author SHA1 Message Date
Adriano Dal Pastro fa11cca2bc docs(readme): allinea a stato finale post-refactor sessione 2026-05-15
Aggiornato per riflettere i 12 commit di refactor della sessione:

  - Layout: 3 servizi Docker (strategy-crypto-paper + strategy-crypto-gui
    + multi-swarm-core-gui), GUI split (core GA / strategy paper)
  - Test: 238 attesi (era 186)
  - Architettura prompt: SYSTEM compositor v3.2 documentato (9 sezioni
    iniettate da scaffold core + contenuto strategy via prompts.json)
  - Grammar: 8 indicatori (5 base + 3 _pct: atr_pct, sma_pct, macd_pct)
  - Input MarketSummary: 14 metriche (5 base + 4 regime + 5 geometria)
  - 7 stili cognitive in prompts.json (era 6, +military_strategist e
    psychologist), con focus_metrics + lookback consigliato + archetipo
  - Deploy: 2 URL distinti (/multi_swarm_core_gui + /strategy_crypto_gui),
    Traefik replacepathregex (NON stripprefix per evitare doppio root_path)
  - Note operative: chown 1000:1000 anche per strategies/ post git mv
  - 3 regression guard test ASCII/archetype/lookback documentati

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 22:05:06 +00:00
Adriano Dal Pastro 6526c6e6e3 refactor(prompts): strategy_crypto v3.2 — consolidamento + 3 invariant tests
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>
2026-05-15 21:54:13 +00:00
Adriano Dal Pastro ccf9f7a33c refactor(prompts): strategy_crypto v3.1 — pulizia post-diagnosi
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>
2026-05-15 21:40:58 +00:00
Adriano Dal Pastro 19a3592a20 feat(prompt): PromptLibrary v3.1 — anti_patterns + output_priorities
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>
2026-05-15 21:38:35 +00:00
Adriano Dal Pastro 5202eb517b refactor(prompt): SYSTEM_TEMPLATE diventa compositor — strategy controlla agent_role/pattern_guidance/instruction
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>
2026-05-15 21:18:54 +00:00
Adriano Dal Pastro 898b24b6a3 fix(orchestrator): definisci prompt_library PRIMA di istanziare HypothesisAgent
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>
2026-05-15 21:05:39 +00:00
Adriano Dal Pastro b6f48e46fc feat(agents): 5 metriche geometrico-frattali + style-aware focus block
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>
2026-05-15 21:04:06 +00:00
Adriano Dal Pastro 0fd31d52ec feat(agents): estendi input LLM con 4 statistiche regime-aware + directive v2.1
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>
2026-05-15 20:43:58 +00:00
Adriano Dal Pastro a43157cd44 refactor(gui): split dashboard in core (GA) + strategy_crypto (paper)
- 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>
2026-05-15 19:23:12 +00:00
Adriano Dal Pastro 436613bfde fix(traefik): sostituisci stripprefix con replacepathregex per evitare doppio root_path
Bug: la dashboard NiceGUI mostrava "Your browser does not support ES modules"
perche' le asset URL nell'HTML erano doppio-prefissate:
  /strategy_crypto_gui/strategy_crypto_gui/_nicegui/...

Root cause: il middleware traefik.stripprefix aggiunge automaticamente
X-Forwarded-Prefix header. uvicorn/Starlette legge il header e setta
root_path automaticamente, raddoppiando con quello passato esplicitamente
a ui.run(root_path="/strategy_crypto_gui").

Fix: traefik.replacepathregex strippa il prefix senza propagare il header.
uvicorn vede solo il root_path da ui.run -> asset prefissati una sola volta.

  - replacepathregex.regex=^/strategy_crypto_gui(/.*|$)
  - replacepathregex.replacement=$1

Verifica end-to-end:
- page: HTTP 200, asset prefix singolo
- /strategy_crypto_gui/_nicegui/3.12.0/static/socket.io.min.js: 200
- / (root): 404 (atteso)

NB: dopo cambio label, necessario `docker restart traefik-traefik-1` per
forzare refresh discovery (problema noto Traefik con label durante recreate
container nello stesso ciclo). Annotare per future modifiche middleware.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:10:18 +00:00
Adriano Dal Pastro f55e4f00c5 fix(deploy): rimuovi PYTHONPATH, member come runtime deps, StripPrefix per NiceGUI subpath
Tre fix di deploy emersi al primo build/up del nuovo compose:

1. Dockerfile: rimosso PYTHONPATH=/app/src
   Con layout uv workspace doppio-nest, PYTHONPATH=/app/src faceva ombra
   alla venv risolvendo le member-dir (multi_swarm_core/, strategy_crypto/)
   come namespace packages PEP 420 senza i sub-package del codice
   (cerbero, persistence, frontend, ecc.). I package sono installati come
   editable dal `uv sync --frozen` nella venv: niente PYTHONPATH necessario.

2. pyproject root: aggiunto [project] dependencies = [multi-swarm-core, strategy-crypto]
   I due member workspace erano in [dependency-groups.dev], escluse da
   `uv sync --frozen --no-dev` del builder Docker -> "ModuleNotFoundError:
   No module named multi_swarm_core". Spostati come dipendenze runtime del
   deployable app root; dev group ora contiene solo pytest/ruff/mypy.
   uv.lock rigenerato.

3. docker-compose.yml: aggiunto Traefik middleware StripPrefix
   NiceGUI con root_path="/strategy_crypto_gui" assume che il proxy
   strippi il prefix prima di girare al container (FastAPI route restano
   "/", "/paper", ecc.). Senza StripPrefix, NiceGUI riceveva
   "/strategy_crypto_gui/" e rispondeva 404. Aggiunte 2 label:
   - strategy-crypto-stripprefix middleware
   - router.middlewares = strategy-crypto-stripprefix

Verifica end-to-end:
- https://swarm.tielogic.xyz/strategy_crypto_gui/ -> HTTP 200 (31KB)
- https://swarm.tielogic.xyz/ -> HTTP 404 (root libera, atteso)
- paper run phase3-baseline-001 (fcf271d0...) tick=1 OK, $1000 equity
- state/strategy_crypto.db creato con 5 tabelle paper_trading_*

NB: permission fix per `src/strategy_crypto/strategy_crypto/strategies/`
fatto manualmente (chown 1000:1000) — i JSON migrati da git mv erano
root:root, container gira come uid 1000. Annotare per future strategies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:59:14 +00:00
Adriano Dal Pastro 96e08ff78f feat(protocol): aggiungi sma_pct + macd_pct per completare la famiglia *_pct
Estende il fix atr_pct (commit f875df3 / 9c3b5ad) coprendo anche SMA e MACD:

protocol/compiler.py:
- _ind_sma_pct(length) = (close - sma) / sma
  Deviazione frazionale del close dalla SMA. Range tipico ±0.1.
  NB: NON e' sma/close (sempre ~1.0, inutile per literal). Uso ideale:
    sma_pct(50) > 0.05 -> "close 5% sopra media a 50 barre" (mean reversion)
- _ind_macd_pct(fast, slow, signal) = macd / close
  MACD come frazione del prezzo. Range tipico ±0.02. Uso ideale:
    macd_pct(12,26,9) > 0.005 -> "momentum > 0.5% del prezzo"

protocol/grammar.py: KNOWN_INDICATORS estesa con sma_pct + macd_pct
protocol/validator.py: arity (1,1) per sma_pct, (0,3) per macd_pct (come macd)

agents/hypothesis.py (system prompt LLM):
- Lista indicatori include sma_pct e macd_pct con annotazioni unita'
- Esempi corretti/errati estesi: sma_pct > 0.05, macd_pct > 0.005
- Pattern guidance: "Mean reversion: sma_pct(long) > 0.05" e
  "Momentum positivo conferma: macd_pct(12,26,9) > 0.005"

genome/mutation_prompt_llm.py: keyword whitelist estesa con sma_pct + macd_pct

Tests (+3):
- test_sma_pct_is_close_deviation_from_sma: identita' algebrica + sign
- test_macd_pct_is_macd_divided_by_close: identita' + scala (rapporto ~close)
- test_sma_pct_and_macd_pct_in_validator: regression validator

Verifica: 191 pass (era 188).

Closes [[protocol_unit_bug]] in full. Family *_pct ora completa per atr/sma/macd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:28:21 +00:00
Adriano Dal Pastro 9c3b5ad586 feat(prompt): integra atr_pct nel system prompt LLM dell'Hypothesis Agent
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>
2026-05-15 18:19:46 +00:00
Adriano Dal Pastro f875df31b4 fix(protocol): aggiungi atr_pct per risolvere bug unità ATR vs literal frazionali
Bug: ATR ritorna unità prezzo assoluto (~30 su ETH @ 3000 USDT), confronti
con literal 0.02/0.01 generavano dead branches (sempre TRUE / sempre FALSE).
Phase 3 baseline-001 abortita per questo motivo.

Fix chirurgico (no breaking change):
- compiler.py: nuovo _ind_atr_pct(df, length) = atr/close (frazione del prezzo)
- grammar.py: atr_pct aggiunto a KNOWN_INDICATORS
- validator.py: arity (1,1) per atr_pct
- atr classico preservato (usato in confronti relativi tipo `atr > sma`)

Strategy patch:
- eth_facd6af85d5d.json: 2 `atr` → `atr_pct` (le 2 condizioni con literal
  0.02 e 0.01 ora producono branch vivi)
- btc_fb63e851.json invariato (usa `atr > sma`, confronto relativo OK)

Tests:
- test_atr_pct_is_atr_divided_by_close: identita' algebrica + ranges expected
- test_atr_pct_in_strategy_eval: branch attivabile con literal frazionali
- 188 pass totale (186 + 2 nuovi)

Decision log: src/multi_swarm_core/docs/decisions/2026-05-15-atr-pct-fix.md

Open: aggiornare il system prompt LLM (mutation_prompt_llm) per includere
atr_pct PRIMA del prossimo Phase 2.x run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:15:01 +00:00
Adriano Dal Pastro 720b2d58d7 Merge branch 'restructure/strategy-crypto-monorepo' 2026-05-15 18:10:25 +00:00
Adriano Dal Pastro 96bbd716ec docs(readme): allinea a monorepo unificato (workspace uv + strategy_crypto subpath)
- Sezione "Layout monorepo (uv workspace)": ridisegna la struttura per chiarire
  i due workspace member (multi-swarm-core + strategy-crypto), DB separati,
  pattern per N strategie future.
- Comandi aggiornati: paper runner ora importa da strategy_crypto.backend,
  dashboard via 'python -m strategy_crypto.frontend.nicegui_app'.
- Backtest cmd punta al nuovo path strategie src/strategy_crypto/strategy_crypto/strategies/.
- Variabili .env: GA_DB_PATH + STRATEGY_CRYPTO_DB_PATH + DASHBOARD_ROOT_PATH.
  Mantenuto nota backcompat su DB_PATH legacy.
- Dashboard: nuova pagina /paper + URL prod /strategy_crypto_gui/.
- Deploy: servizi rinominati strategy-crypto-paper / strategy-crypto-gui,
  bind strategies dal package, image rinominata multi-swarm-coevolutive:dev.
- Rimossi link a doc cancellati (poc_trading_swarm.md, superpowers/, phase1-technical-report).
- Doc rimanenti riposizionati sotto src/multi_swarm_core/docs/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:03:31 +00:00
Adriano Dal Pastro 30add35906 refactor(compose): rinomina servizi + Traefik subpath + dual-DB + bind strategies dal package
Rinomina servizi (era multi-swarm-paper / multi-swarm-dashboard):
- strategy-crypto-paper  — paper runner (container + service + image name)
- strategy-crypto-gui    — NiceGUI dashboard

Routing Traefik:
- Rule: Host(swarm.\${DOMAIN_NAME}) && PathPrefix(/strategy_crypto_gui)
- NESSUN StripPrefix middleware: NiceGUI gestisce root_path internamente
- URL prod: https://swarm.tielogic.xyz/strategy_crypto_gui/
- Root del dominio libera per future GUI di altre strategie

Env block x-swarm-env aggiornato:
- DB_PATH=/app/state/runs.db rimosso
- GA_DB_PATH=/app/state/runs.db (universale GA)
- STRATEGY_CRYPTO_DB_PATH=/app/state/strategy_crypto.db (paper crypto)
- DASHBOARD_ROOT_PATH=/strategy_crypto_gui (passato a ui.run(root_path=...))

Volumi:
- bind strategies dal package: ./src/strategy_crypto/strategy_crypto/strategies:/app/strategies:ro
- image rinominata: multi-swarm:dev → multi-swarm-coevolutive:dev (allineato a wheel)

Entrypoint dashboard: python -m strategy_crypto.frontend.nicegui_app
                      (era multi_swarm.dashboard.nicegui_app)

Verifica: docker compose config parse OK; 2 servizi presenti.

NOTA OPERATIVA: in produzione aggiornare il file .env reale (non in repo)
                per allinearsi a GA_DB_PATH/STRATEGY_CRYPTO_DB_PATH/DASHBOARD_ROOT_PATH.
                Backcompat: DB_PATH legacy ancora letto come alias di GA_DB_PATH.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:02:21 +00:00
Adriano Dal Pastro 8caa526727 test(strategy_crypto): smoke regression + import-mode importlib per workspace
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>
2026-05-15 18:01:30 +00:00
Adriano Dal Pastro 289df4b81f refactor(layout): docs+tests core sotto modulo, cleanup superflui, README strategy
Ownership per modulo:
- Move docs/ root → src/multi_swarm_core/docs/{design,decisions,reports}/
  * 00_documento_zero.md + coevolutive_swarm_system.md → docs/design/
  * decisions/* → docs/decisions/
  * reports/2026-05-14-stato-progetto-e-roadmap.md → docs/reports/
- Move tests/ root → src/multi_swarm_core/tests/

Cleanup superflui consumati (audit trail preservato in docs/decisions):
- poc_trading_swarm.md (POC superato — Phase 3 attiva in prod)
- docs/reports/2026-05-10-phase1-technical-report.md (superato dal 14-mag)
- docs/superpowers/plans/*.md (3 file, plan consumati)
- docs/superpowers/specs/*.md (2 file, spec consumate)
- tests/unit/paper_trading/ (vuota, paper_trading e' migrato in strategy_crypto)
- Directory docs/ root cancellata

NEW: src/strategy_crypto/README.md — overview strategia (scope, layout,
     run, DB schema, pattern N strategie future)

Root resta minima: README.md, pyproject.toml, docker-compose.yml,
                   Dockerfile, .env*, uv.lock + data/series/state/scripts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:59:28 +00:00
Adriano Dal Pastro 2b5da4d1fc refactor(strategies): move JSON freezate sotto strategy_crypto + patch runner
- 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>
2026-05-15 17:58:03 +00:00
Adriano Dal Pastro 37bf64012b refactor(dashboard): move multi_swarm_core/dashboard → strategy_crypto/frontend
- git mv data.py + nicegui_app.py al frontend strategy_crypto
- Cancellato src/multi_swarm_core/multi_swarm_core/dashboard/
- Patch import in data.py: ..persistence → multi_swarm_core.persistence
- Patch nicegui_app.py:
  * Import: multi_swarm_core.dashboard.data → strategy_crypto.frontend.data
  * Dual-DB: split DB_PATH in GA_DB_PATH + PAPER_DB_PATH (env separati)
  * Subpath routing: DASHBOARD_ROOT_PATH passato a ui.run(root_path=...)
  * Header dashboard mostra entrambi i DB
  * Docstring di avvio aggiornata: python -m strategy_crypto.frontend.nicegui_app
  * Title rinominato "Strategy Crypto Dashboard"
- 6 occorrenze get_repo(DB_PATH) → get_repo(GA_DB_PATH)
- 6 occorrenze paper_*_df(DB_PATH, ...) → paper_*_df(PAPER_DB_PATH, ...)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:56:55 +00:00
Adriano Dal Pastro b02be64831 refactor(paper_trading): move multi_swarm_core/paper_trading → strategy_crypto/backend
- git mv executor.py, portfolio.py, persistence.py al backend strategy_crypto
- Cancellato src/multi_swarm_core/multi_swarm_core/paper_trading/
- Patch import: from ..backtest/protocol → from multi_swarm_core.backtest/protocol
- persistence.py:
  * usa schema locale strategy_crypto.backend.schema (no piu' core)
  * docstring aggiornata: DB dedicato state/strategy_crypto.db isolato dal core
  * PaperRepository.init_schema() crea le tabelle paper_trading_*
- backend/__init__.py: re-export pubblico (PaperExecutor, Portfolio, PaperRepository, ...)
- config.py:
  * NEW: ga_db_path (alias DB_PATH legacy + GA_DB_PATH preferito)
  * NEW: strategy_crypto_db_path (STRATEGY_CRYPTO_DB_PATH)
  * db_path conserva come property proxy di ga_db_path per backcompat
- Test: import in venv workspace OK; load_settings() resolve entrambi i path

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:54:31 +00:00
Adriano Dal Pastro 08f1585ab2 refactor(schema): split persistence schema — core GA + strategy_crypto paper
- 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>
2026-05-15 17:46:50 +00:00
Adriano Dal Pastro cd4c3131d9 feat(workspace): setup uv workspace con 2 member (multi-swarm-core + strategy-crypto)
- Root pyproject.toml come workspace coordinator (no codice, dev deps + tool config)
- src/multi_swarm_core/pyproject.toml: package core con dipendenze (pandas, openai, pydantic, ...)
- src/strategy_crypto/pyproject.toml: package strategia con multi-swarm-core come workspace dep
- Aggiunte env var GA_DB_PATH, STRATEGY_CRYPTO_DB_PATH, DASHBOARD_ROOT_PATH in .env.example
- Patch Dockerfile (commento, label, healthcheck per workspace)
- .gitignore: aggiunto .omc/ + state/*.db generico
- Rigenerato uv.lock dal workspace

uv tree mostra:
  strategy-crypto v0.1.0
  ├── multi-swarm-core v0.1.0
  ├── nicegui v3.12.0
  └── ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:45:53 +00:00
Adriano Dal Pastro b6539802e0 refactor(layout): rename multi_swarm → multi_swarm_core con doppia nidificazione uv workspace
- 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>
2026-05-15 17:43:48 +00:00
Adriano Dal Pastro 7d766173a4 chore(structure): bootstrap scheletro src/strategy_crypto member
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>
2026-05-15 17:43:01 +00:00
Adriano Dal Pastro 4d79e66d68 feat(api): esponi API library pubblica via __all__ in protocol/backtest/cerbero/data
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>
v0.1.0-pre-split
2026-05-15 10:33:52 +00:00
Adriano Dal Pastro 4b9cded966 fix(dashboard): leggi righe selezionate da e.args nel callback selection
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>
2026-05-15 05:38:42 +00:00
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