270 lines
9.1 KiB
Python
270 lines
9.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Strategy Comparison Script
|
|
Runs both strategies and compares their performance.
|
|
"""
|
|
|
|
import sys
|
|
|
|
# Add src directory to path for imports
|
|
sys.path.append('src')
|
|
sys.path.append('config')
|
|
|
|
from src.logger_setup import setup_logging
|
|
from src.data_handler import DataHandler
|
|
from src.strategy_factory import get_strategy
|
|
import logging
|
|
|
|
def run_strategy_comparison():
|
|
"""Compare both strategies side by side"""
|
|
setup_logging()
|
|
logger = logging.getLogger(__name__)
|
|
|
|
print("=" * 80)
|
|
print("STRATEGY COMPARISON - CONSERVATIVE vs ENHANCED")
|
|
print("=" * 80)
|
|
|
|
try:
|
|
# Get data once for both strategies
|
|
data_handler = DataHandler()
|
|
bars = data_handler.get_historical_data(days=180)
|
|
|
|
if bars.empty:
|
|
print("❌ No historical data available")
|
|
return
|
|
|
|
# Calculate technical indicators
|
|
bars = data_handler.calculate_technical_indicators(bars)
|
|
|
|
strategies = ['conservative', 'enhanced']
|
|
results = {}
|
|
|
|
for strategy_type in strategies:
|
|
print(f"\n📊 Running {strategy_type.upper()} strategy...")
|
|
|
|
try:
|
|
# Create strategy
|
|
strategy = get_strategy(strategy_type)
|
|
|
|
# Generate signals
|
|
strategy_bars = bars.copy()
|
|
strategy_bars = strategy.generate_signals(strategy_bars)
|
|
|
|
# Run simulation
|
|
results[strategy_type] = run_strategy_simulation(strategy_bars, strategy)
|
|
|
|
print(f"✅ {strategy.name} completed")
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error running {strategy_type} strategy: {e}")
|
|
continue
|
|
|
|
# Display comparison
|
|
display_strategy_comparison(results)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in strategy comparison: {e}")
|
|
print(f"❌ Comparison failed: {e}")
|
|
|
|
def run_strategy_simulation(bars, strategy):
|
|
"""Run simulation for a strategy"""
|
|
|
|
# Position tracking
|
|
position = 0
|
|
entry_price = None
|
|
highest_price_since_entry = None
|
|
bars_since_entry = 0
|
|
trades = []
|
|
|
|
bars['position'] = 0
|
|
|
|
for i in range(1, len(bars)):
|
|
current_price = bars['close'].iloc[i]
|
|
|
|
if bars['buy_signal'].iloc[i] and position == 0:
|
|
# Entry conditions
|
|
if strategy.get_entry_conditions(bars, i):
|
|
position = 1
|
|
entry_price = current_price
|
|
highest_price_since_entry = current_price
|
|
bars_since_entry = 0
|
|
|
|
elif position == 1:
|
|
bars_since_entry += 1
|
|
|
|
if current_price > highest_price_since_entry:
|
|
highest_price_since_entry = current_price
|
|
|
|
# Exit conditions
|
|
should_exit, exit_reason = strategy.should_exit_position(
|
|
current_price=current_price,
|
|
entry_price=entry_price,
|
|
highest_price=highest_price_since_entry,
|
|
bars_since_entry=bars_since_entry,
|
|
sell_signal=bars['sell_signal'].iloc[i],
|
|
ema_trend=bars['ema_trend'].iloc[i]
|
|
)
|
|
|
|
if should_exit:
|
|
# Record trade
|
|
profit_pct = (current_price / entry_price - 1)
|
|
trades.append({
|
|
'entry_price': entry_price,
|
|
'exit_price': current_price,
|
|
'profit_pct': profit_pct,
|
|
'bars_held': bars_since_entry,
|
|
'exit_reason': exit_reason
|
|
})
|
|
|
|
position = 0
|
|
entry_price = None
|
|
highest_price_since_entry = None
|
|
bars_since_entry = 0
|
|
|
|
bars.iloc[i, bars.columns.get_loc('position')] = position
|
|
|
|
# Calculate returns
|
|
bars['market_return'] = bars['close'].pct_change()
|
|
bars['strategy_return'] = bars['position'].shift(1) * bars['market_return']
|
|
|
|
# Apply costs
|
|
trade_cost = 0.0005
|
|
bars['position_change'] = bars['position'].diff().abs()
|
|
bars['costs'] = bars['position_change'] * trade_cost
|
|
bars['strategy_return'] = bars['strategy_return'] - bars['costs']
|
|
|
|
# Cumulative returns
|
|
bars['cum_strategy'] = (1 + bars['strategy_return']).cumprod() - 1
|
|
bars['cum_market'] = (1 + bars['market_return']).cumprod() - 1
|
|
|
|
# Calculate metrics
|
|
total_return = bars['cum_strategy'].iloc[-1] * 100
|
|
market_return = bars['cum_market'].iloc[-1] * 100
|
|
|
|
if len(trades) > 0:
|
|
import numpy as np
|
|
trade_returns = [t['profit_pct'] for t in trades]
|
|
trade_returns = np.array(trade_returns)
|
|
|
|
win_rate = (trade_returns > 0).mean() * 100
|
|
avg_win = trade_returns[trade_returns > 0].mean() * 100 if (trade_returns > 0).any() else 0
|
|
avg_loss = trade_returns[trade_returns < 0].mean() * 100 if (trade_returns < 0).any() else 0
|
|
max_win = trade_returns.max() * 100
|
|
max_loss = trade_returns.min() * 100
|
|
|
|
total_wins = trade_returns[trade_returns > 0].sum()
|
|
total_losses = abs(trade_returns[trade_returns < 0].sum())
|
|
profit_factor = total_wins / total_losses if total_losses > 0 else float('inf')
|
|
else:
|
|
win_rate = avg_win = avg_loss = max_win = max_loss = profit_factor = 0
|
|
|
|
# Max drawdown
|
|
running_max = bars['cum_strategy'].cummax()
|
|
drawdown = (bars['cum_strategy'] - running_max)
|
|
max_drawdown = drawdown.min() * 100
|
|
|
|
# Sharpe ratio
|
|
if bars['strategy_return'].std() > 0:
|
|
import numpy as np
|
|
sharpe = bars['strategy_return'].mean() / bars['strategy_return'].std() * np.sqrt(365 * 24)
|
|
else:
|
|
sharpe = 0
|
|
|
|
return {
|
|
'total_return': total_return,
|
|
'market_return': market_return,
|
|
'outperformance': total_return - market_return,
|
|
'sharpe_ratio': sharpe,
|
|
'max_drawdown': max_drawdown,
|
|
'total_trades': len(trades),
|
|
'win_rate': win_rate,
|
|
'profit_factor': profit_factor,
|
|
'avg_win': avg_win,
|
|
'avg_loss': avg_loss,
|
|
'max_win': max_win,
|
|
'max_loss': max_loss,
|
|
'time_in_market': (bars['position'] > 0).mean() * 100,
|
|
'trades': trades
|
|
}
|
|
|
|
def display_strategy_comparison(results):
|
|
"""Display side-by-side comparison"""
|
|
|
|
if len(results) < 2:
|
|
print("❌ Need at least 2 strategies to compare")
|
|
return
|
|
|
|
print("\n" + "=" * 100)
|
|
print("STRATEGY COMPARISON RESULTS")
|
|
print("=" * 100)
|
|
|
|
# Header
|
|
print(f"{'Metric':<25} {'Conservative':<20} {'Enhanced':<20} {'Difference':<15}")
|
|
print("-" * 100)
|
|
|
|
conservative = results.get('conservative', {})
|
|
enhanced = results.get('enhanced', {})
|
|
|
|
metrics = [
|
|
('Total Return', 'total_return', '%'),
|
|
('Market Return', 'market_return', '%'),
|
|
('Outperformance', 'outperformance', '%'),
|
|
('Sharpe Ratio', 'sharpe_ratio', ''),
|
|
('Max Drawdown', 'max_drawdown', '%'),
|
|
('Total Trades', 'total_trades', ''),
|
|
('Win Rate', 'win_rate', '%'),
|
|
('Profit Factor', 'profit_factor', ''),
|
|
('Avg Win', 'avg_win', '%'),
|
|
('Avg Loss', 'avg_loss', '%'),
|
|
('Max Win', 'max_win', '%'),
|
|
('Max Loss', 'max_loss', '%'),
|
|
('Time in Market', 'time_in_market', '%'),
|
|
]
|
|
|
|
for display_name, key, unit in metrics:
|
|
cons_val = conservative.get(key, 0)
|
|
enh_val = enhanced.get(key, 0)
|
|
diff = enh_val - cons_val
|
|
|
|
if unit == '%':
|
|
cons_str = f"{cons_val:>8.2f}%"
|
|
enh_str = f"{enh_val:>8.2f}%"
|
|
diff_str = f"{diff:>+8.2f}%"
|
|
else:
|
|
cons_str = f"{cons_val:>8.2f}"
|
|
enh_str = f"{enh_val:>8.2f}"
|
|
diff_str = f"{diff:>+8.2f}"
|
|
|
|
print(f"{display_name:<25} {cons_str:<20} {enh_str:<20} {diff_str:<15}")
|
|
|
|
print("=" * 100)
|
|
|
|
# Summary
|
|
print("\n📋 SUMMARY:")
|
|
|
|
if enhanced.get('total_return', 0) > conservative.get('total_return', 0):
|
|
winner = "Enhanced"
|
|
return_diff = enhanced.get('total_return', 0) - conservative.get('total_return', 0)
|
|
else:
|
|
winner = "Conservative"
|
|
return_diff = conservative.get('total_return', 0) - enhanced.get('total_return', 0)
|
|
|
|
print(f"🏆 Best performing strategy: {winner} (+{return_diff:.2f}% return)")
|
|
|
|
# Risk comparison
|
|
cons_risk = abs(conservative.get('max_drawdown', 0))
|
|
enh_risk = abs(enhanced.get('max_drawdown', 0))
|
|
|
|
if cons_risk < enh_risk:
|
|
print(f"🛡️ Lower risk strategy: Conservative ({cons_risk:.2f}% max drawdown)")
|
|
else:
|
|
print(f"🛡️ Lower risk strategy: Enhanced ({enh_risk:.2f}% max drawdown)")
|
|
|
|
print("\n💡 RECOMMENDATIONS:")
|
|
print("• Conservative: Better for letting big winners run, more relaxed exits")
|
|
print("• Enhanced: Better risk control, tighter stops for early losses")
|
|
print("• Choose based on your risk tolerance and market conditions")
|
|
|
|
if __name__ == "__main__":
|
|
run_strategy_comparison()
|