#!/usr/bin/env python3 """ Run Multi-Pair Divergence Strategy backtest and compare with baseline. Compares the multi-pair strategy against the single-pair BTC/ETH regime strategy. """ import sys sys.path.insert(0, '.') from engine.backtester import Backtester from engine.data_manager import DataManager from engine.logging_config import setup_logging, get_logger from engine.reporting import Reporter from strategies.multi_pair import MultiPairDivergenceStrategy, MultiPairConfig from strategies.regime_strategy import RegimeReversionStrategy from engine.market import MarketType logger = get_logger(__name__) def run_baseline(): """Run baseline BTC/ETH regime strategy.""" logger.info("=" * 60) logger.info("BASELINE: BTC/ETH Regime Reversion Strategy") logger.info("=" * 60) dm = DataManager() bt = Backtester(dm) strategy = RegimeReversionStrategy() result = bt.run_strategy( strategy, 'okx', 'ETH-USDT', timeframe='1h', init_cash=10000 ) logger.info("Baseline Results:") logger.info(" Total Return: %.2f%%", result.portfolio.total_return() * 100) logger.info(" Total Trades: %d", result.portfolio.trades.count()) logger.info(" Win Rate: %.1f%%", result.portfolio.trades.win_rate() * 100) return result def run_multi_pair(assets: list[str] | None = None): """Run multi-pair divergence strategy.""" logger.info("=" * 60) logger.info("MULTI-PAIR: Divergence Selection Strategy") logger.info("=" * 60) dm = DataManager() bt = Backtester(dm) # Use provided assets or default if assets: config = MultiPairConfig(assets=assets) else: config = MultiPairConfig() logger.info("Configured %d assets, %d pairs", len(config.assets), config.get_pair_count()) strategy = MultiPairDivergenceStrategy(config=config) result = bt.run_strategy( strategy, 'okx', 'ETH-USDT', # Reference asset (not used for trading, just index alignment) timeframe='1h', init_cash=10000 ) logger.info("Multi-Pair Results:") logger.info(" Total Return: %.2f%%", result.portfolio.total_return() * 100) logger.info(" Total Trades: %d", result.portfolio.trades.count()) logger.info(" Win Rate: %.1f%%", result.portfolio.trades.win_rate() * 100) return result def compare_results(baseline, multi_pair): """Compare and display results.""" logger.info("=" * 60) logger.info("COMPARISON") logger.info("=" * 60) baseline_return = baseline.portfolio.total_return() * 100 multi_return = multi_pair.portfolio.total_return() * 100 improvement = multi_return - baseline_return logger.info("Baseline Return: %.2f%%", baseline_return) logger.info("Multi-Pair Return: %.2f%%", multi_return) logger.info("Improvement: %.2f%% (%.1fx)", improvement, multi_return / baseline_return if baseline_return != 0 else 0) baseline_trades = baseline.portfolio.trades.count() multi_trades = multi_pair.portfolio.trades.count() logger.info("Baseline Trades: %d", baseline_trades) logger.info("Multi-Pair Trades: %d", multi_trades) return { 'baseline_return': baseline_return, 'multi_pair_return': multi_return, 'improvement': improvement, 'baseline_trades': baseline_trades, 'multi_pair_trades': multi_trades } def main(): """Main entry point.""" setup_logging() # Check available assets dm = DataManager() available = [] for symbol in MultiPairConfig().assets: try: dm.load_data('okx', symbol, '1h', market_type=MarketType.PERPETUAL) available.append(symbol) except FileNotFoundError: pass if len(available) < 2: logger.error( "Need at least 2 assets to run multi-pair strategy. " "Run: uv run python scripts/download_multi_pair_data.py" ) return logger.info("Found data for %d assets: %s", len(available), available) # Run baseline baseline_result = run_baseline() # Run multi-pair multi_result = run_multi_pair(available) # Compare comparison = compare_results(baseline_result, multi_result) # Save reports reporter = Reporter() reporter.save_reports(multi_result, "multi_pair_divergence") logger.info("Reports saved to backtest_logs/") if __name__ == "__main__": main()