Files
Multi_Swarm_Coevolutive/README.md
Adriano Dal Pastro 9742df3a1f fix(fitness): hardening anti-overfit post-7y incident — 3 correzioni
Incident: extended run elite (Sharpe IS 1.93) net-negativo su 7y
continuous (fees=101% del gross). Multi-fold validation NON sufficiente:
ogni fold restarta equity, mascherando accumulo fees compound.

Correzioni:

1) Default --start esteso a 2018-09-01 (7.3 anni)
   - Copre bear 2018-19, halving 2020, COVID, ATH 2021, winter 2022,
     ETF rally 2024, regime corrente.
   - Una finestra corta (2y) lasciava il GA libero di overfit single-regime.

2) fees_eat_alpha promosso a hard-kill in fitness v2
   - Da soft-penalty 0.4x a hard-kill 0 fitness.
   - Una strategia con fees > 50% del gross non e' recuperabile via
     selection: il prodotto del GA non puo' deployare con quel cost burden.

3) Nuovo finding negative_net_pnl (HIGH, hard-kill)
   - Fires quando sum(trade.net_pnl) < 0 sul training window.
   - Cattura: gross negativo (no edge direzionale) E gross positivo ma
     fees > gross (edge insufficiente).
   - Sintesi del net-after-fees su finestra continua come "deal-breaker"
     non negoziabile via soft penalty.

CLI:
- --fitness-hard-kill <csv> per override esplicito.
- Default v2: no_trades,degenerate,undertrading,fees_eat_alpha,negative_net_pnl.

Test:
- 252 pass (251 + 1 nuovo: test_negative_net_pnl_fires_on_negative_gross).
- Test e2e WFA aggiornato: passa fitness_hard_kill_findings=('no_trades',)
  perche' il fixture sintetico non produce strategie profittevoli.
- test_no_findings_on_reasonable_strategy rinominato:
  test_rsi_mean_reversion_loses_money_on_synthetic_data (riflette
  semantica reale: RSI mean-rev su synthetic ohlcv ha net negativo).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 11:07:40 +00:00

250 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Multi_Swarm_Coevolutive
Proof-of-concept di sistema co-evolutivo multi-agente per trading quantitativo. Un genetic algorithm fa evolvere una popolazione di agenti LLM (Hypothesis swarm) che generano strategie di trading espresse in JSON strutturato; un layer Falsification deterministico le backtesta su dati storici (default BTC-PERPETUAL Deribit) via Cerbero MCP; un layer Adversarial euristico le sottopone a red-team checks; la fitness combina Deflated Sharpe Ratio (Bailey & López 2014), Sharpe normalizzato e penalizzazione di drawdown, con opzioni v2 soft-kill e combined IS/OOS per Walk-Forward Validation.
## Repository
Gitea Tielogic (privato, accesso SSH):
```bash
git clone ssh://git@git.tielogic.xyz:222/Adriano/Multi_Swarm_Coevolutive.git
```
## Layout monorepo (uv workspace)
Il repo è un **workspace uv** con due member packages indipendenti, principio "**core = framework, strategy = contenuto**":
```
multi_swarm_coevolutive/ repo root (workspace coordinator)
├── pyproject.toml workspace + dev deps + ruff/mypy/pytest
├── docker-compose.yml 3 servizi su immagine condivisa
├── Dockerfile immagine multi-swarm-coevolutive:dev
├── uv.lock lock unico del workspace
├── data/, series/, state/ cache OHLCV + DB (runtime, gitignored)
├── scripts/ CLI entrypoints (run_phase1, run_paper_trading, ...)
└── src/
├── multi_swarm_core/ WORKSPACE MEMBER (wheel: multi-swarm-core)
│ ├── pyproject.toml core deps (pandas, numpy, openai, pydantic, nicegui, ...)
│ ├── multi_swarm_core/ GA + genome + protocol + backtest + cerbero +
│ │ data + llm + agents + ga + orchestrator +
│ │ metrics + persistence + config + dashboard (GA-only)
│ ├── tests/ unit + integration
│ └── docs/ design/ + decisions/ + reports/
└── strategy_crypto/ WORKSPACE MEMBER (wheel: strategy-crypto)
├── pyproject.toml deps: multi-swarm-core (workspace) + nicegui + plotly
├── README.md overview strategia + pattern per nuove strategie
├── strategy_crypto/
│ ├── backend/ paper-trading (executor, portfolio, persistence, schema)
│ ├── frontend/ NiceGUI paper-only dashboard
│ ├── strategies/ JSON freezate (btc_*.json, eth_*.json)
│ └── prompts.json v3.2: agent_role/pattern_guidance/instruction/
│ domain_warnings/anti_patterns/output_priorities +
│ 7 stili cognitive (directive + focus_metrics)
└── tests/ smoke regression
```
**DB separati per dominio:** `state/runs.db` (GA core universale) + `state/strategy_crypto.db` (paper della strategia crypto). Pattern scala a N strategie senza naming collision.
**Pattern N strategie future:** aggiungere `src/strategy_<asset>/` con stesso scheletro (`backend/`, `frontend/`, `strategies/`, `tests/`, `prompts.json`), DB dedicato `state/strategy_<asset>.db`, servizi Docker `strategy-<asset>-paper` + `strategy-<asset>-gui`, GUI su `/strategy_<asset>_gui`. **Zero modifiche al core** richieste.
## Architettura prompt (v3.2)
**Compositor**: il SYSTEM prompt al LLM viene COMPOSTO at-runtime da scaffold core + contenuto strategy:
```
[1] agent_role ← strategy (prompts.json — chi è l'agente)
[2] cognitive_style + directive ← genome (evoluti dal GA)
[3] GRAMMAR_SPEC ← core scaffold (operatori, indicatori, units rules)
[4] pattern_guidance ← strategy (forme di curva astratte, no indicatori prescritti)
[5] domain_warnings ← strategy (es. "crypto trada 24/7, NON inferire funding rate")
[6] CONSTRAINTS ← core scaffold (validator semantics)
[7] anti_patterns ← strategy (7 voci: no >4 AND, no chattering, isteresi, ecc.)
[8] output_priorities ← strategy (5 voci, #1 coerenza con lente cognitiva)
[9] EXAMPLE ← core scaffold
```
**Input USER (calcolato da `build_market_summary`):**
- Base (5): mean, std, skew, kurt, vol_regime
- Regime recente rolling 500 (6): autocorr_lag1 (recent + baseline), hurst, vol_percentile, seasonality (hour + dow)
- Geometria & frattali (7): efficiency_ratio (Kaufman), tail_index (left + right Hill), structural_uptrend (HH/HL), compression, spectral_entropy, dominant_cycle (gated)
- Feature accessibili dal genome + lookback_window
- **Focus per la tua lente**: blocco style-aware (4 metriche prioritarie da `focus_metrics` di prompts.json)
- Instruction finale (da strategy)
**Grammar protocol JSON (8 indicatori):**
| Indicatore | Output | Range |
|------------|--------|-------|
| `sma(length)` | media mobile | unità prezzo |
| `sma_pct(length)` | (close-sma)/sma | ±0.1 frazione |
| `rsi(length)` | oscillator | 0-100 |
| `atr(length)` | true range | unità prezzo |
| `atr_pct(length)` | atr/close | 0-0.1 frazione |
| `realized_vol(window)` | std returns | 0-0.1 frazione |
| `macd(fast,slow,signal)` | momentum | unità prezzo |
| `macd_pct(...)` | macd/close | ±0.02 frazione |
**7 stili cognitive** (in `prompts.json`, editable): physicist, biologist, historian, meteorologist, engineer, military_strategist, psychologist. Ognuno con directive 800-950 char, ASCII-strict, archetipo dominante + lookback consigliato + 4 focus_metrics.
## Stato del progetto
**Phase 3 (paper-trading forward-test) in corso** dal 13 maggio 2026 su VPS. Runner `scripts/run_paper_trading.py` long-running in Docker, dashboard NiceGUI su `https://swarm.tielogic.xyz/strategy_crypto_gui/`. Due strategie freezate in `src/strategy_crypto/strategy_crypto/strategies/`:
- `btc_fb63e851.json` — BTC-PERPETUAL, RSI estremi + ATR vs SMA + filtro orario 9-17 (Sharpe OOS +0,26 su 7,33 anni).
- `eth_facd6af85d5d.json` — ETH-PERPETUAL, regime-based (con `atr_pct` post-fix bug unità).
Phase 1 → 2.7 chiuse (30 run GA, $3.74 cumulato LLM). Sessione refactor 15 maggio 2026:
- Split repo invertito, monorepo unificato come uv workspace
- Family `*_pct` completa (atr_pct, sma_pct, macd_pct) per fix bug unità
- Dashboard split: core (GA) vs strategy (paper)
- Prompt architecture compositor + prompts.json v3.2 (vedi decision log)
Documenti:
- [**Stato progetto e roadmap (14 maggio 2026)**](src/multi_swarm_core/docs/reports/2026-05-14-stato-progetto-e-roadmap.md)
- Decision log: [`src/multi_swarm_core/docs/decisions/`](src/multi_swarm_core/docs/decisions/) (gate Phase 1, nemotron, atr_pct fix)
- Design docs concettuali: [`src/multi_swarm_core/docs/design/`](src/multi_swarm_core/docs/design/)
Stack: Python 3.13, uv workspace, hatchling, pytest+pytest-mock+responses, openai SDK (verso OpenRouter), requests+tenacity, pandas+numpy+scipy, sqlmodel+sqlite, nicegui+plotly, yfinance.
## Setup
```bash
uv sync # installa entrambi i workspace member come editable
cp .env.example .env # compila CERBERO_*_TOKEN e OPENROUTER_API_KEY
uv run pytest # 252 test attesi (248 core + 4 smoke strategy_crypto)
```
### Variabili .env richieste
```bash
# Cerbero MCP (locale o VPS https://cerbero-mcp.tielogic.xyz)
CERBERO_BASE_URL=http://localhost:9001
CERBERO_TESTNET_TOKEN=<testnet bearer>
CERBERO_MAINNET_TOKEN=<mainnet bearer> # serve per dati storici reali
CERBERO_BOT_TAG=swarm-poc-phase1
# LLM provider (unico endpoint via OpenRouter)
OPENROUTER_API_KEY=<sk-or-v1-...>
# DB paths (split per dominio: core GA vs paper della strategia)
GA_DB_PATH=./state/runs.db
STRATEGY_CRYPTO_DB_PATH=./state/strategy_crypto.db
# Deploy Docker — DASHBOARD_ROOT_PATH ora per-servizio (vedi docker-compose.yml)
DOMAIN_NAME=tielogic.xyz
SWARM_DASHBOARD_PORT=8080
```
Backcompat: `DB_PATH` legacy continua a funzionare come alias di `GA_DB_PATH`.
## Comandi principali
```bash
# Quality gates
uv run pytest # tutti i test
uv run pytest src/multi_swarm_core/tests/unit -v # solo unit core
uv run pytest src/strategy_crypto/tests/ -v # smoke strategy_crypto
uv run ruff check src/ scripts/
uv run mypy src/ scripts/
# Smoke run (MockLLM + OHLCV sintetico, no API calls)
uv run python scripts/smoke_run.py
# Run reale Phase 1/2 (Cerbero + OpenRouter, ~$0.15-0.25 per run K=20 10gen,
# ~$0.40-0.55 per run esteso K=40 20gen con WFA OOS).
# Default --start ora 2018-09-01 (7.3y, copre bear+halving+covid+ATH+winter+ETF).
uv run python scripts/run_phase1.py \
--name run-XXX \
--exchange deribit --symbol BTC-PERPETUAL --timeframe 1h \
--population-size 20 --n-generations 10 \
--prompt-mutation-weight 0.30 --fitness-v2 \
--llm-concurrency 8 # 5-8x speedup wall time (default 1)
# fitness-v2 hardened: hard-kill su {no_trades, degenerate, undertrading,
# fees_eat_alpha, negative_net_pnl}. Override via --fitness-hard-kill CSV.
# Default --prompt-library: importlib.resources del package strategy_crypto/prompts.json
# Multi-fold validation di un run esistente (anti-overfit, 7y expanding-window)
uv run python scripts/validate_run.py \
--run-id <run_id> \
--top-k 10 --n-folds 4 --train-ratio 0.5 \
--start 2018-09-01T00:00:00+00:00 --end 2026-01-01T00:00:00+00:00 \
--fitness-v2 \
--output-json state/validation-XXX.json
# Ranking per "robust_score" = min(fitness_oos) su tutti i fold.
# Backtest standalone di una strategia JSON
uv run python scripts/backtest_strategy.py \
--strategy src/strategy_crypto/strategy_crypto/strategies/btc_fb63e851.json \
--start 2018-09-01 --end 2026-01-01
# Paper-trading forward-test (Phase 3)
uv run python scripts/run_paper_trading.py \
--name phase3-papertrade-XXX \
--initial-capital 1000 --poll-seconds 300
# Dashboard NiceGUI locale (2 distinte)
uv run python -m multi_swarm_core.dashboard.nicegui_app # GA core (/, /convergence, /genomes)
uv run python -m strategy_crypto.frontend.nicegui_app # Strategy crypto (/ paper)
```
## Performance & Validation
**Backtest engine vettorializzato** (`backtest/engine.py`): rimosso il loop `pd.iterrows()` a favore di state machine numpy. Speedup misurati:
| Dataset | Before (iterrows) | After (vectorized) | Speedup |
|---------|-------------------|--------------------|---------|
| 2 anni (17545 bar) | 470 ms | **28 ms** | **16.8×** |
| 7 anni (64297 bar) | 1744 ms | **154 ms** | **11.3×** |
Equivalenza numerica garantita: 5 parity test parametrici vs. reference implementation legacy (`test_backtest_engine_vectorized.py`).
**Parallel propose LLM** (`orchestrator/run.py`): `--llm-concurrency N` lancia N chiamate `hypothesis_agent.propose()` concorrenti per generazione tramite `ThreadPoolExecutor`. OpenRouter qwen-2.5 regge 6-10 concorrenti senza rate-limit. Default 1 = backward-compat.
**Multi-fold validation tool** (`scripts/validate_run.py`): qualunque run completato puo' essere rivalutato post-hoc su N fold expanding-window di un dataset esteso (tipicamente 7 anni). Vital per evitare il single-hold-out overfit: il GA puo' selezionare un genome con `fitness_is` alta che collassa OOS (osservato su `phase1-extended-001`: elite IS Sharpe 1.93, OOS Sharpe -1.00). Ranking finale per `robust_score = min(fitness_oos)`. Output JSON con per-fold breakdown + aggregati mean/min/std.
## Dashboard (split core + strategy)
Due NiceGUI dashboard distinte (dark palette, palette neon):
**Core GA**`multi_swarm_core.dashboard.nicegui_app``https://swarm.tielogic.xyz/multi_swarm_core_gui/`:
- **Overview** (`/`): lista runs GA, costo cumulato, metriche aggregate evaluations.
- **GA Convergence** (`/convergence`): fitness median/max/p90 per generazione + entropy.
- **Genomes** (`/genomes`): top-K ordinati per fitness, ispezione system_prompt + JSON strategy.
**Strategy crypto**`strategy_crypto.frontend.nicegui_app``https://swarm.tielogic.xyz/strategy_crypto_gui/`:
- **Paper** (`/`): forward-test live con equity curve, posizioni aperte, trade list, tick log.
In produzione subpath gestiti via `DASHBOARD_ROOT_PATH` (per-servizio) + Traefik `replacepathregex` (NB: NON `stripprefix`, vedi sezione Deploy). La root del dominio resta libera per future GUI di altre strategie.
## Deploy
`docker-compose.yml` definisce 3 servizi su immagine condivisa `multi-swarm-coevolutive:dev`:
- **`strategy-crypto-paper`** — runner `scripts/run_paper_trading.py` long-running.
- **`strategy-crypto-gui`** — NiceGUI paper dashboard su `https://swarm.${DOMAIN_NAME}/strategy_crypto_gui/`.
- **`multi-swarm-core-gui`** — NiceGUI GA dashboard su `https://swarm.${DOMAIN_NAME}/multi_swarm_core_gui/`.
Persistenza via bind mount: `./data/`, `./series/`, `./state/`. Strategie JSON bind-mounted in read-only dal package: `./src/strategy_crypto/strategy_crypto/strategies/`.
```bash
docker compose up -d --build
docker compose logs -f strategy-crypto-paper
docker compose ps
```
Note operative:
- Bind-mount dir devono essere `chown 1000:1000` (uid utente `app` nel container). **Anche `src/strategy_crypto/strategy_crypto/strategies/`** (creata da `git mv`, default `root:root`).
- Override del command paper via env (`PAPER_RUN_NAME`, `PAPER_INITIAL_CAPITAL`, ecc.).
- `SWARM_DASHBOARD_PORT` controlla la porta interna del container (Traefik fa il TLS).
- **Traefik subpath**: usa `replacepathregex` middleware (NON `stripprefix`) per evitare doppio root_path (uvicorn legge `X-Forwarded-Prefix` da stripprefix + applica `root_path` di NiceGUI = doppio prefix). Vedi commit `436613b`.
- Dopo cambio label Traefik: `docker restart traefik-traefik-1` per forzare refresh discovery.
## Sviluppo
Conventional commits con prefix `feat:` `fix:` `chore:` `docs:` `refactor:` `test:`. Body italiano. Footer `Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>` su ogni commit collaborativo.
Branch attuale: `main`. Workspace single-repo, monorepo unificato dal 15 maggio 2026 (split temporaneo monorepo→figlio invertito, vedi tag `v0.1.0-pre-split` come bookmark).
**Modificare il prompt LLM** senza toccare codice: edita `src/strategy_crypto/strategy_crypto/prompts.json`. Schema documentato in `_design_invariants` del JSON stesso. I 3 regression guard test (`test_strategy_crypto_directives_ascii_safe`, `..._have_archetype_marker`, `..._have_lookback_hint`) bloccano regressioni accidentali sulle invarianti di design.