import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import MetaTrader5 as mt5
import random

# Connect to MetaTrader 5
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    quit()

# Define the symbol and time parameters
symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_D1
start = pd.Timestamp("2020-01-01")
end = pd.Timestamp("2021-01-01")

# Request historical data
rates = mt5.copy_rates_range(symbol, timeframe, start, end)
if rates is None:
    print("copy_rates_range() failed, error code =", mt5.last_error())
    mt5.shutdown()
    quit()

# Create DataFrame
data = pd.DataFrame(rates)
data['time'] = pd.to_datetime(data['time'], unit='s')
data.set_index('time', inplace=True)
data = data[['close']]  # Use only the closing price
data.rename(columns={'close': 'Close'}, inplace=True)

# Disconnect from MetaTrader 5
mt5.shutdown()

# Function to calculate strategy performance
def backtest_strategy(short_window, long_window, data):
    data = data.copy()  # Create a copy of the original DataFrame
    data['Short_MA'] = data['Close'].rolling(window=short_window).mean()
    data['Long_MA'] = data['Close'].rolling(window=long_window).mean()
    data.dropna(inplace=True)  # Drop rows with NaN values after calculating moving averages
    data['Signal'] = 0
    data.loc[data.index[short_window:], 'Signal'] = np.where(
        data['Short_MA'][short_window:] > data['Long_MA'][short_window:], 1, 0)
    data['Position'] = data['Signal'].diff()
    
    initial_capital = 10000.0
    positions = 100 * data['Position']
    portfolio = pd.DataFrame(index=data.index)
    portfolio['Holdings'] = positions.cumsum() * data['Close']
    portfolio['Cash'] = initial_capital - (positions * data['Close']).cumsum()
    portfolio['Total'] = portfolio['Holdings'] + portfolio['Cash']
    portfolio['Returns'] = portfolio['Total'].pct_change()
    
    performance = portfolio['Total'].iloc[-1]
    risk = portfolio['Returns'].std()
    
    if np.isnan(performance) or np.isnan(risk) or risk == 0:
        return -np.inf, np.inf  # Penalize NaN values or zero risk
    
    return performance, risk

# Define parameter ranges
short_window_range = range(5, 20)
long_window_range = range(20, 50)

# Random search optimization
results = []
num_iterations = 100  # Number of iterations for random search

for _ in range(num_iterations):
    short_window = random.choice(short_window_range)
    long_window = random.choice(long_window_range)
    if short_window < long_window:
        performance, risk = backtest_strategy(short_window, long_window, data)
        results.append((short_window, long_window, performance, risk))

# Convert results to DataFrame for analysis
results_df = pd.DataFrame(results, columns=['Short_Window', 'Long_Window', 'Performance', 'Risk'])

# Visualize results
plt.figure(figsize=(14, 7))

# Performance chart
plt.subplot(1, 2, 1)
plt.scatter(results_df['Short_Window'], results_df['Long_Window'], c=results_df['Performance'], cmap='viridis')
plt.colorbar(label='Performance')
plt.xlabel('Short Window')
plt.ylabel('Long Window')
plt.title('Performance of Strategy')

# Risk chart
plt.subplot(1, 2, 2)
plt.scatter(results_df['Short_Window'], results_df['Long_Window'], c=results_df['Risk'], cmap='viridis')
plt.colorbar(label='Risk')
plt.xlabel('Short Window')
plt.ylabel('Long Window')
plt.title('Risk of Strategy')

plt.tight_layout()
plt.show()

# Find the best parameters based on performance
best_params = results_df.loc[results_df['Performance'].idxmax()]
print(f"Best parameters: Short = {best_params['Short_Window']}, Long = {best_params['Long_Window']}")
print(f"Best performance: {best_params['Performance']}, Risk: {best_params['Risk']}")
