447 lines
16 KiB
Python
447 lines
16 KiB
Python
"""
|
|
Example usage of the Incremental Backtester.
|
|
|
|
This script demonstrates how to use the IncBacktester for various scenarios:
|
|
1. Single strategy backtesting
|
|
2. Multiple strategy comparison
|
|
3. Parameter optimization with multiprocessing
|
|
4. Custom analysis and result saving
|
|
5. Comprehensive result logging and action tracking
|
|
|
|
Run this script to see the backtester in action with real or synthetic data.
|
|
"""
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
import os
|
|
|
|
from cycles.IncStrategies import (
|
|
IncBacktester, BacktestConfig, IncRandomStrategy
|
|
)
|
|
from cycles.utils.storage import Storage
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def ensure_results_directory():
|
|
"""Ensure the results directory exists."""
|
|
results_dir = "results"
|
|
if not os.path.exists(results_dir):
|
|
os.makedirs(results_dir)
|
|
logger.info(f"Created results directory: {results_dir}")
|
|
return results_dir
|
|
|
|
|
|
def create_sample_data(days: int = 30) -> pd.DataFrame:
|
|
"""
|
|
Create sample OHLCV data for demonstration.
|
|
|
|
Args:
|
|
days: Number of days of data to generate
|
|
|
|
Returns:
|
|
pd.DataFrame: Sample OHLCV data
|
|
"""
|
|
# Create date range
|
|
end_date = datetime.now()
|
|
start_date = end_date - timedelta(days=days)
|
|
timestamps = pd.date_range(start=start_date, end=end_date, freq='1min')
|
|
|
|
# Generate realistic price data
|
|
np.random.seed(42)
|
|
n_points = len(timestamps)
|
|
|
|
# Start with a base price
|
|
base_price = 45000
|
|
|
|
# Generate price movements with trend and volatility
|
|
trend = np.linspace(0, 0.1, n_points) # Slight upward trend
|
|
volatility = np.random.normal(0, 0.002, n_points) # 0.2% volatility
|
|
|
|
# Calculate prices
|
|
log_returns = trend + volatility
|
|
prices = base_price * np.exp(np.cumsum(log_returns))
|
|
|
|
# Generate OHLCV data
|
|
data = []
|
|
for i, (timestamp, close_price) in enumerate(zip(timestamps, prices)):
|
|
# Generate realistic OHLC
|
|
intrabar_vol = close_price * 0.001
|
|
|
|
open_price = close_price + np.random.normal(0, intrabar_vol)
|
|
high_price = max(open_price, close_price) + abs(np.random.normal(0, intrabar_vol))
|
|
low_price = min(open_price, close_price) - abs(np.random.normal(0, intrabar_vol))
|
|
volume = np.random.uniform(50, 500)
|
|
|
|
data.append({
|
|
'open': open_price,
|
|
'high': high_price,
|
|
'low': low_price,
|
|
'close': close_price,
|
|
'volume': volume
|
|
})
|
|
|
|
df = pd.DataFrame(data, index=timestamps)
|
|
return df
|
|
|
|
|
|
def example_single_strategy():
|
|
"""Example 1: Single strategy backtesting with comprehensive results."""
|
|
print("\n" + "="*60)
|
|
print("EXAMPLE 1: Single Strategy Backtesting")
|
|
print("="*60)
|
|
|
|
# Create sample data
|
|
data = create_sample_data(days=7) # 1 week of data
|
|
|
|
# Save data
|
|
storage = Storage()
|
|
data_file = "sample_data_single.csv"
|
|
storage.save_data(data, data_file)
|
|
|
|
# Configure backtest
|
|
config = BacktestConfig(
|
|
data_file=data_file,
|
|
start_date=data.index[0].strftime("%Y-%m-%d"),
|
|
end_date=data.index[-1].strftime("%Y-%m-%d"),
|
|
initial_usd=10000,
|
|
stop_loss_pct=0.02,
|
|
take_profit_pct=0.05
|
|
)
|
|
|
|
# Create strategy
|
|
strategy = IncRandomStrategy(params={
|
|
"timeframe": "15min",
|
|
"entry_probability": 0.15,
|
|
"exit_probability": 0.2,
|
|
"random_seed": 42
|
|
})
|
|
|
|
# Run backtest
|
|
backtester = IncBacktester(config, storage)
|
|
results = backtester.run_single_strategy(strategy)
|
|
|
|
# Print results
|
|
print(f"\nResults:")
|
|
print(f" Strategy: {results['strategy_name']}")
|
|
print(f" Profit: {results['profit_ratio']*100:.2f}%")
|
|
print(f" Final Balance: ${results['final_usd']:,.2f}")
|
|
print(f" Trades: {results['n_trades']}")
|
|
print(f" Win Rate: {results['win_rate']*100:.1f}%")
|
|
print(f" Max Drawdown: {results['max_drawdown']*100:.2f}%")
|
|
|
|
# Save comprehensive results
|
|
backtester.save_comprehensive_results([results], "example_single_strategy")
|
|
|
|
# Cleanup
|
|
if os.path.exists(f"data/{data_file}"):
|
|
os.remove(f"data/{data_file}")
|
|
|
|
return results
|
|
|
|
|
|
def example_multiple_strategies():
|
|
"""Example 2: Multiple strategy comparison with comprehensive results."""
|
|
print("\n" + "="*60)
|
|
print("EXAMPLE 2: Multiple Strategy Comparison")
|
|
print("="*60)
|
|
|
|
# Create sample data
|
|
data = create_sample_data(days=10) # 10 days of data
|
|
|
|
# Save data
|
|
storage = Storage()
|
|
data_file = "sample_data_multiple.csv"
|
|
storage.save_data(data, data_file)
|
|
|
|
# Configure backtest
|
|
config = BacktestConfig(
|
|
data_file=data_file,
|
|
start_date=data.index[0].strftime("%Y-%m-%d"),
|
|
end_date=data.index[-1].strftime("%Y-%m-%d"),
|
|
initial_usd=10000,
|
|
stop_loss_pct=0.015
|
|
)
|
|
|
|
# Create multiple strategies with different parameters
|
|
strategies = [
|
|
IncRandomStrategy(params={
|
|
"timeframe": "5min",
|
|
"entry_probability": 0.1,
|
|
"exit_probability": 0.15,
|
|
"random_seed": 42
|
|
}),
|
|
IncRandomStrategy(params={
|
|
"timeframe": "15min",
|
|
"entry_probability": 0.12,
|
|
"exit_probability": 0.18,
|
|
"random_seed": 123
|
|
}),
|
|
IncRandomStrategy(params={
|
|
"timeframe": "30min",
|
|
"entry_probability": 0.08,
|
|
"exit_probability": 0.12,
|
|
"random_seed": 456
|
|
}),
|
|
IncRandomStrategy(params={
|
|
"timeframe": "1h",
|
|
"entry_probability": 0.06,
|
|
"exit_probability": 0.1,
|
|
"random_seed": 789
|
|
})
|
|
]
|
|
|
|
# Run backtest
|
|
backtester = IncBacktester(config, storage)
|
|
results = backtester.run_multiple_strategies(strategies)
|
|
|
|
# Print comparison
|
|
print(f"\nStrategy Comparison:")
|
|
print(f"{'Strategy':<20} {'Timeframe':<10} {'Profit %':<10} {'Trades':<8} {'Win Rate %':<12}")
|
|
print("-" * 70)
|
|
|
|
for i, result in enumerate(results):
|
|
if result.get("success", True):
|
|
timeframe = result['strategy_params']['timeframe']
|
|
profit = result['profit_ratio'] * 100
|
|
trades = result['n_trades']
|
|
win_rate = result['win_rate'] * 100
|
|
print(f"Strategy {i+1:<13} {timeframe:<10} {profit:<10.2f} {trades:<8} {win_rate:<12.1f}")
|
|
|
|
# Get summary statistics
|
|
summary = backtester.get_summary_statistics(results)
|
|
print(f"\nSummary Statistics:")
|
|
print(f" Best Profit: {summary['profit_ratio']['max']*100:.2f}%")
|
|
print(f" Worst Profit: {summary['profit_ratio']['min']*100:.2f}%")
|
|
print(f" Average Profit: {summary['profit_ratio']['mean']*100:.2f}%")
|
|
print(f" Profit Std Dev: {summary['profit_ratio']['std']*100:.2f}%")
|
|
|
|
# Save comprehensive results
|
|
backtester.save_comprehensive_results(results, "example_multiple_strategies", summary)
|
|
|
|
# Cleanup
|
|
if os.path.exists(f"data/{data_file}"):
|
|
os.remove(f"data/{data_file}")
|
|
|
|
return results, summary
|
|
|
|
|
|
def example_parameter_optimization():
|
|
"""Example 3: Parameter optimization with multiprocessing and comprehensive results."""
|
|
print("\n" + "="*60)
|
|
print("EXAMPLE 3: Parameter Optimization")
|
|
print("="*60)
|
|
|
|
# Create sample data
|
|
data = create_sample_data(days=5) # 5 days for faster optimization
|
|
|
|
# Save data
|
|
storage = Storage()
|
|
data_file = "sample_data_optimization.csv"
|
|
storage.save_data(data, data_file)
|
|
|
|
# Configure backtest
|
|
config = BacktestConfig(
|
|
data_file=data_file,
|
|
start_date=data.index[0].strftime("%Y-%m-%d"),
|
|
end_date=data.index[-1].strftime("%Y-%m-%d"),
|
|
initial_usd=10000
|
|
)
|
|
|
|
# Define parameter grids
|
|
strategy_param_grid = {
|
|
"timeframe": ["5min", "15min", "30min"],
|
|
"entry_probability": [0.08, 0.12, 0.16],
|
|
"exit_probability": [0.1, 0.15, 0.2],
|
|
"random_seed": [42] # Keep seed constant for fair comparison
|
|
}
|
|
|
|
trader_param_grid = {
|
|
"stop_loss_pct": [0.01, 0.015, 0.02],
|
|
"take_profit_pct": [0.0, 0.03, 0.05]
|
|
}
|
|
|
|
# Run optimization (will use SystemUtils to determine optimal workers)
|
|
backtester = IncBacktester(config, storage)
|
|
|
|
print(f"Starting optimization with {len(strategy_param_grid['timeframe']) * len(strategy_param_grid['entry_probability']) * len(strategy_param_grid['exit_probability']) * len(trader_param_grid['stop_loss_pct']) * len(trader_param_grid['take_profit_pct'])} combinations...")
|
|
|
|
results = backtester.optimize_parameters(
|
|
strategy_class=IncRandomStrategy,
|
|
param_grid=strategy_param_grid,
|
|
trader_param_grid=trader_param_grid,
|
|
max_workers=None # Use SystemUtils for optimal worker count
|
|
)
|
|
|
|
# Get summary
|
|
summary = backtester.get_summary_statistics(results)
|
|
|
|
# Print optimization results
|
|
print(f"\nOptimization Results:")
|
|
print(f" Total Combinations: {summary['total_runs']}")
|
|
print(f" Successful Runs: {summary['successful_runs']}")
|
|
print(f" Failed Runs: {summary['failed_runs']}")
|
|
|
|
if summary['successful_runs'] > 0:
|
|
print(f" Best Profit: {summary['profit_ratio']['max']*100:.2f}%")
|
|
print(f" Worst Profit: {summary['profit_ratio']['min']*100:.2f}%")
|
|
print(f" Average Profit: {summary['profit_ratio']['mean']*100:.2f}%")
|
|
|
|
# Show top 3 configurations
|
|
valid_results = [r for r in results if r.get("success", True)]
|
|
valid_results.sort(key=lambda x: x["profit_ratio"], reverse=True)
|
|
|
|
print(f"\nTop 3 Configurations:")
|
|
for i, result in enumerate(valid_results[:3]):
|
|
print(f" {i+1}. Profit: {result['profit_ratio']*100:.2f}% | "
|
|
f"Timeframe: {result['strategy_params']['timeframe']} | "
|
|
f"Entry Prob: {result['strategy_params']['entry_probability']} | "
|
|
f"Stop Loss: {result['trader_params']['stop_loss_pct']*100:.1f}%")
|
|
|
|
# Save comprehensive results
|
|
backtester.save_comprehensive_results(results, "example_parameter_optimization", summary)
|
|
|
|
# Cleanup
|
|
if os.path.exists(f"data/{data_file}"):
|
|
os.remove(f"data/{data_file}")
|
|
|
|
return results, summary
|
|
|
|
|
|
def example_custom_analysis():
|
|
"""Example 4: Custom analysis with detailed result examination."""
|
|
print("\n" + "="*60)
|
|
print("EXAMPLE 4: Custom Analysis")
|
|
print("="*60)
|
|
|
|
# Create sample data with more volatility for interesting results
|
|
data = create_sample_data(days=14) # 2 weeks
|
|
|
|
# Save data
|
|
storage = Storage()
|
|
data_file = "sample_data_analysis.csv"
|
|
storage.save_data(data, data_file)
|
|
|
|
# Configure backtest
|
|
config = BacktestConfig(
|
|
data_file=data_file,
|
|
start_date=data.index[0].strftime("%Y-%m-%d"),
|
|
end_date=data.index[-1].strftime("%Y-%m-%d"),
|
|
initial_usd=25000, # Larger starting capital
|
|
stop_loss_pct=0.025,
|
|
take_profit_pct=0.04
|
|
)
|
|
|
|
# Create strategy with specific parameters for analysis
|
|
strategy = IncRandomStrategy(params={
|
|
"timeframe": "30min",
|
|
"entry_probability": 0.1,
|
|
"exit_probability": 0.15,
|
|
"random_seed": 42
|
|
})
|
|
|
|
# Run backtest
|
|
backtester = IncBacktester(config, storage)
|
|
results = backtester.run_single_strategy(strategy)
|
|
|
|
# Detailed analysis
|
|
print(f"\nDetailed Analysis:")
|
|
print(f" Strategy: {results['strategy_name']}")
|
|
print(f" Timeframe: {results['strategy_params']['timeframe']}")
|
|
print(f" Data Period: {config.start_date} to {config.end_date}")
|
|
print(f" Data Points: {results['data_points']:,}")
|
|
print(f" Processing Time: {results['backtest_duration_seconds']:.2f}s")
|
|
|
|
print(f"\nPerformance Metrics:")
|
|
print(f" Initial Capital: ${results['initial_usd']:,.2f}")
|
|
print(f" Final Balance: ${results['final_usd']:,.2f}")
|
|
print(f" Total Return: {results['profit_ratio']*100:.2f}%")
|
|
print(f" Total Trades: {results['n_trades']}")
|
|
|
|
if results['n_trades'] > 0:
|
|
print(f" Win Rate: {results['win_rate']*100:.1f}%")
|
|
print(f" Average Trade: ${results['avg_trade']:.2f}")
|
|
print(f" Max Drawdown: {results['max_drawdown']*100:.2f}%")
|
|
print(f" Total Fees: ${results['total_fees_usd']:.2f}")
|
|
|
|
# Calculate additional metrics
|
|
days_traded = (pd.to_datetime(config.end_date) - pd.to_datetime(config.start_date)).days
|
|
annualized_return = (1 + results['profit_ratio']) ** (365 / days_traded) - 1
|
|
print(f" Annualized Return: {annualized_return*100:.2f}%")
|
|
|
|
# Risk metrics
|
|
if results['max_drawdown'] > 0:
|
|
calmar_ratio = annualized_return / results['max_drawdown']
|
|
print(f" Calmar Ratio: {calmar_ratio:.2f}")
|
|
|
|
# Save comprehensive results with custom analysis
|
|
backtester.save_comprehensive_results([results], "example_custom_analysis")
|
|
|
|
# Cleanup
|
|
if os.path.exists(f"data/{data_file}"):
|
|
os.remove(f"data/{data_file}")
|
|
|
|
return results
|
|
|
|
|
|
def main():
|
|
"""Run all examples."""
|
|
print("Incremental Backtester Examples")
|
|
print("="*60)
|
|
print("This script demonstrates various features of the IncBacktester:")
|
|
print("1. Single strategy backtesting")
|
|
print("2. Multiple strategy comparison")
|
|
print("3. Parameter optimization with multiprocessing")
|
|
print("4. Custom analysis and metrics")
|
|
print("5. Comprehensive result saving and action logging")
|
|
|
|
# Ensure results directory exists
|
|
ensure_results_directory()
|
|
|
|
try:
|
|
# Run all examples
|
|
single_results = example_single_strategy()
|
|
multiple_results, multiple_summary = example_multiple_strategies()
|
|
optimization_results, optimization_summary = example_parameter_optimization()
|
|
analysis_results = example_custom_analysis()
|
|
|
|
print("\n" + "="*60)
|
|
print("ALL EXAMPLES COMPLETED SUCCESSFULLY!")
|
|
print("="*60)
|
|
print("\n📊 Comprehensive results have been saved to the 'results' directory.")
|
|
print("Each example generated multiple files:")
|
|
print(" 📋 Summary JSON with session info and statistics")
|
|
print(" 📈 Detailed CSV with all backtest results")
|
|
print(" 📝 Action log JSON with all operations performed")
|
|
print(" 📁 Individual strategy JSON files with trades and details")
|
|
print(" 🗂️ Master index JSON for easy navigation")
|
|
|
|
print(f"\n🎯 Key Insights:")
|
|
print(f" • Single strategy achieved {single_results['profit_ratio']*100:.2f}% return")
|
|
print(f" • Multiple strategies: best {multiple_summary['profit_ratio']['max']*100:.2f}%, worst {multiple_summary['profit_ratio']['min']*100:.2f}%")
|
|
print(f" • Optimization tested {optimization_summary['total_runs']} combinations")
|
|
print(f" • Custom analysis provided detailed risk metrics")
|
|
|
|
print(f"\n🔧 System Performance:")
|
|
print(f" • Used SystemUtils for optimal CPU core utilization")
|
|
print(f" • All actions logged for reproducibility")
|
|
print(f" • Results saved in multiple formats for analysis")
|
|
|
|
print(f"\n✅ The incremental backtester is ready for production use!")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Example failed: {e}")
|
|
print(f"\nError: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |