from __future__ import annotations import json import sqlite3 from pathlib import Path from typing import Any import pandas as pd # type: ignore[import-untyped] from ..persistence.repository import Repository def get_repo(db_path: str | Path) -> Repository: return Repository(db_path=db_path) def list_runs_df(repo: Repository) -> pd.DataFrame: return pd.DataFrame(repo.list_runs()) def get_run_overview(repo: Repository, run_id: str) -> dict[str, Any]: run = repo.get_run(run_id) return { "name": run["name"], "started_at": run["started_at"], "completed_at": run["completed_at"], "status": run["status"], "total_cost_usd": run["total_cost_usd"], "config": json.loads(run["config_json"]), } def generations_df(repo: Repository, run_id: str) -> pd.DataFrame: return pd.DataFrame(repo.list_generations(run_id)) def evaluations_df(repo: Repository, run_id: str) -> pd.DataFrame: return pd.DataFrame(repo.list_evaluations(run_id)) def genomes_df( repo: Repository, run_id: str, generation_idx: int | None = None ) -> pd.DataFrame: rows = repo.list_genomes(run_id, generation_idx) flat: list[dict[str, Any]] = [] for r in rows: payload = json.loads(r["payload_json"]) flat.append( { "id": r["id"], "generation_idx": r["generation_idx"], **payload, } ) return pd.DataFrame(flat) def _paper_conn(db_path: str | Path) -> sqlite3.Connection: conn = sqlite3.connect(str(db_path)) conn.row_factory = sqlite3.Row return conn def paper_runs_df(db_path: str | Path) -> pd.DataFrame: with _paper_conn(db_path) as conn: rows = conn.execute( "SELECT id, name, started_at, stopped_at, status, initial_capital, config_json " "FROM paper_trading_runs ORDER BY started_at DESC" ).fetchall() return pd.DataFrame([dict(r) for r in rows]) def paper_equity_df(db_path: str | Path, run_id: str) -> pd.DataFrame: with _paper_conn(db_path) as conn: rows = conn.execute( "SELECT ts, equity, cash, positions_value FROM paper_trading_equity " "WHERE paper_run_id=? ORDER BY ts ASC", (run_id,), ).fetchall() return pd.DataFrame([dict(r) for r in rows]) def paper_positions_df(db_path: str | Path, run_id: str) -> pd.DataFrame: with _paper_conn(db_path) as conn: rows = conn.execute( "SELECT symbol, side, qty, entry_price, entry_ts " "FROM paper_trading_positions WHERE paper_run_id=? ORDER BY symbol", (run_id,), ).fetchall() return pd.DataFrame([dict(r) for r in rows]) def paper_trades_df(db_path: str | Path, run_id: str, limit: int = 100) -> pd.DataFrame: with _paper_conn(db_path) as conn: rows = conn.execute( "SELECT symbol, side, qty, entry_price, exit_price, entry_ts, exit_ts, pnl, fees " "FROM paper_trading_trades WHERE paper_run_id=? ORDER BY exit_ts DESC LIMIT ?", (run_id, limit), ).fetchall() return pd.DataFrame([dict(r) for r in rows]) def paper_ticks_df(db_path: str | Path, run_id: str, limit: int = 50) -> pd.DataFrame: with _paper_conn(db_path) as conn: rows = conn.execute( "SELECT ts, bar_ts, symbol, close_price, signal, action_taken " "FROM paper_trading_ticks WHERE paper_run_id=? ORDER BY ts DESC LIMIT ?", (run_id, limit), ).fetchall() return pd.DataFrame([dict(r) for r in rows]) def paper_run_summary(db_path: str | Path, run_id: str) -> dict[str, Any]: """Aggrega metriche sintetiche per la pagina paper trading.""" with _paper_conn(db_path) as conn: run = conn.execute( "SELECT id, name, started_at, stopped_at, status, initial_capital, config_json " "FROM paper_trading_runs WHERE id=?", (run_id,), ).fetchone() if run is None: return {} run = dict(run) eq_row = conn.execute( "SELECT equity, cash, positions_value, ts FROM paper_trading_equity " "WHERE paper_run_id=? ORDER BY ts DESC LIMIT 1", (run_id,), ).fetchone() trades_agg = conn.execute( "SELECT COUNT(*) AS n, COALESCE(SUM(pnl),0) AS sum_pnl, " "COALESCE(SUM(fees),0) AS sum_fees FROM paper_trading_trades " "WHERE paper_run_id=?", (run_id,), ).fetchone() tick_agg = conn.execute( "SELECT COUNT(*) AS n, MAX(ts) AS last_ts FROM paper_trading_ticks " "WHERE paper_run_id=?", (run_id,), ).fetchone() positions_n = conn.execute( "SELECT COUNT(*) AS n FROM paper_trading_positions WHERE paper_run_id=?", (run_id,), ).fetchone()["n"] initial = float(run["initial_capital"]) current_equity = float(eq_row["equity"]) if eq_row is not None else initial pnl_pct = (current_equity - initial) / initial * 100.0 if initial else 0.0 return { "id": run["id"], "name": run["name"], "status": run["status"], "started_at": run["started_at"], "stopped_at": run["stopped_at"], "initial_capital": initial, "config": json.loads(run["config_json"]), "current_equity": current_equity, "current_cash": float(eq_row["cash"]) if eq_row is not None else initial, "current_positions_value": float(eq_row["positions_value"]) if eq_row is not None else 0.0, "last_equity_ts": eq_row["ts"] if eq_row is not None else None, "pnl_abs": current_equity - initial, "pnl_pct": pnl_pct, "n_trades": int(trades_agg["n"]), "trades_pnl": float(trades_agg["sum_pnl"]), "trades_fees": float(trades_agg["sum_fees"]), "n_ticks": int(tick_agg["n"]), "last_tick_ts": tick_agg["last_ts"], "n_open_positions": int(positions_n), }