289 lines
12 KiB
Python
289 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Analyze Aligned Trades in Detail
|
|
================================
|
|
|
|
This script performs a detailed analysis of the aligned trades to understand
|
|
why there's still a large performance difference between the strategies.
|
|
"""
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from datetime import datetime
|
|
|
|
def load_aligned_trades():
|
|
"""Load the aligned trade files."""
|
|
|
|
print("📊 LOADING ALIGNED TRADES")
|
|
print("=" * 60)
|
|
|
|
original_file = "../results/trades_original_aligned.csv"
|
|
incremental_file = "../results/trades_incremental_aligned.csv"
|
|
|
|
original_df = pd.read_csv(original_file)
|
|
original_df['entry_time'] = pd.to_datetime(original_df['entry_time'])
|
|
original_df['exit_time'] = pd.to_datetime(original_df['exit_time'])
|
|
|
|
incremental_df = pd.read_csv(incremental_file)
|
|
incremental_df['entry_time'] = pd.to_datetime(incremental_df['entry_time'])
|
|
incremental_df['exit_time'] = pd.to_datetime(incremental_df['exit_time'])
|
|
|
|
print(f"Aligned original trades: {len(original_df)}")
|
|
print(f"Incremental trades: {len(incremental_df)}")
|
|
|
|
return original_df, incremental_df
|
|
|
|
def analyze_trade_timing_differences(original_df, incremental_df):
|
|
"""Analyze timing differences between aligned trades."""
|
|
|
|
print(f"\n🕐 ANALYZING TRADE TIMING DIFFERENCES")
|
|
print("=" * 60)
|
|
|
|
# Get buy signals
|
|
orig_buys = original_df[original_df['type'] == 'BUY'].copy()
|
|
inc_buys = incremental_df[incremental_df['type'] == 'BUY'].copy()
|
|
|
|
print(f"Original buy signals: {len(orig_buys)}")
|
|
print(f"Incremental buy signals: {len(inc_buys)}")
|
|
|
|
# Compare first 10 trades
|
|
print(f"\n📋 FIRST 10 ALIGNED TRADES:")
|
|
print("-" * 80)
|
|
print("Original Strategy:")
|
|
for i, (idx, trade) in enumerate(orig_buys.head(10).iterrows()):
|
|
print(f" {i+1:2d}. {trade['entry_time']} - ${trade['entry_price']:8.0f}")
|
|
|
|
print("\nIncremental Strategy:")
|
|
for i, (idx, trade) in enumerate(inc_buys.head(10).iterrows()):
|
|
print(f" {i+1:2d}. {trade['entry_time']} - ${trade['entry_price']:8.0f}")
|
|
|
|
# Find timing differences
|
|
print(f"\n⏰ TIMING ANALYSIS:")
|
|
print("-" * 60)
|
|
|
|
# Group by date to find same-day trades
|
|
orig_buys['date'] = orig_buys['entry_time'].dt.date
|
|
inc_buys['date'] = inc_buys['entry_time'].dt.date
|
|
|
|
common_dates = set(orig_buys['date']) & set(inc_buys['date'])
|
|
print(f"Common trading dates: {len(common_dates)}")
|
|
|
|
timing_diffs = []
|
|
price_diffs = []
|
|
|
|
for date in sorted(list(common_dates))[:10]:
|
|
orig_day_trades = orig_buys[orig_buys['date'] == date]
|
|
inc_day_trades = inc_buys[inc_buys['date'] == date]
|
|
|
|
if len(orig_day_trades) > 0 and len(inc_day_trades) > 0:
|
|
orig_time = orig_day_trades.iloc[0]['entry_time']
|
|
inc_time = inc_day_trades.iloc[0]['entry_time']
|
|
orig_price = orig_day_trades.iloc[0]['entry_price']
|
|
inc_price = inc_day_trades.iloc[0]['entry_price']
|
|
|
|
time_diff = (inc_time - orig_time).total_seconds() / 60 # minutes
|
|
price_diff = ((inc_price - orig_price) / orig_price) * 100
|
|
|
|
timing_diffs.append(time_diff)
|
|
price_diffs.append(price_diff)
|
|
|
|
print(f" {date}: Original {orig_time.strftime('%H:%M')} (${orig_price:.0f}), "
|
|
f"Incremental {inc_time.strftime('%H:%M')} (${inc_price:.0f}), "
|
|
f"Diff: {time_diff:+.0f}min, {price_diff:+.2f}%")
|
|
|
|
if timing_diffs:
|
|
avg_time_diff = np.mean(timing_diffs)
|
|
avg_price_diff = np.mean(price_diffs)
|
|
print(f"\nAverage timing difference: {avg_time_diff:+.1f} minutes")
|
|
print(f"Average price difference: {avg_price_diff:+.2f}%")
|
|
|
|
def analyze_profit_distributions(original_df, incremental_df):
|
|
"""Analyze profit distributions between strategies."""
|
|
|
|
print(f"\n💰 ANALYZING PROFIT DISTRIBUTIONS")
|
|
print("=" * 60)
|
|
|
|
# Get sell signals (exits)
|
|
orig_exits = original_df[original_df['type'].str.contains('EXIT|EOD', na=False)].copy()
|
|
inc_exits = incremental_df[incremental_df['type'].str.contains('EXIT|EOD', na=False)].copy()
|
|
|
|
orig_profits = orig_exits['profit_pct'].values * 100
|
|
inc_profits = inc_exits['profit_pct'].values * 100
|
|
|
|
print(f"Original strategy trades: {len(orig_profits)}")
|
|
print(f" Winning trades: {len(orig_profits[orig_profits > 0])} ({len(orig_profits[orig_profits > 0])/len(orig_profits)*100:.1f}%)")
|
|
print(f" Average profit: {np.mean(orig_profits):.2f}%")
|
|
print(f" Best trade: {np.max(orig_profits):.2f}%")
|
|
print(f" Worst trade: {np.min(orig_profits):.2f}%")
|
|
print(f" Std deviation: {np.std(orig_profits):.2f}%")
|
|
|
|
print(f"\nIncremental strategy trades: {len(inc_profits)}")
|
|
print(f" Winning trades: {len(inc_profits[inc_profits > 0])} ({len(inc_profits[inc_profits > 0])/len(inc_profits)*100:.1f}%)")
|
|
print(f" Average profit: {np.mean(inc_profits):.2f}%")
|
|
print(f" Best trade: {np.max(inc_profits):.2f}%")
|
|
print(f" Worst trade: {np.min(inc_profits):.2f}%")
|
|
print(f" Std deviation: {np.std(inc_profits):.2f}%")
|
|
|
|
# Analyze profit ranges
|
|
print(f"\n📊 PROFIT RANGE ANALYSIS:")
|
|
print("-" * 60)
|
|
|
|
ranges = [(-100, -5), (-5, -1), (-1, 0), (0, 1), (1, 5), (5, 100)]
|
|
range_names = ["< -5%", "-5% to -1%", "-1% to 0%", "0% to 1%", "1% to 5%", "> 5%"]
|
|
|
|
for i, (low, high) in enumerate(ranges):
|
|
orig_count = len(orig_profits[(orig_profits >= low) & (orig_profits < high)])
|
|
inc_count = len(inc_profits[(inc_profits >= low) & (inc_profits < high)])
|
|
|
|
orig_pct = (orig_count / len(orig_profits)) * 100 if len(orig_profits) > 0 else 0
|
|
inc_pct = (inc_count / len(inc_profits)) * 100 if len(inc_profits) > 0 else 0
|
|
|
|
print(f" {range_names[i]:>10}: Original {orig_count:3d} ({orig_pct:4.1f}%), "
|
|
f"Incremental {inc_count:3d} ({inc_pct:4.1f}%)")
|
|
|
|
return orig_profits, inc_profits
|
|
|
|
def analyze_trade_duration(original_df, incremental_df):
|
|
"""Analyze trade duration differences."""
|
|
|
|
print(f"\n⏱️ ANALYZING TRADE DURATION")
|
|
print("=" * 60)
|
|
|
|
# Get complete trades (buy + sell pairs)
|
|
orig_buys = original_df[original_df['type'] == 'BUY'].copy()
|
|
orig_exits = original_df[original_df['type'].str.contains('EXIT|EOD', na=False)].copy()
|
|
|
|
inc_buys = incremental_df[incremental_df['type'] == 'BUY'].copy()
|
|
inc_exits = incremental_df[incremental_df['type'].str.contains('EXIT|EOD', na=False)].copy()
|
|
|
|
# Calculate durations
|
|
orig_durations = []
|
|
inc_durations = []
|
|
|
|
for i, buy in orig_buys.iterrows():
|
|
exits = orig_exits[orig_exits['entry_time'] == buy['entry_time']]
|
|
if len(exits) > 0:
|
|
duration = (exits.iloc[0]['exit_time'] - buy['entry_time']).total_seconds() / 3600 # hours
|
|
orig_durations.append(duration)
|
|
|
|
for i, buy in inc_buys.iterrows():
|
|
exits = inc_exits[inc_exits['entry_time'] == buy['entry_time']]
|
|
if len(exits) > 0:
|
|
duration = (exits.iloc[0]['exit_time'] - buy['entry_time']).total_seconds() / 3600 # hours
|
|
inc_durations.append(duration)
|
|
|
|
print(f"Original strategy:")
|
|
print(f" Average duration: {np.mean(orig_durations):.1f} hours")
|
|
print(f" Median duration: {np.median(orig_durations):.1f} hours")
|
|
print(f" Min duration: {np.min(orig_durations):.1f} hours")
|
|
print(f" Max duration: {np.max(orig_durations):.1f} hours")
|
|
|
|
print(f"\nIncremental strategy:")
|
|
print(f" Average duration: {np.mean(inc_durations):.1f} hours")
|
|
print(f" Median duration: {np.median(inc_durations):.1f} hours")
|
|
print(f" Min duration: {np.min(inc_durations):.1f} hours")
|
|
print(f" Max duration: {np.max(inc_durations):.1f} hours")
|
|
|
|
return orig_durations, inc_durations
|
|
|
|
def create_detailed_comparison_plots(original_df, incremental_df, orig_profits, inc_profits):
|
|
"""Create detailed comparison plots."""
|
|
|
|
print(f"\n📊 CREATING DETAILED COMPARISON PLOTS")
|
|
print("=" * 60)
|
|
|
|
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
|
|
|
|
# Plot 1: Profit distribution comparison
|
|
ax1.hist(orig_profits, bins=30, alpha=0.7, label='Original', color='blue', density=True)
|
|
ax1.hist(inc_profits, bins=30, alpha=0.7, label='Incremental', color='red', density=True)
|
|
ax1.set_title('Profit Distribution Comparison')
|
|
ax1.set_xlabel('Profit (%)')
|
|
ax1.set_ylabel('Density')
|
|
ax1.legend()
|
|
ax1.grid(True, alpha=0.3)
|
|
|
|
# Plot 2: Cumulative profit over time
|
|
orig_exits = original_df[original_df['type'].str.contains('EXIT|EOD', na=False)].copy()
|
|
inc_exits = incremental_df[incremental_df['type'].str.contains('EXIT|EOD', na=False)].copy()
|
|
|
|
orig_cumulative = np.cumsum(orig_exits['profit_pct'].values) * 100
|
|
inc_cumulative = np.cumsum(inc_exits['profit_pct'].values) * 100
|
|
|
|
ax2.plot(range(len(orig_cumulative)), orig_cumulative, label='Original', color='blue', linewidth=2)
|
|
ax2.plot(range(len(inc_cumulative)), inc_cumulative, label='Incremental', color='red', linewidth=2)
|
|
ax2.set_title('Cumulative Profit Over Trades')
|
|
ax2.set_xlabel('Trade Number')
|
|
ax2.set_ylabel('Cumulative Profit (%)')
|
|
ax2.legend()
|
|
ax2.grid(True, alpha=0.3)
|
|
|
|
# Plot 3: Trade timing scatter
|
|
orig_buys = original_df[original_df['type'] == 'BUY']
|
|
inc_buys = incremental_df[incremental_df['type'] == 'BUY']
|
|
|
|
ax3.scatter(orig_buys['entry_time'], orig_buys['entry_price'],
|
|
alpha=0.6, label='Original', color='blue', s=20)
|
|
ax3.scatter(inc_buys['entry_time'], inc_buys['entry_price'],
|
|
alpha=0.6, label='Incremental', color='red', s=20)
|
|
ax3.set_title('Trade Entry Timing')
|
|
ax3.set_xlabel('Date')
|
|
ax3.set_ylabel('Entry Price ($)')
|
|
ax3.legend()
|
|
ax3.grid(True, alpha=0.3)
|
|
|
|
# Plot 4: Profit vs trade number
|
|
ax4.scatter(range(len(orig_profits)), orig_profits, alpha=0.6, label='Original', color='blue', s=20)
|
|
ax4.scatter(range(len(inc_profits)), inc_profits, alpha=0.6, label='Incremental', color='red', s=20)
|
|
ax4.set_title('Individual Trade Profits')
|
|
ax4.set_xlabel('Trade Number')
|
|
ax4.set_ylabel('Profit (%)')
|
|
ax4.legend()
|
|
ax4.grid(True, alpha=0.3)
|
|
ax4.axhline(y=0, color='black', linestyle='--', alpha=0.5)
|
|
|
|
plt.tight_layout()
|
|
plt.savefig('../results/detailed_aligned_analysis.png', dpi=300, bbox_inches='tight')
|
|
print("Detailed analysis plot saved: ../results/detailed_aligned_analysis.png")
|
|
|
|
def main():
|
|
"""Main analysis function."""
|
|
|
|
print("🔍 DETAILED ANALYSIS OF ALIGNED TRADES")
|
|
print("=" * 80)
|
|
|
|
try:
|
|
# Load aligned trades
|
|
original_df, incremental_df = load_aligned_trades()
|
|
|
|
# Analyze timing differences
|
|
analyze_trade_timing_differences(original_df, incremental_df)
|
|
|
|
# Analyze profit distributions
|
|
orig_profits, inc_profits = analyze_profit_distributions(original_df, incremental_df)
|
|
|
|
# Analyze trade duration
|
|
analyze_trade_duration(original_df, incremental_df)
|
|
|
|
# Create detailed plots
|
|
create_detailed_comparison_plots(original_df, incremental_df, orig_profits, inc_profits)
|
|
|
|
print(f"\n🎯 KEY FINDINGS:")
|
|
print("=" * 80)
|
|
print("1. Check if strategies are trading at different times within the same day")
|
|
print("2. Compare profit distributions to see if one strategy has better trades")
|
|
print("3. Analyze trade duration differences")
|
|
print("4. Look for systematic differences in entry/exit timing")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ Error during analysis: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
if __name__ == "__main__":
|
|
success = main()
|
|
exit(0 if success else 1) |