import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime, timedelta
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Conv1D, MaxPooling1D, Dropout
import tf2onnx
import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras import callbacks
from tensorflow.keras.callbacks import ModelCheckpoint
import time

# Ruta del archivo donde se encuentra el símbolo
file_path = 'C:/Users/jsgas/AppData/Roaming/MetaQuotes/Terminal/24F345EB9F291441AFE537834F9D8A19/MQL5/Files/Files/symbol.txt'
file_path_time = 'C:/Users/jsgas/AppData/Roaming/MetaQuotes/Terminal/24F345EB9F291441AFE537834F9D8A19/MQL5/Files/Files/time.txt'

# Función para leer el símbolo desde el archivo
def read_symbol_from_file():
    try:
        with open(file_path, 'r', encoding='utf-16') as file:
            raw_data = file.read()
            print(f"Raw data from file: {repr(raw_data)}")  # Print raw data
            symbol = raw_data.strip()  # Lee el contenido y elimina espacios en blanco adicionales
            symbol = symbol.replace('\ufeff', '')  # Remove BOM character if present
            print(f"Symbol after stripping whitespace: {symbol}")
            return symbol
    except FileNotFoundError:
        print(f"El archivo {file_path} no existe.")
        return None

def read_time_from_file():
    try:
        with open(file_path_time, 'r', encoding='utf-16') as file:
            raw_data = file.read()
            print(f"Raw data from time file: {repr(raw_data)}")  # Print raw data
            symbol = raw_data.strip()  # Lee el contenido y elimina espacios en blanco adicionales
            symbol = symbol.replace('\ufeff', '')  # Remove BOM character if present
            print(f"Time after stripping whitespace: {symbol}")
            return symbol
    except FileNotFoundError:
        print(f"El archivo {file_path} no existe.")
        return None

def convert_string_to_datetime(date_string):
    # Convertir la cadena de fecha a un objeto datetime
    start_date = datetime.strptime(date_string, "%Y.%m.%d")
    return start_date

# Iniciar el temporizador
start_time = time.time()

# Ejemplo de uso: leer el símbolo desde el archivo
symbol = read_symbol_from_file()
time_str = read_time_from_file()
print(time_str)

start_date = convert_string_to_datetime(time_str)
print(start_date)

if symbol:
    print(f"Símbolo leído desde el archivo: {symbol}")

#start_date = datetime(2024, 1, 1)

# Función para descargar datos desde MT5
def descargar_datos_mt5(symbol, start_date):
    if not mt5.initialize():
        print("initialize() failed")
        mt5.shutdown()
        return None
    
    print("MT5 initialized successfully")
    
    # Verifica si el símbolo es válido
    symbols = mt5.symbols_get()
    symbol_names = [s.name for s in symbols]
    
    if symbol not in symbol_names:
        print(f"Símbolo {symbol} no válido.")
        mt5.shutdown()
        return None

    # Establecer el rango de fechas
    utc_from = start_date

    rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, utc_from, 10000)

    if rates is None or len(rates) == 0:
        print(f"No se pudieron obtener datos para {symbol}.")
        mt5.shutdown()
        return None

    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time', inplace=True)
    
    mt5.shutdown()
    return df['close'].values

# Load data
data = descargar_datos_mt5(symbol, start_date)
if data is not None:
    print(f"Datos descargados correctamente para {symbol}.")
else:
    print("No se pudo obtener datos de MT5.")

if data is None:
    raise SystemExit("No se pudo obtener datos de MT5.")

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
data = scaler.fit_transform(data.reshape(-1, 1))

# Function to create samples from the sequence
def crear_muestras(dataset, pasos_de_tiempo=120):
    X, y = [], []
    for i in range(pasos_de_tiempo, len(dataset)):
        X.append(dataset[i-pasos_de_tiempo:i, 0])
        y.append(dataset[i, 0])
    return np.array(X), np.array(y)

# Prepare training and test data
pasos_de_tiempo = 120
X, y = crear_muestras(data, pasos_de_tiempo)
X = X.reshape(X.shape[0], X.shape[1], 1)  # Reshape for LSTM

# Split the data (80% for training)
split = int(0.8 * len(X))
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

# Train the model
model = Sequential()
model.add(Conv1D(filters=256, kernel_size=2, activation='relu', padding='same', input_shape=(X_train.shape[1], 1)))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(100, return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(100, return_sequences=False))
model.add(Dropout(0.3))
model.add(Dense(units=1, activation='sigmoid'))
model.compile(optimizer='adam', loss='mse', metrics=[tf.keras.metrics.RootMeanSquaredError(name='rmse'), 'mae'])

# Set up early stopping
early_stopping = callbacks.EarlyStopping(
    monitor='val_loss',
    patience=5,
    restore_best_weights=True,
)

# Checkpoint to save the best model
checkpoint = ModelCheckpoint(
    'best_model.h5', 
    monitor='val_loss', 
    save_best_only=True, 
    save_weights_only=False
)

# Model training for 300 epochs
history = model.fit(X_train, y_train, epochs=300, validation_data=(X_test, y_test), batch_size=32, callbacks=[early_stopping, checkpoint], verbose=2)

# Plot the training history
plt.figure(figsize=(10, 5))
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.plot(history.history['rmse'], label='Train RMSE')
plt.plot(history.history['val_rmse'], label='Validation RMSE')
plt.plot(history.history['mae'], label='Train MAE')
plt.plot(history.history['val_mae'], label='Validation MAE')
plt.title('Model Training History ' + str(symbol))
plt.xlabel('Epochs')
plt.ylabel('Loss/RMSE/MAE')
plt.legend()
plt.savefig(str(symbol) + '_plot_graph.png')  # Save the plot as an image file

# Convert the model to ONNX
onnx_model, _ = tf2onnx.convert.from_keras(model, opset=13, output_path='C:/Users/jsgas/AppData/Roaming/MetaQuotes/Terminal/24F345EB9F291441AFE537834F9D8A19/MQL5/Files/Files/model_' + str(symbol) +'_'+ str(time_str) + '.onnx')
print('ONNX model saved as model_' + str(symbol) +'_'+ str(time_str) + '.onnx')

# Evaluate the model
train_loss, train_rmse, train_mae = model.evaluate(X_train, y_train, verbose=0)
test_loss, test_rmse, test_mae = model.evaluate(X_test, y_test, verbose=0)

# Calculate additional metrics
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)

train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)

train_mae = mean_absolute_error(y_train, y_train_pred)
test_mae = mean_absolute_error(y_test, y_test_pred)

print(f"train_loss={train_loss:.3f}, train_rmse={train_rmse:.3f}, train_mae={train_mae:.3f}, train_r2={train_r2:.3f}, train_mse={train_mse:.3f}")
print(f"test_loss={test_loss:.3f}, test_rmse={test_rmse:.3f}, test_mae={test_mae:.3f}, test_r2={test_r2:.3f}, test_mse={test_mse:.3f}")

# Guardar las métricas en un archivo txt
with open(str(symbol) + '_metrics.txt', 'w') as f:
    f.write(f"train_loss={train_loss:.3f}, train_rmse={train_rmse:.3f}, train_mae={train_mae:.3f}, train_r2={train_r2:.3f}, train_mse={train_mse:.3f}\n")
    f.write(f"test_loss={test_loss:.3f}, test_rmse={test_rmse:.3f}, test_mae={test_mae:.3f}, test_r2={test_r2:.3f}, test_mse={test_mse:.3f}\n")

# Detener el temporizador y mostrar el tiempo de ejecución
end_time = time.time()
execution_time = end_time - start_time
print(f"Tiempo de ejecución del script: {execution_time:.2f} segundos")
