"""Confronto per-trade dei 4 winner cross-run (BTC/ETH × 1h/5m). Per ogni winner: ri-esegue il backtest su 4 fold WFA expanding-window e raccoglie trade buoni/non buoni, win-rate, avg PnL, return, max DD, Sharpe. """ from __future__ import annotations import argparse from datetime import datetime import pandas as pd # type: ignore[import-untyped] from multi_swarm_core.agents.hypothesis import _try_parse from multi_swarm_core.backtest.engine import BacktestEngine from multi_swarm_core.cerbero.client import CerberoClient from multi_swarm_core.config import load_settings from multi_swarm_core.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest from multi_swarm_core.data.splits import expanding_walk_forward from multi_swarm_core.metrics.basic import max_drawdown, sharpe_ratio, total_return from multi_swarm_core.persistence.repository import Repository from multi_swarm_core.protocol.compiler import compile_strategy # (run_name, genome_id, symbol, timeframe, label) WINNERS = [ ("phase1-btc-100-001", "238e481262c1594c", "BTC-PERPETUAL", "1h", "BTC 1h sharpshooter (Gen 7)"), ("phase1-btc-100-001", "23a24989e2ed0f84", "BTC-PERPETUAL", "1h", "BTC 1h robust (Gen 0 elite)"), ("phase1-eth-100-001", "4b45a72c13acf1d5", "ETH-PERPETUAL", "1h", "ETH 1h best-by-sharpe (killed)"), ("phase1-btc-100-5m-001", "f8ca6642adf7e0cd", "BTC-PERPETUAL", "5m", "BTC 5m robust winner"), ("phase1-eth-100-5m-001", "c04dff7086bb9588", "ETH-PERPETUAL", "5m", "ETH 5m OOS winner"), ] def analyze_genome(run_id: str, genome_id: str, symbol: str, timeframe: str, label: str, settings, cerbero, loader) -> None: repo = Repository(settings.ga_db_path) repo.init_schema() evs = [e for e in repo.list_evaluations(run_id) if e["genome_id"] == genome_id] if not evs: print(f" no eval for {genome_id} in {run_id}") return ev = evs[0] strat, err = _try_parse(ev.get("raw_text") or "") if strat is None: print(f" parse error: {err}") return req = OHLCVRequest( symbol=symbol, timeframe=timeframe, start=datetime.fromisoformat("2018-09-01T00:00:00+00:00"), end=datetime.fromisoformat("2026-01-01T00:00:00+00:00"), ) ohlcv = loader.load(req) splits = expanding_walk_forward(ohlcv.index, train_ratio=0.5, n_folds=4) engine = BacktestEngine(fees_bp=5.0) print(f"\n>>> {label}") print(f" {genome_id} | fit_IS={ev['fitness']:.4f} sharpe_IS={ev['sharpe']:.3f} trades_IS={ev['n_trades']}") print(f" {'fold':<5} {'period':<26} {'trades':>7} {'wins':>5} {'losses':>7} {'win%':>6} {'avg_w':>10} {'avg_l':>10} {'ret':>8} {'maxDD':>7} {'sharpe':>7}") sum_ret = 0.0 sum_trades = 0 sum_wins = 0 for s in splits: test_df = ohlcv.loc[s.test_idx] try: signal_fn = compile_strategy(strat) signals = signal_fn(test_df) bt = engine.run(test_df, signals) except Exception as e: print(f" fold {s.fold}: error {e}") continue trades = bt.trades n = len(trades) wins = [t.net_pnl for t in trades if t.net_pnl > 0] losses = [t.net_pnl for t in trades if t.net_pnl <= 0] nw, nl = len(wins), len(losses) wr = (nw / n * 100) if n else 0.0 aw = (sum(wins) / nw) if nw else 0.0 al = (sum(losses) / nl) if nl else 0.0 if n > 0: notional = float(test_df["close"].iloc[0]) eq = (bt.equity_curve / notional) + 1.0 ret = total_return(eq) dd = max_drawdown(eq) sr = sharpe_ratio(bt.returns, periods_per_year=8760) else: ret = dd = sr = 0.0 period = f"{str(s.test_idx[0])[:10]}..{str(s.test_idx[-1])[:10]}" print(f" {s.fold:<5} {period:<26} {n:>7} {nw:>5} {nl:>7} {wr:>5.1f}% {aw:>10.1f} {al:>10.1f} {ret:>7.2%} {dd:>6.2%} {sr:>7.3f}") sum_ret += ret sum_trades += n sum_wins += nw overall_wr = (sum_wins / sum_trades * 100) if sum_trades else 0.0 print(f" {'='*5} TOTALS: {sum_trades:>7} {sum_wins:>5} {sum_trades-sum_wins:>7} {overall_wr:>5.1f}% cum_ret={sum_ret:+.2%}") def main() -> None: settings = load_settings() repo = Repository(settings.ga_db_path) repo.init_schema() name_to_id: dict[str, str] = {} for w in WINNERS: run_name = w[0] if run_name in name_to_id: continue runs = repo.list_runs() for r in runs: if r["name"] == run_name: name_to_id[run_name] = r["id"] break token = ( settings.cerbero_mainnet_token.get_secret_value() if settings.cerbero_mainnet_token else settings.cerbero_testnet_token.get_secret_value() ) cerbero = CerberoClient( base_url=settings.cerbero_base_url, token=token, bot_tag=settings.cerbero_bot_tag, ) loader = CerberoOHLCVLoader(client=cerbero, cache_dir=settings.series_dir) print(f"{'='*120}") print(f"PER-TRADE COMPARISON — {len(WINNERS)} winner candidates × 4 folds WFA") print(f"{'='*120}") for run_name, genome_id, symbol, timeframe, label in WINNERS: run_id = name_to_id.get(run_name) if not run_id: print(f"!!! run not found: {run_name}") continue analyze_genome(run_id, genome_id, symbol, timeframe, label, settings, cerbero, loader) if __name__ == "__main__": main()