#!/usr/bin/env python3 """ Compare Original vs Incremental Strategies on Same Data ====================================================== This script runs both strategies on the exact same data period from btcusd_1-min_data.csv to ensure a fair comparison. """ import sys import os import json import pandas as pd import numpy as np from datetime import datetime import matplotlib.pyplot as plt import matplotlib.dates as mdates # Add the parent directory to the path to import cycles modules sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from cycles.utils.storage import Storage from cycles.IncStrategies.inc_backtester import IncBacktester, BacktestConfig from cycles.IncStrategies.metatrend_strategy import IncMetaTrendStrategy from cycles.utils.data_utils import aggregate_to_minutes def run_original_strategy_via_main(start_date: str, end_date: str, initial_usd: float, stop_loss_pct: float): """Run the original strategy using the main.py system.""" print(f"\nšŸ”„ Running Original Strategy via main.py...") # Create a temporary config file for the original strategy config = { "start_date": start_date, "stop_date": end_date, "initial_usd": initial_usd, "timeframes": ["15min"], "strategies": [ { "name": "default", "weight": 1.0, "params": { "stop_loss_pct": stop_loss_pct, "timeframe": "15min" } } ], "combination_rules": { "min_strategies": 1, "min_confidence": 0.5 } } # Save temporary config temp_config_file = "temp_config.json" with open(temp_config_file, 'w') as f: json.dump(config, f, indent=2) try: # Import and run the main processing function from main import process_timeframe_data from cycles.utils.storage import Storage storage = Storage() # Load data using absolute path data_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "btcusd_1-min_data.csv") print(f"Loading data from: {data_file}") if not os.path.exists(data_file): print(f"āŒ Data file not found: {data_file}") return None data_1min = storage.load_data(data_file, start_date, end_date) print(f"Loaded {len(data_1min)} minute-level data points") if len(data_1min) == 0: print(f"āŒ No data loaded for period {start_date} to {end_date}") return None # Run the original strategy results_rows, trade_rows = process_timeframe_data(data_1min, "15min", config, debug=False) if not results_rows: print("āŒ No results from original strategy") return None result = results_rows[0] trades = [trade for trade in trade_rows if trade['timeframe'] == result['timeframe']] return { 'strategy_name': 'Original MetaTrend', 'n_trades': result['n_trades'], 'win_rate': result['win_rate'], 'avg_trade': result['avg_trade'], 'max_drawdown': result['max_drawdown'], 'initial_usd': result['initial_usd'], 'final_usd': result['final_usd'], 'profit_ratio': (result['final_usd'] - result['initial_usd']) / result['initial_usd'], 'total_fees_usd': result['total_fees_usd'], 'trades': trades, 'data_points': len(data_1min) } finally: # Clean up temporary config file if os.path.exists(temp_config_file): os.remove(temp_config_file) def run_incremental_strategy(start_date: str, end_date: str, initial_usd: float, stop_loss_pct: float): """Run the incremental strategy using the new backtester.""" print(f"\nšŸ”„ Running Incremental Strategy...") storage = Storage() # Use absolute path for data file data_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "btcusd_1-min_data.csv") # Create backtester configuration config = BacktestConfig( data_file=data_file, start_date=start_date, end_date=end_date, initial_usd=initial_usd, stop_loss_pct=stop_loss_pct, take_profit_pct=0.0 ) # Create strategy strategy = IncMetaTrendStrategy( name="metatrend", weight=1.0, params={ "timeframe": "15min", "enable_logging": False } ) # Run backtest backtester = IncBacktester(config, storage) result = backtester.run_single_strategy(strategy) result['strategy_name'] = 'Incremental MetaTrend' return result def save_comparison_results(original_result: dict, incremental_result: dict, output_dir: str): """Save comparison results to files.""" os.makedirs(output_dir, exist_ok=True) # Save original trades original_trades_file = os.path.join(output_dir, "original_trades.csv") if original_result and original_result['trades']: trades_df = pd.DataFrame(original_result['trades']) trades_df.to_csv(original_trades_file, index=False) print(f"Saved original trades to: {original_trades_file}") # Save incremental trades incremental_trades_file = os.path.join(output_dir, "incremental_trades.csv") if incremental_result['trades']: # Convert to same format as original trades_data = [] for trade in incremental_result['trades']: trades_data.append({ 'entry_time': trade.get('entry_time'), 'exit_time': trade.get('exit_time'), 'entry_price': trade.get('entry_price'), 'exit_price': trade.get('exit_price'), 'profit_pct': trade.get('profit_pct'), 'type': trade.get('type'), 'fee_usd': trade.get('fee_usd') }) trades_df = pd.DataFrame(trades_data) trades_df.to_csv(incremental_trades_file, index=False) print(f"Saved incremental trades to: {incremental_trades_file}") # Save comparison summary comparison_file = os.path.join(output_dir, "strategy_comparison.json") # Convert numpy types to Python types for JSON serialization def convert_numpy_types(obj): if hasattr(obj, 'item'): # numpy scalar return obj.item() elif isinstance(obj, dict): return {k: convert_numpy_types(v) for k, v in obj.items()} elif isinstance(obj, list): return [convert_numpy_types(v) for v in obj] else: return obj comparison_data = { 'test_date': datetime.now().isoformat(), 'data_file': 'btcusd_1-min_data.csv', 'original_strategy': { 'name': original_result['strategy_name'] if original_result else 'Failed', 'n_trades': int(original_result['n_trades']) if original_result else 0, 'win_rate': float(original_result['win_rate']) if original_result else 0, 'avg_trade': float(original_result['avg_trade']) if original_result else 0, 'max_drawdown': float(original_result['max_drawdown']) if original_result else 0, 'initial_usd': float(original_result['initial_usd']) if original_result else 0, 'final_usd': float(original_result['final_usd']) if original_result else 0, 'profit_ratio': float(original_result['profit_ratio']) if original_result else 0, 'total_fees_usd': float(original_result['total_fees_usd']) if original_result else 0, 'data_points': int(original_result['data_points']) if original_result else 0 }, 'incremental_strategy': { 'name': incremental_result['strategy_name'], 'n_trades': int(incremental_result['n_trades']), 'win_rate': float(incremental_result['win_rate']), 'avg_trade': float(incremental_result['avg_trade']), 'max_drawdown': float(incremental_result['max_drawdown']), 'initial_usd': float(incremental_result['initial_usd']), 'final_usd': float(incremental_result['final_usd']), 'profit_ratio': float(incremental_result['profit_ratio']), 'total_fees_usd': float(incremental_result['total_fees_usd']), 'data_points': int(incremental_result.get('data_points_processed', 0)) } } if original_result: comparison_data['comparison'] = { 'profit_difference': float(incremental_result['profit_ratio'] - original_result['profit_ratio']), 'trade_count_difference': int(incremental_result['n_trades'] - original_result['n_trades']), 'win_rate_difference': float(incremental_result['win_rate'] - original_result['win_rate']) } with open(comparison_file, 'w') as f: json.dump(comparison_data, f, indent=2) print(f"Saved comparison summary to: {comparison_file}") return comparison_data def create_comparison_plot(original_result: dict, incremental_result: dict, start_date: str, end_date: str, output_dir: str): """Create a comparison plot showing both strategies.""" print(f"\nšŸ“Š Creating comparison plot...") # Load price data for plotting storage = Storage() data_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "btcusd_1-min_data.csv") data_1min = storage.load_data(data_file, start_date, end_date) aggregated_data = aggregate_to_minutes(data_1min, 15) fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 12)) # Plot 1: Price with trade signals ax1.plot(aggregated_data.index, aggregated_data['close'], 'k-', alpha=0.7, linewidth=1, label='BTC Price') # Plot original strategy trades if original_result and original_result['trades']: original_trades = original_result['trades'] for trade in original_trades: entry_time = pd.to_datetime(trade.get('entry_time')) exit_time = pd.to_datetime(trade.get('exit_time')) entry_price = trade.get('entry_price') exit_price = trade.get('exit_price') if entry_time and entry_price: # Buy signal (above price line) ax1.scatter(entry_time, entry_price * 1.02, color='green', marker='^', s=50, alpha=0.8, label='Original Buy' if trade == original_trades[0] else "") if exit_time and exit_price: # Sell signal (above price line) color = 'red' if trade.get('profit_pct', 0) < 0 else 'blue' ax1.scatter(exit_time, exit_price * 1.02, color=color, marker='v', s=50, alpha=0.8, label='Original Sell' if trade == original_trades[0] else "") # Plot incremental strategy trades incremental_trades = incremental_result['trades'] if incremental_trades: for trade in incremental_trades: entry_time = pd.to_datetime(trade.get('entry_time')) exit_time = pd.to_datetime(trade.get('exit_time')) entry_price = trade.get('entry_price') exit_price = trade.get('exit_price') if entry_time and entry_price: # Buy signal (below price line) ax1.scatter(entry_time, entry_price * 0.98, color='lightgreen', marker='^', s=50, alpha=0.8, label='Incremental Buy' if trade == incremental_trades[0] else "") if exit_time and exit_price: # Sell signal (below price line) exit_type = trade.get('type', 'STRATEGY_EXIT') if exit_type == 'STOP_LOSS': color = 'orange' elif exit_type == 'TAKE_PROFIT': color = 'purple' else: color = 'lightblue' ax1.scatter(exit_time, exit_price * 0.98, color=color, marker='v', s=50, alpha=0.8, label=f'Incremental {exit_type}' if trade == incremental_trades[0] else "") ax1.set_title(f'Strategy Comparison: {start_date} to {end_date}', fontsize=14, fontweight='bold') ax1.set_ylabel('Price (USD)', fontsize=12) ax1.legend(loc='upper left') ax1.grid(True, alpha=0.3) # Format x-axis ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d')) ax1.xaxis.set_major_locator(mdates.MonthLocator()) plt.setp(ax1.xaxis.get_majorticklabels(), rotation=45) # Plot 2: Performance comparison strategies = ['Original', 'Incremental'] profits = [ original_result['profit_ratio'] * 100 if original_result else 0, incremental_result['profit_ratio'] * 100 ] colors = ['blue', 'green'] bars = ax2.bar(strategies, profits, color=colors, alpha=0.7) ax2.set_title('Profit Comparison', fontsize=14, fontweight='bold') ax2.set_ylabel('Profit (%)', fontsize=12) ax2.grid(True, alpha=0.3, axis='y') # Add value labels on bars for bar, profit in zip(bars, profits): height = bar.get_height() ax2.text(bar.get_x() + bar.get_width()/2., height + (0.5 if height >= 0 else -1.5), f'{profit:.2f}%', ha='center', va='bottom' if height >= 0 else 'top', fontweight='bold') plt.tight_layout() # Save plot plot_file = os.path.join(output_dir, "strategy_comparison.png") plt.savefig(plot_file, dpi=300, bbox_inches='tight') plt.close() print(f"Saved comparison plot to: {plot_file}") def print_comparison_summary(original_result: dict, incremental_result: dict): """Print a detailed comparison summary.""" print("\n" + "="*80) print("STRATEGY COMPARISON SUMMARY") print("="*80) if not original_result: print("āŒ Original strategy failed to run") print(f"āœ… Incremental strategy: {incremental_result['profit_ratio']*100:.2f}% profit") return print(f"\nšŸ“Š PERFORMANCE METRICS:") print(f"{'Metric':<20} {'Original':<15} {'Incremental':<15} {'Difference':<15}") print("-" * 65) # Profit comparison orig_profit = original_result['profit_ratio'] * 100 inc_profit = incremental_result['profit_ratio'] * 100 profit_diff = inc_profit - orig_profit print(f"{'Profit %':<20} {orig_profit:<15.2f} {inc_profit:<15.2f} {profit_diff:<15.2f}") # Final USD comparison orig_final = original_result['final_usd'] inc_final = incremental_result['final_usd'] usd_diff = inc_final - orig_final print(f"{'Final USD':<20} ${orig_final:<14.2f} ${inc_final:<14.2f} ${usd_diff:<14.2f}") # Trade count comparison orig_trades = original_result['n_trades'] inc_trades = incremental_result['n_trades'] trade_diff = inc_trades - orig_trades print(f"{'Total Trades':<20} {orig_trades:<15} {inc_trades:<15} {trade_diff:<15}") # Win rate comparison orig_wr = original_result['win_rate'] * 100 inc_wr = incremental_result['win_rate'] * 100 wr_diff = inc_wr - orig_wr print(f"{'Win Rate %':<20} {orig_wr:<15.2f} {inc_wr:<15.2f} {wr_diff:<15.2f}") # Average trade comparison orig_avg = original_result['avg_trade'] * 100 inc_avg = incremental_result['avg_trade'] * 100 avg_diff = inc_avg - orig_avg print(f"{'Avg Trade %':<20} {orig_avg:<15.2f} {inc_avg:<15.2f} {avg_diff:<15.2f}") # Max drawdown comparison orig_dd = original_result['max_drawdown'] * 100 inc_dd = incremental_result['max_drawdown'] * 100 dd_diff = inc_dd - orig_dd print(f"{'Max Drawdown %':<20} {orig_dd:<15.2f} {inc_dd:<15.2f} {dd_diff:<15.2f}") # Fees comparison orig_fees = original_result['total_fees_usd'] inc_fees = incremental_result['total_fees_usd'] fees_diff = inc_fees - orig_fees print(f"{'Total Fees USD':<20} ${orig_fees:<14.2f} ${inc_fees:<14.2f} ${fees_diff:<14.2f}") print("\n" + "="*80) # Determine winner if profit_diff > 0: print(f"šŸ† WINNER: Incremental Strategy (+{profit_diff:.2f}% better)") elif profit_diff < 0: print(f"šŸ† WINNER: Original Strategy (+{abs(profit_diff):.2f}% better)") else: print(f"šŸ¤ TIE: Both strategies performed equally") print("="*80) def main(): """Main comparison function.""" print("šŸš€ Comparing Original vs Incremental Strategies on Same Data") print("=" * 80) # Configuration start_date = "2025-01-01" end_date = "2025-05-01" initial_usd = 10000 stop_loss_pct = 0.03 # 3% stop loss print(f"šŸ“… Test Period: {start_date} to {end_date}") print(f"šŸ’° Initial Capital: ${initial_usd:,}") print(f"šŸ›‘ Stop Loss: {stop_loss_pct*100:.1f}%") print(f"šŸ“Š Data Source: btcusd_1-min_data.csv") try: # Run both strategies original_result = run_original_strategy_via_main(start_date, end_date, initial_usd, stop_loss_pct) incremental_result = run_incremental_strategy(start_date, end_date, initial_usd, stop_loss_pct) # Print comparison summary print_comparison_summary(original_result, incremental_result) # Save results output_dir = "results/strategy_comparison" comparison_data = save_comparison_results(original_result, incremental_result, output_dir) # Create comparison plot create_comparison_plot(original_result, incremental_result, start_date, end_date, output_dir) print(f"\nšŸ“ Results saved to: {output_dir}/") print(f" - strategy_comparison.json") print(f" - strategy_comparison.png") print(f" - original_trades.csv") print(f" - incremental_trades.csv") return True except Exception as e: print(f"\nāŒ Error during comparison: {e}") import traceback traceback.print_exc() return False if __name__ == "__main__": success = main() sys.exit(0 if success else 1)