TradingAgents/run_crypto_backtest.py

400 lines
13 KiB
Python

"""
Full Crypto Backtesting Example
Runs actual backtests with real historical data
"""
import os
import sys
from datetime import datetime, timedelta
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from tradingagents.backtesting import CryptoBacktestEngine, OrderType
from tradingagents.backtesting.crypto_data_loader import CryptoDataLoader, CRYPTO_MARKET_CYCLES
from tradingagents.backtesting.crypto_strategy_evaluator import CryptoStrategyEvaluator
def print_section(title):
"""Print formatted section header."""
print("\n" + "=" * 80)
print(f" {title}")
print("=" * 80 + "\n")
# ============================================================================
# STRATEGY DEFINITIONS
# ============================================================================
def buy_and_hold_strategy(timestamp, row, engine):
"""Buy once and hold."""
if len(engine.positions) == 0:
return OrderType.BUY, "Initial buy - Buy and Hold strategy"
return OrderType.HOLD, "Holding position"
class SimpleMovingAverageCrossover:
"""Simple MA crossover strategy."""
def __init__(self, short_window=20, long_window=50):
self.short_window = short_window
self.long_window = long_window
self.prices = []
def __call__(self, timestamp, row, engine):
self.prices.append(row['close'])
if len(self.prices) < self.long_window:
return OrderType.HOLD, "Building price history"
# Calculate simple moving averages
short_ma = sum(self.prices[-self.short_window:]) / self.short_window
long_ma = sum(self.prices[-self.long_window:]) / self.long_window
# Golden cross - buy signal
if short_ma > long_ma and len(engine.positions) == 0:
return OrderType.BUY, f"Golden cross: SMA{self.short_window} (${short_ma:.0f}) > SMA{self.long_window} (${long_ma:.0f})"
# Death cross - sell signal
elif short_ma < long_ma and len(engine.positions) > 0:
return OrderType.SELL, f"Death cross: SMA{self.short_window} (${short_ma:.0f}) < SMA{self.long_window} (${long_ma:.0f})"
return OrderType.HOLD, f"No signal: SMA{self.short_window}=${short_ma:.0f}, SMA{self.long_window}=${long_ma:.0f}"
class MomentumStrategy:
"""Buy on momentum, sell on reversal."""
def __init__(self, lookback=10, momentum_threshold=0.05):
self.lookback = lookback
self.momentum_threshold = momentum_threshold
self.prices = []
def __call__(self, timestamp, row, engine):
self.prices.append(row['close'])
if len(self.prices) < self.lookback:
return OrderType.HOLD, "Building price history"
# Calculate momentum (% change over lookback period)
momentum = (self.prices[-1] - self.prices[-self.lookback]) / self.prices[-self.lookback]
# Strong upward momentum - buy
if momentum > self.momentum_threshold and len(engine.positions) == 0:
return OrderType.BUY, f"Strong momentum: +{momentum:.2%} over {self.lookback} days"
# Momentum reversal - sell
elif momentum < -self.momentum_threshold and len(engine.positions) > 0:
return OrderType.SELL, f"Momentum reversal: {momentum:.2%} over {self.lookback} days"
return OrderType.HOLD, f"Momentum neutral: {momentum:.2%}"
# ============================================================================
# BACKTEST EXECUTION
# ============================================================================
def run_single_backtest():
"""Example 1: Run a single backtest."""
print_section("EXAMPLE 1: Single Backtest - Buy and Hold")
try:
# Setup
print("Setting up backtest...")
engine = CryptoBacktestEngine(
initial_capital=10000,
commission_rate=0.001,
slippage_rate=0.002
)
loader = CryptoDataLoader(exchange_id='binance')
evaluator = CryptoStrategyEvaluator(engine, loader)
# Define period
symbol = 'BTC/USDT'
start_date = datetime(2024, 1, 1)
end_date = datetime(2024, 6, 1)
print(f"Symbol: {symbol}")
print(f"Period: {start_date.date()} to {end_date.date()}")
print(f"Initial Capital: ${engine.initial_capital:,.2f}\n")
# Run backtest
print("Fetching data and running backtest...\n")
metrics = evaluator.run_backtest(
symbol=symbol,
start_date=start_date,
end_date=end_date,
strategy_func=buy_and_hold_strategy,
timeframe='1d'
)
# Display results
print("\n📊 BACKTEST RESULTS:")
print(f" Final Capital: ${metrics['final_capital']:,.2f}")
print(f" Total Return: {metrics['total_return_pct']:.2f}%")
print(f" Max Drawdown: {metrics['max_drawdown_pct']:.2f}%")
print(f" Sharpe Ratio: {metrics['sharpe_ratio']:.2f}")
print(f" Total Trades: {metrics['total_trades']}")
print(f" Win Rate: {metrics['win_rate_pct']:.1f}%")
return True
except Exception as e:
print(f"\n❌ Error running backtest: {e}")
print("\nNote: This requires:")
print(" 1. CCXT installed: pip install ccxt")
print(" 2. Internet connection to fetch data")
import traceback
traceback.print_exc()
return False
def compare_strategies_example():
"""Example 2: Compare multiple strategies."""
print_section("EXAMPLE 2: Strategy Comparison")
try:
# Setup
print("Setting up strategy comparison...")
engine = CryptoBacktestEngine(initial_capital=10000)
loader = CryptoDataLoader(exchange_id='binance')
evaluator = CryptoStrategyEvaluator(engine, loader)
# Define strategies
strategies = {
'Buy & Hold': buy_and_hold_strategy,
'MA Cross (20/50)': SimpleMovingAverageCrossover(20, 50),
'Momentum (10d)': MomentumStrategy(10, 0.05),
}
# Run comparison
symbol = 'BTC/USDT'
start_date = datetime(2024, 1, 1)
end_date = datetime(2024, 6, 1)
print(f"Symbol: {symbol}")
print(f"Period: {start_date.date()} to {end_date.date()}")
print(f"Strategies: {len(strategies)}\n")
print("Running comparisons...\n")
comparison_df = evaluator.compare_strategies(
symbol=symbol,
start_date=start_date,
end_date=end_date,
strategies=strategies,
timeframe='1d'
)
# Find best strategy
print("\n🏆 BEST STRATEGIES:")
best_return = comparison_df.loc[comparison_df['total_return_pct'].idxmax()]
best_sharpe = comparison_df.loc[comparison_df['sharpe_ratio'].idxmax()]
print(f" Best Return: {best_return['strategy_name']} ({best_return['total_return_pct']:.2f}%)")
print(f" Best Sharpe: {best_sharpe['strategy_name']} ({best_sharpe['sharpe_ratio']:.2f})")
return True
except Exception as e:
print(f"\n❌ Error in comparison: {e}")
import traceback
traceback.print_exc()
return False
def walk_forward_test_example():
"""Example 3: Walk-forward testing."""
print_section("EXAMPLE 3: Walk-Forward Testing")
try:
# Setup
print("Setting up walk-forward test...")
engine = CryptoBacktestEngine(initial_capital=10000)
loader = CryptoDataLoader(exchange_id='binance')
evaluator = CryptoStrategyEvaluator(engine, loader)
# Define parameters
symbol = 'BTC/USDT'
start_date = datetime(2024, 1, 1)
end_date = datetime(2024, 6, 1)
print(f"Symbol: {symbol}")
print(f"Period: {start_date.date()} to {end_date.date()}")
print(f"Train Period: 60 days")
print(f"Test Period: 30 days\n")
# Run walk-forward test
print("Running walk-forward test...\n")
results = evaluator.run_walk_forward_test(
symbol=symbol,
start_date=start_date,
end_date=end_date,
strategy_func=SimpleMovingAverageCrossover(20, 50),
train_period_days=60,
test_period_days=30,
timeframe='1d'
)
# Analyze results
if results:
import numpy as np
returns = [r['total_return_pct'] for r in results]
sharpes = [r['sharpe_ratio'] for r in results]
print("\n📊 WALK-FORWARD SUMMARY:")
print(f" Periods Tested: {len(results)}")
print(f" Avg Return: {np.mean(returns):.2f}%")
print(f" Std Dev: {np.std(returns):.2f}%")
print(f" Avg Sharpe: {np.mean(sharpes):.2f}")
print(f" Win Rate: {sum(1 for r in returns if r > 0) / len(returns) * 100:.1f}%")
return True
except Exception as e:
print(f"\n❌ Error in walk-forward test: {e}")
import traceback
traceback.print_exc()
return False
def market_cycle_test_example():
"""Example 4: Test on historical market cycles."""
print_section("EXAMPLE 4: Market Cycle Testing")
try:
# Setup
print("Testing strategy across market cycles...")
engine = CryptoBacktestEngine(initial_capital=10000)
loader = CryptoDataLoader(exchange_id='binance')
evaluator = CryptoStrategyEvaluator(engine, loader)
# Use recent cycles only (to ensure data availability)
recent_cycles = [
{
'name': '2022 Bear Market',
'type': 'bear',
'start': '2022-01-01',
'end': '2022-11-21'
},
{
'name': '2023 Recovery',
'type': 'bull',
'start': '2023-01-01',
'end': '2023-12-31'
},
{
'name': '2024 YTD',
'type': 'bull',
'start': '2024-01-01',
'end': '2024-06-01'
}
]
print(f"Testing {len(recent_cycles)} market cycles\n")
# Run tests
results = evaluator.test_on_market_cycles(
symbol='BTC/USDT',
strategy_func=SimpleMovingAverageCrossover(20, 50),
cycles=recent_cycles,
timeframe='1d'
)
# Analyze by cycle type
print("\n📊 PERFORMANCE BY MARKET CYCLE:")
bull_returns = []
bear_returns = []
for cycle_name, metrics in results.items():
cycle_type = metrics['cycle_type']
return_pct = metrics['total_return_pct']
print(f" {cycle_name:20s} ({cycle_type:4s}): {return_pct:>7.2f}%")
if cycle_type == 'bull':
bull_returns.append(return_pct)
else:
bear_returns.append(return_pct)
if bull_returns and bear_returns:
import numpy as np
print(f"\n Bull Market Avg: {np.mean(bull_returns):.2f}%")
print(f" Bear Market Avg: {np.mean(bear_returns):.2f}%")
return True
except Exception as e:
print(f"\n❌ Error in cycle testing: {e}")
import traceback
traceback.print_exc()
return False
# ============================================================================
# MAIN
# ============================================================================
def main():
"""Run all backtest examples."""
print("\n" + "=" * 80)
print(" CRYPTO BACKTESTING - FULL EXECUTION EXAMPLES")
print("=" * 80)
print("\nThis script runs actual backtests with real historical data.")
print("\nRequirements:")
print(" ✓ CCXT installed (pip install ccxt)")
print(" ✓ Internet connection")
print(" ✓ ~5-10 minutes for data fetching and execution\n")
import time
results = {}
start_time = time.time()
# Run examples
print("\n🚀 Starting backtest execution...\n")
results['single'] = run_single_backtest()
time.sleep(1)
results['comparison'] = compare_strategies_example()
time.sleep(1)
results['walk_forward'] = walk_forward_test_example()
time.sleep(1)
results['cycles'] = market_cycle_test_example()
# Summary
elapsed = time.time() - start_time
print_section("EXECUTION SUMMARY")
total = len(results)
passed = sum(1 for r in results.values() if r is True)
for name, result in results.items():
status = "✅ SUCCESS" if result else "❌ FAILED"
print(f"{status:15s} - {name}")
print(f"\nResults: {passed}/{total} examples completed")
print(f"Execution time: {elapsed:.1f} seconds")
if passed == total:
print("\n🎉 All backtest examples completed successfully!")
else:
print(f"\n⚠️ {total - passed} example(s) failed.")
print("\nTroubleshooting:")
print(" 1. Install CCXT: pip install ccxt")
print(" 2. Check internet connection")
print(" 3. Review error messages above")
print("\n📚 Next Steps:")
print(" 1. Review backtest results above")
print(" 2. Try different strategies in examples/crypto_backtest_examples.py")
print(" 3. Integrate with Phase 2 crypto agents")
print(" 4. Calibrate risk parameters based on results")
print(" 5. Proceed to Phase 4: Paper Trading\n")
if __name__ == "__main__":
main()