Adriano Dal Pastro 23b7273e71 feat(paper): ETH a tick 5m + tooling per-year/per-tick analysis
scripts/run_paper_trading.py: AssetConfig ETH ora usa timeframe="5m" invece
del default 1h. Il winner c04dff7086 e' stato trovato dal GA su dati 5m
e a 1h la strategia perde:
- ETH @ 5m (native): +359.50% cum 7y, 77% winrate, max DD/yr 19%
- ETH @ 1h (precedente): -33.03% cum 7y, 67% winrate, max DD 74%
BTC resta a 1h (winner 238e4812 native a 1h, +104% 7y, Sharpe 2+ in 3 anni).

Nuovi script di analisi:
- scripts/yearly_strategies.py: breakdown per anno (2019-2025) di 4
  strategie su tick di discovery (trade/winrate/return/maxDD/Sharpe).
- scripts/multi_tick_strategies.py: confronto cross-tick (5m/15m/1h)
  per i 2 winner correnti. Documenta la divergenza tick-paper di ETH.

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

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):

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:

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

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

# 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

# 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 GAmulti_swarm_core.dashboard.nicegui_apphttps://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 cryptostrategy_crypto.frontend.nicegui_apphttps://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/.

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.

S
Description
No description provided
Readme 2.3 MiB
Languages
Python 99.5%
Dockerfile 0.5%