#!/usr/bin/env python3 """ Multi-stock backtesting script for strategy evolution. Tests one or more strategies across multiple symbols and timeframes. """ import sys import os from datetime import datetime, timedelta import pandas as pd import numpy as np import logging # Add project root to path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from trading_cli.backtest.engine import BacktestEngine from trading_cli.strategy.strategy_factory import create_trading_strategy, available_strategies from trading_cli.data.market import fetch_ohlcv_yfinance # Configure logging logging.basicConfig(level=logging.WARNING) logger = logging.getLogger(__name__) DEFAULT_SYMBOLS = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "NVDA", "META", "AMD", "COIN", "MARA"] DEFAULT_DAYS = 365 def run_multi_backtest(symbols, strategy_ids, days=DEFAULT_DAYS, config=None): if config is None: config = { "signal_buy_threshold": 0.2, "signal_sell_threshold": -0.15, "risk_pct": 0.02, "stop_loss_pct": 0.05, } results = [] print(f"{'Symbol':<8} | {'Strategy':<15} | {'Return %':>10} | {'Sharpe':>8} | {'Win%':>6} | {'Trades':>6}") print("-" * 70) for symbol in symbols: # Fetch data once per symbol ohlcv = fetch_ohlcv_yfinance(symbol, days=days) if ohlcv.empty: print(f"Failed to fetch data for {symbol}") continue for strategy_id in strategy_ids: # Create strategy strat_config = config.copy() strat_config["strategy_id"] = strategy_id strategy = create_trading_strategy(strat_config) # Run backtest engine = BacktestEngine( config=strat_config, use_sentiment=False, # Skip sentiment for pure technical baseline strategy=strategy ) res = engine.run(symbol, ohlcv, initial_capital=100_000.0) print(f"{symbol:<8} | {strategy_id:<15} | {res.total_return_pct:>9.2f}% | {res.sharpe_ratio:>8.2f} | {res.win_rate:>5.1f}% | {res.total_trades:>6}") results.append({ "symbol": symbol, "strategy": strategy_id, "return_pct": res.total_return_pct, "sharpe": res.sharpe_ratio, "win_rate": res.win_rate, "trades": res.total_trades, "max_drawdown": res.max_drawdown_pct }) # Aggregate results by strategy df = pd.DataFrame(results) if not df.empty: summary = df.groupby("strategy").agg({ "return_pct": ["mean", "std"], "sharpe": "mean", "win_rate": "mean", "trades": "sum" }) print("\n--- Summary ---") print(summary) return df if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("--symbols", nargs="+", default=DEFAULT_SYMBOLS) parser.add_argument("--strategies", nargs="+", default=["hybrid", "mean_reversion", "momentum", "trend_following"]) parser.add_argument("--days", type=int, default=DEFAULT_DAYS) args = parser.parse_args() run_multi_backtest(args.symbols, args.strategies, args.days)