Files
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

159 lines
6.6 KiB
YAML

# docker-compose.yml — Multi-Swarm Coevolutive
#
# Tre servizi della strategia crypto, condividono la stessa immagine
# `multi-swarm-coevolutive:dev` buildata dal Dockerfile root (uv workspace):
#
# * strategy-crypto-paper — paper-trading runner long-running
# (scripts/run_paper_trading.py)
# * strategy-crypto-gui — NiceGUI dashboard esposta da Traefik su
# https://swarm.${DOMAIN_NAME:-tielogic.xyz}/strategy_crypto_gui
# * multi-swarm-core-gui — NiceGUI dashboard GA esposta su
# https://swarm.${DOMAIN_NAME:-tielogic.xyz}/multi_swarm_core_gui
#
# Entrambi joinano la rete external `traefik` cosi' il client Cerbero
# risolve direttamente l'host `cerbero-mcp` (porta 9000) senza passare
# dal gateway pubblico ne' dal TLS.
#
# Pattern N strategie future: aggiungere strategy-<asset>-paper + strategy-<asset>-gui
# con PathPrefix(/strategy_<asset>_gui) e DASHBOARD_ROOT_PATH dedicato.
#
# Dati persistenti via bind mount dalla cartella del repo:
# ./data cache OHLCV intermedia
# ./series cache parquet per timeframe/symbol
# ./state contiene runs.db (GA) + strategy_crypto.db (paper) + WAL/SHM
# ./src/strategy_crypto/strategy_crypto/strategies JSON freezate (ro)
#
# Secrets (token Cerbero + OpenRouter): caricati da .env via env_file.
networks:
traefik:
external: true
x-swarm-env: &swarm-env
# Override: rotta interna verso cerbero-mcp (no TLS, no traefik hop)
CERBERO_BASE_URL: http://cerbero-mcp:9000
# Override: path container per persistenza
DATA_DIR: /app/data
SERIES_DIR: /app/series
# DB separati per dominio:
GA_DB_PATH: /app/state/runs.db
STRATEGY_CRYPTO_DB_PATH: /app/state/strategy_crypto.db
# DASHBOARD_ROOT_PATH e' ora per-servizio (vedi environment blocks sotto).
# IMPORTANT: NON usare StripPrefix middleware con questo. NiceGUI/Starlette
# gestisce internamente il root_path su request path che ARRIVANO con prefix.
# StripPrefix causa doppio prefix negli asset URL (NiceGUI prefixa + uvicorn
# rilegge X-Forwarded-Prefix e prefixa di nuovo).
services:
strategy-crypto-paper:
build:
context: .
dockerfile: Dockerfile
image: multi-swarm-coevolutive:dev
container_name: strategy-crypto-paper
restart: unless-stopped
networks: [traefik]
env_file: .env
environment:
<<: *swarm-env
volumes:
- ./data:/app/data
- ./series:/app/series
- ./state:/app/state
- ./src/strategy_crypto/strategy_crypto/strategies:/app/strategies:ro
# Niente HTTP da controllare: ci affidiamo a `restart: unless-stopped`
# e ai log per la liveness del runner.
command:
- python
- /app/scripts/run_paper_trading.py
- --name=${PAPER_RUN_NAME:-phase3-papertrade-prod}
- --initial-capital=${PAPER_INITIAL_CAPITAL:-1000}
- --fees-bp=${PAPER_FEES_BP:-5.0}
- --poll-seconds=${PAPER_POLL_SECONDS:-300}
- --lookback-bars=${PAPER_LOOKBACK_BARS:-500}
- --strategies-dir=/app/strategies
labels:
- com.centurylinklabs.watchtower.enable=true
strategy-crypto-gui:
image: multi-swarm-coevolutive:dev
build:
context: .
dockerfile: Dockerfile
container_name: strategy-crypto-gui
restart: unless-stopped
networks: [traefik]
env_file: .env
environment:
<<: *swarm-env
DASHBOARD_ROOT_PATH: /strategy_crypto_gui
volumes:
# Dashboard legge solo strategy_crypto.db: state/ in read-only (WAL: vedi nota)
- ./state:/app/state:ro
entrypoint:
- python
- -m
- strategy_crypto.frontend.nicegui_app
command: []
healthcheck:
test:
- "CMD"
- "python"
- "-c"
- "import os, urllib.request; urllib.request.urlopen(f'http://localhost:{os.environ.get(\"SWARM_DASHBOARD_PORT\",\"8080\")}/', timeout=3).close()"
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
labels:
- traefik.enable=true
- traefik.docker.network=traefik
- "traefik.http.routers.strategy-crypto-gui.rule=Host(`swarm.${DOMAIN_NAME:-tielogic.xyz}`) && PathPrefix(`/strategy_crypto_gui`)"
- traefik.http.routers.strategy-crypto-gui.tls=true
- traefik.http.routers.strategy-crypto-gui.entrypoints=websecure
- traefik.http.routers.strategy-crypto-gui.tls.certresolver=mytlschallenge
- "traefik.http.services.strategy-crypto-gui.loadbalancer.server.port=${SWARM_DASHBOARD_PORT:-8080}"
# replacepathregex (NON stripprefix): strippa il prefix dalla request senza
# aggiungere X-Forwarded-Prefix header. uvicorn vede solo root_path da
# ui.run(root_path=...), quindi gli asset URL vengono prefissati una sola
# volta (no doppio prefix come succederebbe con stripprefix).
- "traefik.http.middlewares.strategy-crypto-replace.replacepathregex.regex=^/strategy_crypto_gui(/.*|$$)"
- "traefik.http.middlewares.strategy-crypto-replace.replacepathregex.replacement=$$1"
- "traefik.http.routers.strategy-crypto-gui.middlewares=strategy-crypto-replace"
- com.centurylinklabs.watchtower.enable=true
multi-swarm-core-gui:
image: multi-swarm-coevolutive:dev
build:
context: .
dockerfile: Dockerfile
container_name: multi-swarm-core-gui
restart: unless-stopped
networks: [traefik]
env_file: .env
environment:
<<: *swarm-env
DASHBOARD_ROOT_PATH: /multi_swarm_core_gui
volumes:
- ./state:/app/state:ro
entrypoint: [python, -m, multi_swarm_core.dashboard.nicegui_app]
command: []
healthcheck:
test: ["CMD", "python", "-c", "import os, urllib.request; urllib.request.urlopen(f'http://localhost:{os.environ.get(\"SWARM_DASHBOARD_PORT\",\"8080\")}/', timeout=3).close()"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
labels:
- traefik.enable=true
- traefik.docker.network=traefik
- "traefik.http.routers.multi-swarm-core-gui.rule=Host(`swarm.${DOMAIN_NAME:-tielogic.xyz}`) && PathPrefix(`/multi_swarm_core_gui`)"
- traefik.http.routers.multi-swarm-core-gui.tls=true
- traefik.http.routers.multi-swarm-core-gui.entrypoints=websecure
- traefik.http.routers.multi-swarm-core-gui.tls.certresolver=mytlschallenge
- "traefik.http.services.multi-swarm-core-gui.loadbalancer.server.port=${SWARM_DASHBOARD_PORT:-8080}"
- "traefik.http.middlewares.multi-swarm-core-replace.replacepathregex.regex=^/multi_swarm_core_gui(/.*|$$)"
- "traefik.http.middlewares.multi-swarm-core-replace.replacepathregex.replacement=$$1"
- "traefik.http.routers.multi-swarm-core-gui.middlewares=multi-swarm-core-replace"
- com.centurylinklabs.watchtower.enable=true