import pandas as pd
import requests
import re
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
import random
# Paramètres de la commande
instrument = "eurusd"
type_de_donnees = "h1"
format_fichier = "csv"
debut = pd.Timestamp(2023, 1,1)
fin = pd.Timestamp(2024, 1, 1)

content_dir = Path("/content")
commande = f"npx dukascopy-node -i {instrument} -from {debut:%Y-%m-%d} -to {fin:%Y-%m-%d} -v {True} -t {type_de_donnees} -f {format_fichier}"

try:
    resultat = subprocess.run(commande, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
    sortie_commande = resultat.stdout
    #print(sortie_commande)

    # Extraction du nom du fichier CSV et traitement des données
    match = re.search(r"File saved: (\S+)", sortie_commande)
    if match:
        chemin_fichier_csv = match.group(1)
        chemin_fichier_csv = content_dir / chemin_fichier_csv  # Avec pathlib, nous assurons l’uniformité du chemin
        print("Chemin du fichier CSV:", chemin_fichier_csv)
        try:
            donnees = pd.read_csv(chemin_fichier_csv)
            donnees['timestamp'] = pd.to_datetime(donnees['timestamp'], unit='ms').dt.strftime('%Y-%m-%d %H:%M')
            donnees = donnees.rename(columns={'timestamp': 'timestamp'})
            donnees.to_csv(chemin_fichier_csv, index=False)
            print(f"Le fichier CSV a été mis à jour avec les timestamps formatés : {chemin_fichier_csv}")
            print(donnees.head())
            print(donnees)
        except Exception as e:
            print("Erreur lors de la lecture ou de la conversion du fichier CSV:", e)
    else:
        print("Le nom du fichier n’a pas pu être extrait.")
except subprocess.CalledProcessError as e:
    print("Erreur lors de l’exécution de la commande:", e)


import numpy as np
def get_close_prices_around_time(donnees, specific_time, hours_before=30, hours_after=30):
    '''
    Renvoie les prix de fermeture des ‘hours_before’ heures précédentes et des ‘hours_after’ heures suivantes
    autour d’une heure ‘specific_time’ donnée, chaque entrée correspondant à une heure complète.

    :param donnees: DataFrame contenant les données horaires avec une colonne ‘timestamp’ et une colonne ‘close’.
    :param specific_time: pd.Timestamp représentant l’heure précise pour laquelle on veut les prix de fermeture.
    :param hours_before: Nombre d’heures avant l’heure spécifique.
    :param hours_after: Nombre d’heures après l’heure spécifique.
    :return: DataFrame avec les prix de fermeture pour each hour autour de ‘specific_time’
    '''

    if isinstance(specific_time, str):
        specific_time = pd.to_datetime(specific_time)

    start_time = specific_time - pd.Timedelta(hours=hours_before)
    end_time = specific_time + pd.Timedelta(hours=hours_after)

    last_friday_20h = specific_time - pd.Timedelta(days=(specific_time.weekday() - 4) % 7)
    last_friday_20h = last_friday_20h.replace(hour=20, minute=0, second=0, microsecond=0)
    if specific_time.weekday() < 4 or (specific_time.weekday() == 4 and specific_time.hour < 20):
        last_friday_20h -= pd.Timedelta(weeks=1)

    last_friday_row = donnees[donnees['timestamp'] == last_friday_20h.strftime('%Y-%m-%d %H:%M')]
    #print(last_friday_row)
    last_friday_close = last_friday_row['close'].iloc[0] if not last_friday_row.empty else np.nan

    #print('last_friday_21h:', last_friday_21h)
    #print('donnees timestamp:', donnees['timestamp'].head())
    #print('donnees types:', donnees.dtypes)
    #print('Recherche pour:', last_friday_21h.strftime('%Y-%m-%d %H:%M'))
    #print('Correspondances trouvées:', len(last_friday_row))

    # Enregistrement complet des heures entre start_time et end_time
    full_time_range = pd.date_range(start=start_time, end=end_time, freq='H')

    donnees['timestamp'] = pd.to_datetime(donnees['timestamp'])

    # Préparation d’un DataFrame avec la série temporelle complète pour garantir toutes les heures
    full_time_frame = pd.DataFrame({'timestamp': full_time_range})

    # Fusionner les données avec la série temporelle complète
    merged_data = pd.merge(full_time_frame, donnees, on='timestamp', how='left')

    # Si la première valeur est NaN, utilisée last_friday_close comme valeur initiale pour le remplissage forward.
    if pd.isna(merged_data['close'].iloc[0]) and not np.isnan(last_friday_close):
        merged_data.at[merged_data.index[0], 'close'] = last_friday_close

    # Remplacer les valeurs manquantes par la valeur disponible la plus proche dans le temps (remplissage en avant).
    merged_data['close'].fillna(method='ffill', inplace=True)

    # Creating a list representing the days of the week with 0, except for the day of the ‘specific_time’
    weekday_list = [0] * 7
    weekday_list[specific_time.weekday()] = 1

    return weekday_list + merged_data['close'].tolist()

#On genère le dataset
Nombre_inputs = 7 +1000
Nombre_targets = 50
Taille_dataset = 10000
# Exemple d’utilisation (suppose que les données ont été correctement chargées dans ‘donnees’):
def get_random_time(annee = [2023]):
  mois = random.randint(1,12)
  if mois in [1,3,5,7,8,10,12]:
    jour = random.randint(1,31)
  elif mois in [4,6,9,11]:
    jour = random.randint(1,30)
  else:
    jour = random.randint(1,28)
  heure = random.randint(0,23)
  annee = annee[random.randint(0,len(annee)-1)]
  return f'{annee}-{mois}-{jour} {heure}:00'
hours_before = Nombre_inputs -7
hours_after = Nombre_targets -1 
#specific_time = pd.Timestamp('2024-01-15 12:00')
specific_time = pd.Timestamp(get_random_time())
close_prices_around = get_close_prices_around_time(donnees, specific_time, hours_before,hours_after)
data = [get_close_prices_around_time(donnees, pd.Timestamp(get_random_time()), hours_before,hours_after) for d in range(Taille_dataset)]
print(data)
targets = [d[hours_before+7:] for d in data]
inputs = [d[:hours_before+7] for d in data]
for input in range(len(inputs)):
  if  pd.isna(inputs[input][7]):
    inputs[input]= [0. for _ in range(hours_before +7 )]
for target in range(len(targets)):
  if pd.isna(targets[target][0]):
    targets[target] = [0. for _ in range(1+  hours_after)]
'''for input in inputs:
  print(input)
for target in targets:
  print(target)'''

# Matplotlib
import matplotlib.pyplot as plt
# Tensorflow
import tensorflow as tf
# Numpy and Pandas
import numpy as np
import pandas as pd
# Ohter import
import sys
import random
from sklearn.preprocessing import StandardScaler

assert hasattr(tf, "function") # Be sure to use tensorflow 2.0


# Créer un modèle séquentiel
model = tf.keras.Sequential()
# Flatten
model = tf.keras.models.Sequential()

# Add the layers
model.add(tf.keras.layers.Dense(Nombre_inputs, input_shape=(Nombre_inputs,), activation='relu'))
#model.add(tf.keras.layers.Dense(300, activation='relu'))
model.add(tf.keras.layers.Dense(200, activation='relu'))
model.add(tf.keras.layers.Dense(Nombre_targets, activation='linear'))

'''model_output = model.predict([[1 for _ in range(30)]])
print(model_output)'''

# Afficher le résumé du modèle
model.summary()

# Compile the model
model.compile(
    loss="mean_squared_error",
    optimizer="adam",
    metrics=["accuracy"]
)

#Chercher le dataset
print(len(inputs[0]))
inputs = np.array(inputs)*10
targets = np.array(targets)*10

# Reshape the dataset and convert to float
inputs = inputs.reshape(-1, Nombre_inputs)
targets = targets.reshape(-1, Nombre_targets)
inputs = inputs.astype(float)
targets = targets.astype(float)

scaler = StandardScaler()
#inputs = scaler.fit_transform(inputs)
#targets = scaler.fit_transform(targets)


print(inputs.shape)
print(targets.shape)
#print(inputs)
print(inputs[0:1])
print(targets[0:1])
print(inputs)
print(targets)
model_output = model.predict(inputs[0:1])
print(model_output)

#On entraine le model
from types import new_class
#Train the model
for _ in range(10):
  model.fit(inputs, targets, epochs=20, validation_split=0.2)
history = model.fit(inputs, targets, epochs=20, validation_split=0.2)

#On affiche le progret
loss_curve = history.history["loss"]
acc_curve = history.history["accuracy"]

loss_val_curve = history.history["val_loss"]
acc_val_curve = history.history["val_accuracy"]
print(loss_curve)
plt.plot(loss_curve, label="Train")
plt.plot(loss_val_curve, label="Val")
plt.legend(loc='upper left')
plt.title("Loss")
plt.show()

plt.plot(acc_curve, label="Train")
plt.plot(acc_val_curve, label="Val")
plt.legend(loc='upper left')
plt.title("Accuracy")
plt.show()

exemple = random.randint(0,Taille_dataset)
print(inputs)
model_output = model.predict(inputs[exemple:exemple+1])
print(inputs[0:1])
print(model_output)
print()
print(targets[0:1])
#print(model_output, targets[0:2])
print()



#On affiche la fonction et la prediction
fonction = []
fonction .extend(inputs[exemple][7:])
fonction .extend(targets[exemple])
prediction = []
prediction .extend(inputs[exemple][7:])
prediction .extend(model_output[0])



plt.plot(fonction, label="Fonction")
plt.plot(prediction, label="Prediction")
plt.legend(loc='upper left')
plt.title("Resultat")
plt.show()


Optimise au maximum ce code