from __future__ import annotations import argparse from datetime import datetime from multi_swarm.cerbero.client import CerberoClient from multi_swarm.config import load_settings from multi_swarm.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest from multi_swarm.genome.hypothesis import ModelTier from multi_swarm.llm.client import LLMClient from multi_swarm.orchestrator.run import RunConfig, run_phase1 def parse_args() -> argparse.Namespace: p = argparse.ArgumentParser(description="Multi-Swarm Phase 1 runner") p.add_argument("--name", default="phase1-spike-001") p.add_argument("--population-size", type=int, default=20) p.add_argument("--n-generations", type=int, default=10) p.add_argument("--elite-k", type=int, default=2) p.add_argument("--tournament-k", type=int, default=3) p.add_argument("--p-crossover", type=float, default=0.5) p.add_argument("--seed", type=int, default=42) p.add_argument( "--exchange", default="deribit", choices=["deribit", "bybit", "hyperliquid"], ) p.add_argument("--symbol", default="BTC-PERPETUAL") p.add_argument("--timeframe", default="1h") p.add_argument("--start", default="2024-01-01T00:00:00+00:00") p.add_argument("--end", default="2026-01-01T00:00:00+00:00") p.add_argument("--fees-bp", type=float, default=5.0) p.add_argument("--n-trials-dsr", type=int, default=50) p.add_argument( "--prompt-mutation-weight", type=float, default=0.0, help="Phase 2.5: probabilità (0-1) che la mutazione invochi LLM mutator tier B", ) p.add_argument( "--fees-eat-alpha-threshold", type=float, default=0.5, help="Adversarial gate: kill se fees/gross_pnl > soglia (default 0.5, ablation 0.7)", ) p.add_argument( "--flat-too-long-threshold", type=float, default=0.95, help="Adversarial gate: kill se signal flat > soglia delle bar (default 0.95, ablation 0.98)", ) p.add_argument( "--undertrading-threshold", type=int, default=10, help="Adversarial: kill se n_trades < soglia (default 10, bump per filtrare lucky-shot)", ) p.add_argument( "--fitness-v2", action="store_true", help=( "Attiva fitness v2: solo {no_trades, degenerate, undertrading} azzerano; " "gli altri HIGH applicano soft penalty multiplicativa" ), ) p.add_argument( "--fitness-soft-penalty", type=float, default=0.4, help="v2: fattore soft penalty per HIGH non-hard (default 0.4 → 1 HIGH → 0.71x)", ) p.add_argument( "--wfa-train-split", type=float, default=None, help="Walk-forward: frazione bar usate per training (es. 0.7 = primi 70%% in-sample, ultimi 30%% OOS)", ) p.add_argument( "--wfa-top-k", type=int, default=5, help="Walk-forward: quanti top genomi rivalutare OOS (default 5)", ) return p.parse_args() def main() -> None: args = parse_args() settings = load_settings() 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) req = OHLCVRequest( symbol=args.symbol, timeframe=args.timeframe, start=datetime.fromisoformat(args.start), end=datetime.fromisoformat(args.end), exchange=args.exchange, ) ohlcv = loader.load(req) print(f"OHLCV loaded: {len(ohlcv)} bars from {ohlcv.index[0]} to {ohlcv.index[-1]}") llm = LLMClient( openrouter_api_key=settings.openrouter_api_key.get_secret_value(), model_tier_s=settings.llm_model_tier_s, model_tier_a=settings.llm_model_tier_a, model_tier_b=settings.llm_model_tier_b, model_tier_c=settings.llm_model_tier_c, model_tier_d=settings.llm_model_tier_d, openrouter_base_url=settings.openrouter_base_url, ) cfg = RunConfig( run_name=args.name, population_size=args.population_size, n_generations=args.n_generations, elite_k=args.elite_k, tournament_k=args.tournament_k, p_crossover=args.p_crossover, seed=args.seed, model_tier=ModelTier.C, symbol=args.symbol, timeframe=args.timeframe, fees_bp=args.fees_bp, n_trials_dsr=args.n_trials_dsr, db_path=settings.db_path, prompt_mutation_weight=args.prompt_mutation_weight, fees_eat_alpha_threshold=args.fees_eat_alpha_threshold, flat_too_long_threshold=args.flat_too_long_threshold, undertrading_threshold=args.undertrading_threshold, fitness_hard_kill_findings=( ("no_trades", "degenerate", "undertrading") if args.fitness_v2 else None ), fitness_adversarial_soft_penalty=args.fitness_soft_penalty, wfa_train_split=args.wfa_train_split, wfa_top_k=args.wfa_top_k, ) run_id = run_phase1(cfg, ohlcv=ohlcv, llm=llm) print(f"Run completed: {run_id}") if __name__ == "__main__": main()