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>
This commit is contained in:
Adriano Dal Pastro
2026-05-15 17:56:55 +00:00
parent b02be64831
commit 37bf64012b
3 changed files with 31 additions and 20 deletions
@@ -7,7 +7,7 @@ from typing import Any
import pandas as pd # type: ignore[import-untyped] import pandas as pd # type: ignore[import-untyped]
from ..persistence.repository import Repository from multi_swarm_core.persistence.repository import Repository
def get_repo(db_path: str | Path) -> Repository: def get_repo(db_path: str | Path) -> Repository:
@@ -1,6 +1,6 @@
"""NiceGUI dashboard — port progressivo da Streamlit. """NiceGUI dashboard — port progressivo da Streamlit.
Avvio: ``uv run python -m multi_swarm_core.dashboard.nicegui_app`` Avvio: ``uv run python -m strategy_crypto.frontend.nicegui_app``
Default port 8080. Streamlit resta su 8501 durante la migrazione. Default port 8080. Streamlit resta su 8501 durante la migrazione.
Riusa ``dashboard.data`` (Repository helpers) senza modifiche al backend. Riusa ``dashboard.data`` (Repository helpers) senza modifiche al backend.
@@ -28,7 +28,7 @@ import pandas as pd # type: ignore[import-untyped]
import plotly.graph_objects as go # type: ignore[import-untyped] import plotly.graph_objects as go # type: ignore[import-untyped]
from nicegui import app, ui from nicegui import app, ui
from multi_swarm_core.dashboard.data import ( from strategy_crypto.frontend.data import (
evaluations_df, evaluations_df,
generations_df, generations_df,
genomes_df, genomes_df,
@@ -43,7 +43,11 @@ from multi_swarm_core.dashboard.data import (
paper_trades_df, paper_trades_df,
) )
DB_PATH = os.environ.get("DB_PATH", "./runs.db") # Dual-DB: GA core e paper strategy_crypto vivono in DB separati.
GA_DB_PATH = os.environ.get("GA_DB_PATH", "./state/runs.db")
PAPER_DB_PATH = os.environ.get("STRATEGY_CRYPTO_DB_PATH", "./state/strategy_crypto.db")
# Subpath per Traefik: "" in dev, "/strategy_crypto_gui" in prod.
DASHBOARD_ROOT_PATH = os.environ.get("DASHBOARD_ROOT_PATH", "")
REFRESH_INTERVAL_S = 3.0 REFRESH_INTERVAL_S = 3.0
# --- Neon Trading Dashboard palette --- # --- Neon Trading Dashboard palette ---
@@ -384,11 +388,11 @@ def _build_header(active: str) -> None:
with ui.row().classes("items-center gap-3"): with ui.row().classes("items-center gap-3"):
ui.html(f'<span style="color:{COLOR_TEXT_MUTED};font-size:12px;' ui.html(f'<span style="color:{COLOR_TEXT_MUTED};font-size:12px;'
f'font-family:JetBrains Mono,monospace;">' f'font-family:JetBrains Mono,monospace;">'
f'{Path(DB_PATH).resolve().name}</span>') f'{Path(GA_DB_PATH).resolve().name} + {Path(PAPER_DB_PATH).resolve().name}</span>')
def _runs_options() -> dict[str, str]: def _runs_options() -> dict[str, str]:
repo = get_repo(DB_PATH) repo = get_repo(GA_DB_PATH)
runs = list_runs_df(repo) runs = list_runs_df(repo)
if runs.empty: if runs.empty:
return {} return {}
@@ -399,7 +403,7 @@ def _runs_options() -> dict[str, str]:
def _snapshot(run_id: str) -> dict[str, Any]: def _snapshot(run_id: str) -> dict[str, Any]:
repo = get_repo(DB_PATH) repo = get_repo(GA_DB_PATH)
ov = get_run_overview(repo, run_id) ov = get_run_overview(repo, run_id)
evals = evaluations_df(repo, run_id) evals = evaluations_df(repo, run_id)
gens = generations_df(repo, run_id) gens = generations_df(repo, run_id)
@@ -632,8 +636,8 @@ def convergence() -> None:
) )
ui.button("🔄 Refresh", on_click=lambda: refresh()).props("outline color=primary") ui.button("🔄 Refresh", on_click=lambda: refresh()).props("outline color=primary")
fitness_plot = ui.plotly(_convergence_figure(generations_df(get_repo(DB_PATH), state["run_id"]))).classes("w-full") fitness_plot = ui.plotly(_convergence_figure(generations_df(get_repo(GA_DB_PATH), state["run_id"]))).classes("w-full")
entropy_plot = ui.plotly(_entropy_figure(generations_df(get_repo(DB_PATH), state["run_id"]))).classes("w-full q-mt-md") entropy_plot = ui.plotly(_entropy_figure(generations_df(get_repo(GA_DB_PATH), state["run_id"]))).classes("w-full q-mt-md")
ui.separator() ui.separator()
ui.label("Tabella generazioni").classes("text-subtitle1 q-mt-md") ui.label("Tabella generazioni").classes("text-subtitle1 q-mt-md")
@@ -656,8 +660,8 @@ def convergence() -> None:
if not run_id: if not run_id:
return return
try: try:
gens = generations_df(get_repo(DB_PATH), run_id) gens = generations_df(get_repo(GA_DB_PATH), run_id)
ov = get_run_overview(get_repo(DB_PATH), run_id) ov = get_run_overview(get_repo(GA_DB_PATH), run_id)
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001
ui.notify(f"Errore: {e}", type="negative") ui.notify(f"Errore: {e}", type="negative")
return return
@@ -801,7 +805,7 @@ def genomes() -> None:
if not run_id: if not run_id:
return return
try: try:
repo = get_repo(DB_PATH) repo = get_repo(GA_DB_PATH)
evals = evaluations_df(repo, run_id) evals = evaluations_df(repo, run_id)
gens = genomes_df(repo, run_id) gens = genomes_df(repo, run_id)
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001
@@ -874,7 +878,7 @@ def genomes() -> None:
def _paper_runs_options(only_running: bool = False) -> dict[str, str]: def _paper_runs_options(only_running: bool = False) -> dict[str, str]:
runs = paper_runs_df(DB_PATH) runs = paper_runs_df(PAPER_DB_PATH)
if runs.empty: if runs.empty:
return {} return {}
if only_running: if only_running:
@@ -1014,11 +1018,11 @@ def paper() -> None:
if not run_id: if not run_id:
return return
try: try:
summary = paper_run_summary(DB_PATH, run_id) summary = paper_run_summary(PAPER_DB_PATH, run_id)
eq = paper_equity_df(DB_PATH, run_id) eq = paper_equity_df(PAPER_DB_PATH, run_id)
positions = paper_positions_df(DB_PATH, run_id) positions = paper_positions_df(PAPER_DB_PATH, run_id)
ticks = paper_ticks_df(DB_PATH, run_id, limit=30) ticks = paper_ticks_df(PAPER_DB_PATH, run_id, limit=30)
trades = paper_trades_df(DB_PATH, run_id, limit=50) trades = paper_trades_df(PAPER_DB_PATH, run_id, limit=50)
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001
ui.notify(f"Errore: {e}", type="negative") ui.notify(f"Errore: {e}", type="negative")
return return
@@ -1081,14 +1085,21 @@ def paper() -> None:
def main() -> None: def main() -> None:
app.on_startup(lambda: print(f"DB: {Path(DB_PATH).resolve()}")) app.on_startup(
lambda: print(
f"GA DB: {Path(GA_DB_PATH).resolve()} | "
f"Paper DB: {Path(PAPER_DB_PATH).resolve()} | "
f"root_path: {DASHBOARD_ROOT_PATH or '/'}"
)
)
ui.run( ui.run(
host="0.0.0.0", host="0.0.0.0",
port=int(os.environ.get("SWARM_DASHBOARD_PORT", "8080")), port=int(os.environ.get("SWARM_DASHBOARD_PORT", "8080")),
title="Multi-Swarm Dashboard", title="Strategy Crypto Dashboard",
reload=False, reload=False,
show=False, show=False,
dark=True, dark=True,
root_path=DASHBOARD_ROOT_PATH,
) )