//+------------------------------------------------------------------+
//| Colibri.mq4
//| Copyright © 2010, Laurent Béville (LoBev)
//| http://fr-fr.facebook.com/pages/La-Martinique-Fleur-des-Caraibes/13968306412
//+------------------------------------------------------------------+
#property copyright "Copyright © 2010, Laurent Béville"
#property link      "http://fr-fr.facebook.com/pages/La-Martinique-Fleur-des-Caraibes/13968306412"
#define           n.Tentatives.Connection          5           // try to connect to server 5 times
#define           n.Tentatives.Clôture             5           // try to close orders 5 times
#define           Temps.De.Référence               3           // wait 3 Seconds before opening  or closing
#define           Durée.Max.Tentative              10          // ea try to send orders during 10 seconds
#define           Add.Slippage                     1           // Slippage
#define           Expiration.Trade.en.heures       48          // Pending orders erased after 48 hours
#define           Dist.Min.Entr.Niv                5           // Minimum distance between levels 
#define           Perte.Max.Par.Jour               0.06        // System is allowed to risk a maximum of 6% of equity every day
#define           Exposition.Maximale              0.1         // You are allowed to risk 10% of equity when taking into account all openend positions
               // ############# GESTION - URGENCE ############# //
extern string     ________1________                = "********* GEST. - URGENCES *********";
extern bool       FERMER.TOUTES.LES.POSES          = false;    // Close all opened orders
extern bool       FERMER.POSES.ACHETEUSES          = false;    // Close all buy orders
extern bool       FERMER.POSES.VENDEUSES           = false;    // Close All sell orders
extern string     Cible                            = "--- Fermeture Ciblée ---";
extern string     MAGIC.NUM.POUR.FERM.             = "";       // see below
extern bool       FERMER.POSE                      = false;    // Close a specific order by entering its magic number
extern bool       RUN.ON.INIT                      = false;    // Start ea when placed on chart
               // ############# TRADES-PARAMETRES ############# //
extern string     ________2________                = "*********** PARAM.-ORDRES **********";
// Choix du MODE ENTRY :
extern bool       Afficher.Montant.Perte           = false;    // Show the amount of losses
extern string     INFO.ORDRE                       = "0:Rien 1:Comptant 2:Stop 3:Limite 4:Stop Suiv.";
extern int        Type.Ordre                       = 0;        // Order type you want to send (4 types)
extern string     INFO.ORDRE.2                     = "0:Pas dépendant ; Si ordre mère exécuté -> 1:Exécuter  2:Effacer 3:Clôturer";
extern int        MagicNum.Ordre.Mère              = 0;        // Magic number of mother order
extern int        Dépendance                       = 0;        // Action to do with others orders (Execute/Erase/Close) when mother order is executed
extern string     Mini.Sep                         = "----------";
extern int        Trailing.Stop                    = 0;        // 
extern int        Trail.Entry.Pips                 = 0;        // Level in points at which ea start trailing stops
extern double     Exposition.Au.Risque             = 0.02;     // Risk exposure in %
extern int        Dist.Stop.Min.En.pips            = 50;       // Minimum distance between stop and entry point
extern int        Ecart.Entre.Niveaux              = 2;        // Distance defined between levels
extern string     ________3________                = "-----------------------------------------------------";
extern bool       ACHETER                          = false;    // BUY
extern bool       VENDRE                           = false;    // or SELL
               // ############# GRILLE - OPTIONS ############# //
extern string     ________4________                = "******* CHOIX OPTIONS GRILLE *******";               
extern bool       UTILISATION_GRILLE               = false;    // Security Use Grid : must be set to TRUE
extern string     ________5________                = "-----------------------------------------------------";
extern double     Niveau.De.Protection             = 0;        // Stopping price level 
extern double     Prix.Achat                       = 0;        // Buying price
extern double     Prix.Vente                       = 0;        // Selling Price
extern string     ________6________                = "-----------------------------------------------------";
// Choose calculation type :
extern bool       Grille.Centrée.?                 = false;    // Level calculated from Grid Center level or by its boundering levels
// if you choose boundering levels calculation mode :
extern double     Grille.Borne.Sup                 = 0;        // Highest level price
extern double     Grille.Borne.Inf                 = 0;        // Lowest level price
// if you choose grid center calculation mode :
extern double     Grille.LC.Niv                    = 0;        // Grid center price
extern double     Grille.LC.Pas                    = 0;        // Levels step in points   
// Maximum levels number :
extern double     Grille.Nbre.Niveaux              = 0;        // Levels number
               // ############# EFFACER - TRACES - ORDRE ############# //
extern string     ________7________                = "****** PROCEDURES NETTOYAGE ******";
extern bool       Clean.Trace.Prise.Profit         = true;     // Clean profits arrows
extern bool       Clean.Trace.Stop.Perte           = true;     // Clean stops arrows             
extern bool       Clean.Trace.Entree.Sortie        = false;    // Clean entry arrows
// ****************************************************************** //
// ******************* CODES MAGIC NUMBER - ORDRES ****************** //
// ****************************************************************** //
// Sens        = Achat : 1 ; Vente : 2
// Type        = Au Marché : 0 ; Comptant : 1 ; Stop : 2 ; Limite : 3
//               Stop Suiveur : 4
// Rang        = Rang de l'ordre (< 10)
// Dépendance  = 0 : Pas de dépendance ; 
//               1 : Effacer si ordre mère exécuté
//               2 : Ouvrir si ordre mère exécuté
//               3 : Clôturer si ordre mère exécuté
// 
// Calcul du Magic-Number =
// Sens + Type + Rang + Dépendance
//
// Dans OrderComment() : mettre le magic number de l'ordre mère ou rien
// ***************************************************************** //
double Taille.Pos.Global = 0, Taille.Pos.par.Niv = 0;
// Variables globales
string      Content[10];
string      str.Comment =  "";
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
   {
   //----
      // Initialisation importante : en cas de changement de compte
      GlobalVariableSet("Montant.Journalier.En.Compte", AccountBalance());
      
      // Création des variables globales en cas de non-existence
      if (!GlobalVariableCheck("$_ACTIVATION_EXPERT_$"))   
         GlobalVariableSet("$_ACTIVATION_EXPERT_$", 0);
      
      // L'expert commence à tourner à l'initialisation
      if (RUN.ON.INIT) start();
   //----
      return(0);
   }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
   {
   //----
      // :::::::::: Sous Module Stop Suiveurs :::::::::: //
      Stop_Suiveurs();
       
      // :::::::::: Fermeture de positions dans l'urgence :::::::::: // 
      Fermetures();     
       // ******************************************************************* //
      // !!! SECURITE !!!
      if (Perte.Max.Atteinte.?()) return(0);
      // ******************************************************************* //
      // :::::::::: Utilisation Grille :::::::::: //
      if (UTILISATION_GRILLE && Params_Are_Ok_?(Grille.Nbre.Niveaux))
         Prog_Grille_Is_Ok_?(Type.Ordre, Niveau.De.Protection, Grille.Centrée.?, Grille.Nbre.Niveaux, 
                     Grille.Borne.Sup, Grille.Borne.Inf, Grille.LC.Niv, Grille.LC.Pas);
   //----
      return(0);
   }
//+------------------------------------------------------------------+
// ****************************************************************** //
// ************************ Programme Grille ************************ //
// ****************************************************************** //
bool Prog_Grille_Is_Ok_?(int _Type.Ord, double _Stop.Protect.en.$, bool _Grille.Centrée.?, 
               int _Nbre.Niv, double _Grille.Borne.Sup, double _Grille.Borne.Inf, 
               double _Grille.LC.Niv, double _Grille.LC.Pas)
   {
      
      double NivPas = 0, Premier.Niv = 0, Adjust.Lot = 0, mult = 0; 
      int sens = 0;
      // :::::::::: Ajustement du Nombre de niveaux :::::::::: //
      if ((!_Grille.Centrée.? && _Grille.Borne.Sup != 0 && _Grille.Borne.Inf != 0) ||
            (_Grille.Centrée.? && _Grille.LC.Niv != 0 && _Grille.LC.Pas != 0))
         
         Ajust_Nbre_Niveaux_Max(_Type.Ord, _Stop.Protect.en.$, _Grille.Centrée.?, _Nbre.Niv,
                        _Grille.Borne.Sup, _Grille.Borne.Inf, _Grille.LC.Niv, _Grille.LC.Pas);         
      // :::::::::: Formattage des tailles de positions :::::::::: //
      // En principe les tailles de position sont calculées dans Ajust_Nbre_Niveaux_Max(...)
      Formattage_Tailles_Pos();
    
      // :::::::::: Calcul Pas de Progression entre Niveaux :::::::::: //
      NivPas = Calc_Pas_Progression(_Grille.Centrée.?, _Nbre.Niv, _Grille.Borne.Sup, _Grille.Borne.Inf, 
                     _Grille.LC.Niv, _Grille.LC.Pas);   
      // :::::::::: Calcul du Premier Niveau par défaut :::::::::: //                     
      Premier.Niv = Init_Premier_Niveau(_Grille.Centrée.?, _Nbre.Niv, _Grille.Borne.Sup, _Grille.Borne.Inf, 
                     _Grille.LC.Niv, _Grille.LC.Pas);                  
     
      // :::::::::: Calcul du nombre de mini-lots compl. :::::::::: //
      double Nbre.MiniLots.Compl = MathRound((Taille.Pos.Global - (Taille.Pos.par.Niv * _Nbre.Niv)) /
                           MarketInfo(Symbol(), MODE_LOTSTEP));
     
      // :::::::::: Remplissage de la var. Adjust.Lot :::::::::: //         
      Adjust.Lot = Taille.Pos.par.Niv;
      
      // :::::::::: Calcul du Magic Number :::::::::: //
      if (ACHETER) sens = 1; 
      else if (VENDRE) sens = 2;
      
      int Magic.Num = Calc_Magic_Num(sens, Type.Ordre, Dépendance);
      // --- Filtre sur la longueur maximale du magic number ---
      // Autrement dit : pas plus de 9 ordres par préfixe "sens + type"
      if (StringLen(DoubleToStr(Magic.Num, 0)) > 4) return;
            
      // :::::::::: Passer les ordres grille :::::::::: // 
      int Solde.Niv = _Nbre.Niv;
      double Objectif.Niv = Premier.Niv; // Initialisation au premier niveau
      
      if (Condit_Pass_Ord_are_Ok_?() && !FERMER.TOUTES.LES.POSES && !FERMER.POSE)
         {
            // Passer en boucle n ordres tel que n = _Nbre.Niv         
            for (int i = 1; i <= _Nbre.Niv; i++)
               {
                  // Réinitialisation de Adjust.Lot
                  Adjust.Lot = Taille.Pos.par.Niv;
                  
                  // Conditions pour augmenter la var. Adjust.Lot   
                  if (Test_Sur_Nbre_Aléat(Nbre.MiniLots.Compl, Solde.Niv)
                           && Nbre.MiniLots.Compl > 0 && Nbre.MiniLots.Compl >= Solde.Niv) 
                     {
                        Adjust.Lot += MarketInfo(Symbol(), MODE_LOTSTEP);
                        Nbre.MiniLots.Compl--; 
                     }
                  
                  // Décrémentation pour calcul du nombre de TP restant à ajuster
                  Solde.Niv--;
                  
                  // Avant de passer un ordre s'assurer que c'est possible
                  Attendre_Disponibilité_pr_Ordre();
                  
                  // Etablir le commentaire de l'ordre
                  str.Comment = Order_Comment();
                 
                  // Ne passer des ordres que si l'expert a été activé manuellement
                  // sinon retour
                  if (GlobalVariableGet("$_ACTIVATION_EXPERT_$") == 1)
                     {   
                        if (mult >= (_Nbre.Niv - 1) &&
                              Succès_Ordre_?(mult, Symbol(), Type.Ordre, Adjust.Lot, 
                                    MarketInfo(Symbol(), MODE_SPREAD) + Add.Slippage, 
                                    Niveau.De.Protection, Objectif.Niv, str.Comment, Magic.Num, 
                                    (3600 * Expiration.Trade.en.heures + TimeLocal())))
                                    
                        GlobalVariableSet("$_ACTIVATION_EXPERT_$", 0);
                     }
                  else return(0);
                  
                  // Incrémentation pour prochain niveau
                  mult++;
                  
                  // Recherche du prochain niveau
                  if (ACHETER) Objectif.Niv += NivPas;
                  if (VENDRE) Objectif.Niv -= NivPas; 
               }
         }
   }
// ****************************************************************** //
// ************************ Commentaire Ordre *********************** //
// ****************************************************************** //
string Order_Comment()
   {
      string Str.Com = StringConcatenate(MagicNum.Ordre.Mère, "#", Trailing.Stop, "#", Trail.Entry.Pips);
      
      // Renvoi de la chaîne
      return(Str.Com);
   }
// ****************************************************************** //
// ****** Attendre la disponibilité du serveur pour transaction ***** //
// ****************************************************************** //
void Attendre_Disponibilité_pr_Ordre()
   {
      int timer1 = TimeLocal(), timer2 = TimeLocal();   
      
      // ----- TEST de disponibilité ----- //
      while (!IsConnected() || IsTradeContextBusy() || !IsTradeAllowed())
         {
            Sleep(10); // Attendre 10 ms
            timer2 = TimeLocal();
            if (timer2 - timer1 > 2000) break; // Sortie après + de 2 secondes
         }
   }
// ****************************************************************** //
// ************************* Passer un ordre ************************ //
// ****************************************************************** //
bool Succès_Ordre_?(int _mult, string _Symbol, int _Type.Ordre, double _Volume, int _Slippage, 
               double _Stoploss, double _Takeprofit, string _Comment = "", int _Magic.Num = 0, 
               datetime _Expiration = 0)
   {
      int err = GetLastError();
      err = 0;
           
      // --- Vérification de la validité du stop --- //
      if (MathAbs(_Stoploss - (Ask + Bid) / 2) < MarketInfo(Symbol(), MODE_STOPLEVEL) * Point)
         {
            GlobalVariableSet("$_ACTIVATION_EXPERT_$", 0);
            Alert("La distance Stop - Entrée est insuffisante");
            return(false);
         }
      // --- Rafraîchir les cotations ---
      RefreshRates();
      
      // :::::::::: ACHAT AU MARCHE :::::::::: //
      if (_Type.Ordre == 1 && ACHETER)
         Passer_Un_Ordre(Symbol(), OP_BUY, _Volume, Ask, _Slippage, _Stoploss, _Takeprofit,  
                     _Comment, _Magic.Num, _Expiration, Green);
            
      // :::::::::: VENTE AU MARCHE :::::::::: //
      if (_Type.Ordre == 1 && VENDRE)
         Passer_Un_Ordre(Symbol(), OP_SELLSTOP, _Volume, Bid, _Slippage, _Stoploss, _Takeprofit, 
                     _Comment, _Magic.Num, _Expiration, Red);
      // :::::::::: ACHAT STOP :::::::::: //
      if ((_Type.Ordre == 2 || _Type.Ordre == 4) && ACHETER)
         Passer_Un_Ordre(Symbol(), OP_BUYSTOP, _Volume, Prix.Achat + _mult * Ecart.Entre.Niveaux * Point, 
                  _Slippage, _Stoploss, _Takeprofit, _Comment, _Magic.Num, _Expiration, Teal);
      
      // :::::::::: VENTE STOP :::::::::: //
      if ((_Type.Ordre == 2 || _Type.Ordre == 4) && VENDRE)
         Passer_Un_Ordre(Symbol(), OP_SELLSTOP, _Volume, Prix.Vente - _mult * Ecart.Entre.Niveaux * Point,
                  _Slippage, _Stoploss, _Takeprofit, _Comment, _Magic.Num, _Expiration, Orchid);
      // :::::::::: ACHAT LIMITE :::::::::: //
      if (_Type.Ordre == 3 && ACHETER)
         Passer_Un_Ordre(Symbol(), OP_BUYLIMIT, _Volume, Prix.Achat + _mult * Ecart.Entre.Niveaux * Point, 
                  _Slippage, _Stoploss, _Takeprofit, _Comment, _Magic.Num, _Expiration, Teal);
      // :::::::::: VENTE LIMITE :::::::::: //               
      if (_Type.Ordre == 3 && VENDRE)
         Passer_Un_Ordre(Symbol(), OP_SELLLIMIT, _Volume, Prix.Vente - _mult * Ecart.Entre.Niveaux * Point, 
                  _Slippage, _Stoploss, _Takeprofit, _Comment, _Magic.Num, _Expiration, Orchid);
      
      err = GetLastError();
      
      Print("err : ", GetLastError());
      
      // Renvoi de true par défaut
      return(true);
   }
// ****************************************************************** //
// ************************* Passer un ordre ************************ //
// ****************************************************************** //
bool Passer_Un_Ordre(string _Symbol, int _Cmd, double _Volume, double _Price, int _Slippage, 
               double _Stoploss, double _Takeprofit, string _Comment = "", int _Magic.Num = 0, 
               datetime _Expiration = 0, color _Arrow.Color = CLR_NONE)
   {
   
      int ticket = -1;
      
      // La plateforme est-elle connectée ?
      if (!IsConnected()) 
         {
            Print("La plateforme n\'est pas connectée. IsConnected() == false");
            return(-1);
         }
      
      // L'expert a t-il été désactivé ?
      if (IsStopped()) 
         {
            Print("L\'expert a été désactivé. IsStopped() == false");
            return(-1);
         }
      
      int cnt = 0; 
      // Attente jusqu'à ce qu'il soit permis de passer un trade
      // dans la limite d'un certain délai   
      while (!IsTradeAllowed() && cnt < n.Tentatives.Connection)
         {
            Temps_Attente_Aleatoire();
            cnt ++;
         }
      
      // Une fois le délai maximum passé, trader est-il permis ?
      // Si oui continuer, sinon sortie de la sous-procédure
      if(!IsTradeAllowed()) return(-1);
      
      // Calibrage des objectifs : Stop, Prix d'entrée, Prise de profit
      // selon le format requis par la devise en cours
      _Price       = NormalizeDouble(_Price, Digits);
      _Stoploss    = NormalizeDouble(_Stoploss, Digits);
      _Takeprofit  = NormalizeDouble(_Takeprofit, Digits);
      
      // Passer les ordres
      ticket =OrderSend(_Symbol, _Cmd, _Volume, _Price, _Slippage, _Stoploss, 
                     _Takeprofit,_Comment,_Magic.Num, _Expiration,_Arrow.Color);
      
   }
// ****************************************************************** //
// ********************* Temps Attente Aleatoire ******************** //
// ****************************************************************** //
void Temps_Attente_Aleatoire()
   {
      // Conversion du temps de référence en dixième de secondes
      double Dixièmes.De.Secondes = MathCeil(Temps.De.Référence / 0.1);
      if (Dixièmes.De.Secondes <= 0) return;
      
      // Définition en dixième de secondes la durée maximum pendant laquelle
      // l'ordre tentera de passer
      int Durée.Tentative.dsec = MathRound(Durée.Max.Tentative / 0.1);
      
      double p = 1.0 - 1.0 / Dixièmes.De.Secondes;
      
      // Repos pendant 1 dixième de seconde
      Sleep(100);
      
      for (int i = 0; i < Durée.Tentative.dsec; i++)
         {
            if (MathRand() >= 32768 * p) 
               {
                  break;
               }
            
            // Repos pendant 1 dixième de seconde
            Sleep(100);
         }
   }
// ****************************************************************** //
// ************** Conditions Passage Ordres Remplies ? ************** //
// ****************************************************************** //
bool Condit_Pass_Ord_are_Ok_?()
   {
      double Risque.Résid. = 0, Margin.Lots.Risk = 0;
      
      // Calcul du risque résiduel pouvant être pris sous forme de lots
      if (OrdersTotal() > 0)      
         {      
            // :::::::::::::::: EXPOSITION ::::::::::::::: //
            for (int i = OrdersTotal(); i >= 0; i--)
               {
                  OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
            
                  // Ne considérer que les ordres déjà en cours
                  if (OrderType() <= OP_SELL) 
                     {
                        Margin.Lots.Risk += MathAbs((OrderOpenPrice() - OrderStopLoss()) / Point) 
                                    * OrderLots() * MarketInfo(Symbol(), MODE_TICKVALUE);
                     }
               }
            // ---
         
            Risque.Résid. = Exposition.Maximale - (Margin.Lots.Risk / AccountBalance());
         }
      // :::::::::::::::: TEST CONDITION ::::::::::::::: //      
      return(Risque.Résid. > Exposition.Au.Risque);    
   }
// ****************************************************************** //
// ********************* Test sur Nombre Aléatoire ****************** //
// ****************************************************************** //
bool Test_Sur_Nbre_Aléat(int _Nbre.MiniLots.Compl, int _Nbre.Niv)
   {
      // Alerte sur valeurs incohérentes
      if (_Nbre.Niv < _Nbre.MiniLots.Compl)
         Alert("Erreur : _Nbre.Niv < _Nbre.MiniLots.Compl. Le test aléatoire ne peut avoir lieu");
      
      // Initialisation du générateur de nombre aléatoire
      MathSrand(TimeLocal());
      
      int Nombre.Aléatoire = MathRand();
      double Seuil = 32767 * (_Nbre.MiniLots.Compl / _Nbre.Niv);
      // Renvoi de la valeur du test proprement dit
      return(Nombre.Aléatoire < Seuil);      
   }
// ****************************************************************** //
// ****************** Initialisation Premier Niveau ***************** //
// ****************************************************************** //
double Init_Premier_Niveau(bool _Grille.Centrée.?, int _Nbre.Niv, double _Grille.Borne.Sup, 
                        double _Grille.Borne.Inf, double _Grille.LC.Niv, double _Grille.LC.Pas)
   {
      // --- CAS : GRILLE NON CENTREE ---
      
      // Achat :
      if (!_Grille.Centrée.? && ACHETER) return(_Grille.Borne.Inf);
      // Vente :
      if (!_Grille.Centrée.? && VENDRE) return(_Grille.Borne.Sup);
      
      // --- CAS : GRILLE CENTREE ---   
      
      // Achat :
      if (_Grille.Centrée.? && ACHETER)
         return(_Grille.LC.Niv - ((_Nbre.Niv - 1) / 2) * _Grille.LC.Niv * Point);
      
      // Vente :
      if (_Grille.Centrée.? && VENDRE)  
         return(_Grille.LC.Niv + ((_Nbre.Niv - 1) / 2) * _Grille.LC.Niv * Point);  
         
         
      // Renvoi de - 10000 par défaut
      return(-10000);                        
   }
// ****************************************************************** //
// ***************** Pas de Progression des Niveaux ***************** //
// ****************************************************************** //
double Calc_Pas_Progression(bool _Grille.Centrée.?, int _Nbre.Niv, double _Grille.Borne.Sup, 
                        double _Grille.Borne.Inf, double _Grille.LC.Niv, double _Grille.LC.Pas)
   {
      double Pas = 0;
      
      // Grille Canal : définition du pas de progression
      if (!_Grille.Centrée.?)
         {
            Pas = (MathAbs(_Grille.Borne.Sup - _Grille.Borne.Inf) 
                                 + MarketInfo(Symbol(), MODE_TICKSIZE)) / _Nbre.Niv;         
         }
      
      // Grille Centrée : définition du pas de progression
      if (_Grille.Centrée.?)
         {
            Pas = _Grille.LC.Pas * Point;         
         }
      // Formatter la var. Pas
      return(NormalizeDouble(Pas, Digits));
   }
// ****************************************************************** //
// ******************* Formattage Tailles Position******************* //
// ****************************************************************** //
bool Formattage_Tailles_Pos()
   {
      if (MarketInfo(Symbol(), MODE_LOTSTEP) == 0.1)
         {
            if (Taille.Pos.par.Niv >= NormalizeDouble(Taille.Pos.par.Niv, 1))
               Taille.Pos.par.Niv = NormalizeDouble(Taille.Pos.par.Niv, 1);
                       
            if (Taille.Pos.par.Niv < NormalizeDouble(Taille.Pos.par.Niv, 1))
               Taille.Pos.par.Niv = NormalizeDouble(Taille.Pos.par.Niv, 1) 
                              - MarketInfo(Symbol(), MODE_LOTSTEP);
            
            // Renvoi
            return(true);
         }
      if (MarketInfo(Symbol(), MODE_LOTSTEP) == 0.01)
         {
            if (Taille.Pos.par.Niv >= NormalizeDouble(Taille.Pos.par.Niv, 2))
               Taille.Pos.par.Niv = NormalizeDouble(Taille.Pos.par.Niv, 2);
                       
            if (Taille.Pos.par.Niv < NormalizeDouble(Taille.Pos.par.Niv, 2))
               Taille.Pos.par.Niv = NormalizeDouble(Taille.Pos.par.Niv, 2) 
                              - MarketInfo(Symbol(), MODE_LOTSTEP);
            // Renvoi
            return(true);                              
         }
         
      // Renvoi
      return(false);                     
   }
// ****************************************************************** //
// ***************** Ajustement du nombre de niveaux **************** //
// ****************************************************************** //
double Ajust_Nbre_Niveaux_Max(int _Type.Ord, double _Stop.Protect.en.$, bool _Grille.Centrée.?, 
               int _Nbre.Niv, double _Grille.Borne.Sup, double _Grille.Borne.Inf, 
               double _Grille.LC.Niv, double _Grille.LC.Pas)
   {
      
      double Max.Niv.Théoriq = 0;
         
      // ----------------------------------------------------------- //
      // ------------------------ SECURITE 1 ----------------------- //
      // ----------------------------------------------------------- //  
      // Ajustement du nombre de niveaux en fonction du point 
      // d'entrée et des objectifs de profit
      // OBJECTIF CANAL
      if (!_Grille.Centrée.?)
         {
            // Calcul du nombre max. de niveaux possibles entre les bornes supérieures 
            // et inférieures, compte tenu de la distance min. autorisée entre les niveaux
            Max.Niv.Théoriq = MathAbs(MathFloor((1/Point) * (_Grille.Borne.Sup - _Grille.Borne.Inf) 
                        / Dist.Min.Entr.Niv));
            
            // Test de validité : Ajustement éventuel
            if (_Nbre.Niv > Max.Niv.Théoriq) _Nbre.Niv = Max.Niv.Théoriq;
         }
      
      // OBJECTIF LIGNE CENTRALE + ORDRE AU MARCHE
      if (_Grille.Centrée.? && _Type.Ord == 1)
         {
            // Calcul du nombre max. de niveaux autorisés, compte tenu de la distance entre
            // La ligne centrale de prise de profit et le cours d'entrée
            Max.Niv.Théoriq = 2 * MathAbs(MathFloor((1/Point) * (_Grille.LC.Niv - ((Ask+Bid)/2))
                        / Dist.Min.Entr.Niv));
                        
            // Test de validité : Ajustement éventuel
            if (_Nbre.Niv > Max.Niv.Théoriq) _Nbre.Niv = Max.Niv.Théoriq;                        
         }
      // OBJECTIF LIGNE CENTRALE + (ORDRE STOP ACHAT OU ORDRE LIMITE ACHAT)
      if (  _Grille.Centrée.? && 
           (_Type.Ord == 2 || _Type.Ord == 3 || (_Type.Ord == 4 && ACHETER && !VENDRE)) && 
            Prix.Achat != 0 && Prix.Vente == 0)
         {
            // Calcul du nombre max. de niveaux autorisés, compte tenu de la distance entre
            // La ligne centrale de prise de profit et le prix d'achat envisagé
            Max.Niv.Théoriq = 2 * MathAbs(MathFloor((1/Point) * (_Grille.LC.Niv - Prix.Achat)
                        / Dist.Min.Entr.Niv));     
                        
            // Test de validité : Ajustement éventuel
            if (_Nbre.Niv > Max.Niv.Théoriq) _Nbre.Niv = Max.Niv.Théoriq;                            
         }
      // OBJECTIF LIGNE CENTRALE + (ORDRE STOP VENTE OU ORDRE LIMITE VENTE)
      if (  _Grille.Centrée.? && 
           (_Type.Ord == 2 || _Type.Ord == 3 || (_Type.Ord == 4 && !ACHETER && VENDRE)) && 
           Prix.Achat == 0 && Prix.Vente != 0)
         {
            // Calcul du nombre max. de niveaux autorisés, compte tenu de la distance entre
            // La ligne centrale de prise de profit et le prix de vente envisagé
            Max.Niv.Théoriq = 2 * MathAbs(MathFloor((1/Point) * (_Grille.LC.Niv - Prix.Vente)
                        / Dist.Min.Entr.Niv));           
                        
            // Test de validité : Ajustement éventuel
            if (_Nbre.Niv > Max.Niv.Théoriq) _Nbre.Niv = Max.Niv.Théoriq;
         }      
      // ----------------------------------------------------------- // 
      // ----------------------------------------------------------- //
      // ------------------------ SECURITE 2 ----------------------- //
      // ----------------------------------------------------------- //  
      // Si la taille de position envisagée est trop petite pour être 
      // répartie entre x lignes de niveaux => Réajuster le nombre de
      // lignes de sortie envisagées. 
      
      // On calcule ici la taille de position globale
      Taille.Pos.par.Niv = Calc.Taille.Pos.par.Niv(_Type.Ord, _Nbre.Niv, _Stop.Protect.en.$);
      Taille.Pos.Global = _Nbre.Niv * Taille.Pos.par.Niv;
      // Test : condition de validité portant sur _Nbre.Niv
      if (Taille.Pos.Global < (_Nbre.Niv * MarketInfo(Symbol(), MODE_LOTSTEP)))
         {   
            // Ajustement _Nbre.Niv
            _Nbre.Niv = MathFloor(Taille.Pos.Global / MarketInfo(Symbol(), MODE_LOTSTEP));
            
            // Recalcul des tailles de position global/par Niveau     
            Taille.Pos.par.Niv = MarketInfo(Symbol(), MODE_LOTSTEP);
            Taille.Pos.Global = _Nbre.Niv * Taille.Pos.par.Niv;                
         }
      // ----------------------------------------------------------- //
            
   }
// ****************************************************************** //
// **** Calcul de la taille de position par niveau de la grille ***** //
// ****************************************************************** //
double Calc.Taille.Pos.par.Niv(int _Type.Ord, int _Nbre.Niv, double _Stop.Protect.en.$)
   {
      double dist.Stop.Protect.en.Pips = 0;
      
      // Alerte + Retour si le nombre de niveau = 0
      if (_Nbre.Niv == 0)
         {
            Alert("Erreur : Spécifiez un nombre de niveaux > 0 !!!");
            return(0);
         }
         
      // Calcul de la distance du stop au point d'entrée théorique   
      if (_Type.Ord == 1)
         dist.Stop.Protect.en.Pips = MathAbs((_Stop.Protect.en.$ - (Ask+Bid)/2)) * (1/Point);
      
      else if((_Type.Ord == 2 || _Type.Ord == 3 || _Type.Ord == 4) && ACHETER)
         dist.Stop.Protect.en.Pips = MathAbs((_Stop.Protect.en.$ - (Prix.Achat + 0.5 * 
                              Ecart.Entre.Niveaux * Point * (_Nbre.Niv - 1)))) 
                                          * (1/Point);
      
      else if((_Type.Ord == 2 || _Type.Ord == 3 || _Type.Ord == 4) && VENDRE)
         dist.Stop.Protect.en.Pips = MathAbs((_Stop.Protect.en.$ - (Prix.Vente - 0.5 * 
                              Ecart.Entre.Niveaux * Point * (_Nbre.Niv - 1)))) 
                                          * (1/Point);         
   
      // Calcul du montant risqué en $ compte tenu du stop pour 1 lot standard
      double Lot.Margin.en.$ = dist.Stop.Protect.en.Pips * MarketInfo(Symbol(), MODE_TICKVALUE);
      
      // Calcul de la TP pour un niveau
      return((AccountBalance() * Exposition.Au.Risque) / (Lot.Margin.en.$ * _Nbre.Niv));
   }
// ****************************************************************** //
// **************** Calcul du Magic Number approprié **************** //
// ****************************************************************** //
int Calc_Magic_Num(int _sens, int _type, int _dépendance)
   {
      // Initialisation
      int Somme = 0;
      int rang = Calc_Rang_Ordre(_sens, _type);
      
      // Calcul Magic Number      
      Somme = StrToInteger(StringConcatenate(_sens, _type, rang, _dépendance));
      
      // Renvoi de la valeur calculée
      return(Somme);
   }
// ****************************************************************** //
// ******************* Calcul du rang de l'ordre ******************** //
// ****************************************************************** //
int Calc_Rang_Ordre(int _sens, int _type)
   {
      int total         = OrdersTotal();
      int rang, num     = 0;
            
      // Définition du préfixe : sens + type
      string Magic.Prefix = StringConcatenate(_sens, _type);
      
      // ...
      string MA.to.Str = "";
      
      // Parcourir l'ensemble des ordres en cours
      for(int i = total; i > 0; i--)
         {
            OrderSelect(i, SELECT_BY_POS, MODE_TRADES);   
            
            // Conversion du magic number sélectionné en chaîne de caractère
            MA.to.Str = DoubleToStr(OrderMagicNumber(), 0);
            
            // Le préfixe en question est reconnu parmi les magic numbers en cours
            if (StringSubstr(MA.to.Str, 0, 2) == Magic.Prefix)   
               {
                  // Place le rang de l'ordre analysé dans une var. temp.
                  num = StrToInteger(StringSubstr(MA.to.Str, 2, 1));
                  
                  // Rafraîchissement de la variable rang ssi nécessaire
                  if (num > rang)  rang = num; 
               }
         }
      
      // Incrémentation du rang
      rang++;
      
      // Renvoi de la valeur rang incrémenté de +1
      return(rang);
   }
// ****************************************************************** //
// ********************** Contrôle Paramétrage ********************** //
// ****************************************************************** //
bool Params_Are_Ok_?(int _Nbre.Niv)
   {
      // :::::::::::::::: SECURITE ::::::::::::::: //
      // Alerte + Retour si le nombre de niveau = 0
      if (_Nbre.Niv == 0 && (ACHETER || VENDRE))
         {
            Alert("Erreur : Spécifiez un nombre de niveaux > 0 !!!");
            return(false);
         }  
      // :::::::::::::::: SECURITE ::::::::::::::: //   
 
      // :::::::::::::::: RETOUR PAR DEFAUT ::::::::::::::: //  
      return (true);
   }
// ****************************************************************** //
// ****************** Perte Max. Autorisée par Jour ***************** //
// ****************************************************************** //
bool Perte.Max.Atteinte.?()
   {
      int total = OrdersHistoryTotal();
      double Pertes.Journalières.Accumulées = 0, Part.Representative.Pertes = 0;
   
      // Calculer le montant du compte en dollars chaque jour
      // Réactualisation entre 0 et 4h du matin
      if (Hour() < 3 && Hour() >= 0) 
         GlobalVariableSet("Montant.Journalier.En.Compte", AccountBalance());
      
      if (!GlobalVariableCheck("Montant.Journalier.En.Compte") 
            || GlobalVariableGet("Montant.Journalier.En.Compte") == 0)   
         GlobalVariableSet("Montant.Journalier.En.Compte", AccountBalance());
         
      // Initialisation de la variable calculant le % des pertes
      Pertes.Journalières.Accumulées = 0;
            
      for(int i = total; i > 0; i--)
         {
            OrderSelect(i,SELECT_BY_POS, MODE_HISTORY);
            if(OrderSymbol() == Symbol() &&  TimeDayOfYear(OrderOpenTime()) == DayOfYear()
                  && OrderProfit() < 0)
               {
                  Pertes.Journalières.Accumulées += OrderProfit();
                  Part.Representative.Pertes = Pertes.Journalières.Accumulées 
                                          / GlobalVariableGet("Montant.Journalier.En.Compte");
                  
                  if (Part.Representative.Pertes > Perte.Max.Par.Jour) return(true);
               }
         } 
         
         // Renvoi par défaut :
         return(false);
   }
// ****************************************************************** //
// *************************** Fermetures *************************** //
// ****************************************************************** //
int Fermetures()
   {
      // Fermer l'intégralité des positions
      if (FERMER.TOUTES.LES.POSES)     Fermer_Positions();
      
      // Ne fermer que les positions acheteuses
      if (FERMER.POSES.ACHETEUSES)  {     
         Fermer_Positions(0, OP_BUY);
         Fermer_Positions(0, OP_BUYSTOP);
         Fermer_Positions(0, OP_BUYLIMIT);}
      
      // Ne fermer que les positions vendeuses
      if (FERMER.POSES.VENDEUSES)   {      
         Fermer_Positions(0, OP_SELL);
         Fermer_Positions(0, OP_SELLSTOP);
         Fermer_Positions(0, OP_SELLLIMIT); } 
      
      // Ne fermer qu'une seule position bien ciblée
      if (FERMER.POSE)                 Fermer_Positions(StrToInteger(MAGIC.NUM.POUR.FERM.));     
      // :::::::::: Réinitialisation de la variable d'activation de l'ea :::::::::: // 
      if (FERMER.TOUTES.LES.POSES || FERMER.POSES.ACHETEUSES 
            || FERMER.POSES.VENDEUSES ||FERMER.POSE)
         GlobalVariableSet("$_ACTIVATION_EXPERT_$", 0);
   }
// ****************************************************************** //
// ******************* Fermer (Toutes) Position(s) ****************** //
// ****************************************************************** //
int Fermer_Positions(int _Identifiant = 0, int _Fermer.Ordre.Type = 6)
   {
      bool     Ordre.Clôturé.?                  = false;
      string   Nom.Arrow.Clean.CodeUn           = "";
      string   Nom.Arrow.Clean.CodeQuatre.Tp    = "";
      string   Nom.Arrow.Clean.CodeQuatre.Stp   = "";
      
      for (int i = OrdersTotal(); i >= 0; i--)
         {
            OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
            
            // Initialisation à false de la variable de fermeture après chaque fermeture
            Ordre.Clôturé.? = false;
            
            // *************************************************************** //
            // ---------------- CREATION NOM OBJET A EFFACER ----------------- //
            // *************************************************************** //                        
                           
            // Effacer les objets graphiques associés au trade
            Nom.Arrow.Clean.CodeUn = Creation_Nom_Objet_A_Effacer("Arrow", 1, "",
               OrderSymbol(), OrderTicket(), OrderType(), OrderLots(), OrderOpenPrice(),
               OrderTakeProfit(), OrderStopLoss()); 
                           
            Nom.Arrow.Clean.CodeQuatre.Tp = Creation_Nom_Objet_A_Effacer("Arrow", 4, "Tp", 
               OrderSymbol(), OrderTicket(), OrderType(), OrderLots(), OrderOpenPrice(),
               OrderTakeProfit(), OrderStopLoss()); 
            Nom.Arrow.Clean.CodeQuatre.Stp = Creation_Nom_Objet_A_Effacer("Arrow", 4, "Stp", 
               OrderSymbol(), OrderTicket(), OrderType(), OrderLots(), OrderOpenPrice(),
               OrderTakeProfit(), OrderStopLoss()); 
                                                      
            // *************************************************************** //   
            
            // :::::::::::::::: FERMER L'INTEGRALITE DES ORDRES ::::::::::::::: // 
            // :::::::::::::::: OU FERMER UN ORDRE SPECIFIQUE ::::::::::::::: //            
            if ((Symbol() == OrderSymbol() && _Identifiant == 0) ||
                (Symbol() == OrderSymbol() && _Identifiant != 0 && OrderMagicNumber() == _Identifiant))
               {
                  if (
          // ...        
   (OrderType() == OP_BUY        && (_Fermer.Ordre.Type == OP_BUY        || _Fermer.Ordre.Type == 6)) ||
   (OrderType() == OP_SELL       && (_Fermer.Ordre.Type == OP_SELL       || _Fermer.Ordre.Type == 6)) ||
   (OrderType() == OP_BUYSTOP    && (_Fermer.Ordre.Type == OP_BUYSTOP    || _Fermer.Ordre.Type == 6)) ||
   (OrderType() == OP_SELLSTOP   && (_Fermer.Ordre.Type == OP_SELLSTOP   || _Fermer.Ordre.Type == 6)) ||
   (OrderType() == OP_BUYLIMIT   && (_Fermer.Ordre.Type == OP_BUYLIMIT   || _Fermer.Ordre.Type == 6)) ||
   (OrderType() == OP_SELLLIMIT  && (_Fermer.Ordre.Type == OP_SELLLIMIT  || _Fermer.Ordre.Type == 6))
          // ...      
                     )
                     {
                        int incr. = 0;
                        while (!Ordre.Clôturé.? && incr. < n.Tentatives.Clôture)
                           {
                              incr.++;
                              
                              // Attendre en cas de problème : déconnexion ou occupation
                              int timer1 = TimeLocal();
                              int timer2 = TimeLocal();
                              
                              // Attente aléatoire si les conditions ne sont pas réunies
                              Attendre_Disponibilité_pr_Ordre();
                              
                              // Rafraîchir cotation
                              RefreshRates();
                              
                              // Tentative de clôture
                              if (OrderType() == OP_BUY && OrderType() == OP_SELL)
                                 Ordre.Clôturé.? = OrderClose(OrderTicket(),OrderLots(), Bid, MarketInfo(Symbol(), MODE_SPREAD)+10, Violet);
                              
                              if (OrderType() != OP_BUY && OrderType() != OP_SELL)
                                 Ordre.Clôturé.? = OrderDelete(OrderTicket(), Violet); 
                              
                              if (!Ordre.Clôturé.?) Sleep(150); // Attente si seulement échec
                              
                           }
                        
                        // ------ EFFACER ------
                        if (Clean.Trace.Entree.Sortie) 
                              Effacer.Objet(Nom.Arrow.Clean.CodeUn);
                        if (Clean.Trace.Prise.Profit) 
                              Effacer.Objet(Nom.Arrow.Clean.CodeQuatre.Tp);
                        if (Clean.Trace.Stop.Perte) 
                              Effacer.Objet(Nom.Arrow.Clean.CodeQuatre.Stp);      
                     }
               }
         }
   }
// ****************************************************************** //
// ******************* Dénomination Objet à Effacer ***************** //
// ****************************************************************** //
string Creation_Nom_Objet_A_Effacer(string _Type.Objet, int _Code.Objet, string _Code.Sous.Objet, 
               string _Devise.Ordre, int _Ticket.Ordre, int _Type.Ordre, double _Quantité.Lots, 
               double _Prix.Ouverture, double _Prix.Prise.Profit, double _Prix.Stop.Perte)
   {
      // ex : Arrow (code 1) : "#30900169 buy stop 0.03 EURUSDm at 1.2864"
      // ex : Arrow (code 4) : "#31235132 buy stop 0.03 EURUSDm at 1.2658 take profit at 1.325"
      // ex : Arrow (code 4) : "#31010932 buy stop 0.02 EURUSDm at 1.2874 stop loss at 1.2706"
      
      string Nom.Objet.A.Effacer = "";
      
      if (_Type.Objet == "Arrow")   
         {
            // ----- Ajout du ticket de l'ordre ----- 
            Nom.Objet.A.Effacer = StringConcatenate("#", DoubleToStr(_Ticket.Ordre, 0)); 
            
            // ----- Ajout du type de l'ordre ----- 
            
            string Ord.Typ[6] = {" buy ", " sell ", " buy stop ", " sell stop ", " buy limit ", " sell limit "};
            
            Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, Ord.Typ[_Type.Ordre]);
            
            // ----- Ajout du volume ----- 
            int decim. = Lots.Decimals();
            string str.Q = DoubleToStr(NormalizeDouble(_Quantité.Lots, decim.), decim.);
            
            Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, str.Q);
            
            // ----- Ajout de la devise d'intervention ----- 
            Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, " ", _Devise.Ordre, " ");
            
            // ----- Ajout du prix d'ouverture ----- 
            string str.Pr = DoubleToStr(_Prix.Ouverture, Digits);
            
            Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, "at ", str.Pr);
            
            // --- Pré-Calculs ---
            if (Digits > 5) Print("Trop de chiffres après la virgule. Devise impossible à traiter correctement",
                              "Sous-Module : Creation.Nom.Objet.A.Effacer(...)");
            int add.cf = 0;
                  
            if (Bid >= 0   && Ask < 10)   add.cf = 0; // ex. type : 1.xx.. 
            if (Bid >= 10  && Ask < 100)  add.cf = 1; // ex. type : 12.xx..
            if (Bid >= 100 && Ask < 1000) add.cf = 2; // ex. type : 125.xx.. 
            if (Bid >= 1000) Print("Impossible de continuer : valeur monnaie trop grande (>1000).,", 
                                 "Sous-Module : Creation.Nom.Objet.A.Effacer(...)");                    
            // ---
            
            // ****** CAS CODE 4 ****** 
            if (_Code.Objet == 4 && _Code.Sous.Objet == "Tp")
               {
                  Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, " take profit at "); 
                  
                  string str.Tp = "";
                  // Découpage du nombre str.Tp, dg.Pow chiffres après la virgule
                  str.Tp = DoubleToStr(_Prix.Prise.Profit, Digits);                        
                  
                  for (int i = 1; i <= 5; i++)      
                     if (MathMod(10^Digits * _Prix.Prise.Profit, 10^i) == 0 && Digits > (i-1)) 
                        str.Tp = StringSubstr(str.Tp, 0, add.cf + Digits - ((i-1) + (Digits==i)));
                  
                  Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, str.Tp);  
               }
            if (_Code.Objet == 4 && _Code.Sous.Objet == "Stp")
               {
                  Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, " stop loss at "); 
                  
                  string str.Stp = "";
                  
                  // Découpage du nombre str.Stp, dg.Pow chiffres après la virgule
                  str.Stp = DoubleToStr(_Prix.Stop.Perte, Digits);                        
                        
                  for (i = 1; i <= 5; i++)
                     if (MathMod(10^Digits * _Prix.Stop.Perte, 10^i) == 0 && Digits > (i-1)) 
                        str.Stp = StringSubstr(str.Stp, 0, add.cf + Digits - ((i-1) + (Digits==i)));
                  Nom.Objet.A.Effacer = StringConcatenate(Nom.Objet.A.Effacer, str.Stp);  
               }               
         }
      // Renvoi du nom de l'objet
      return(Nom.Objet.A.Effacer);         
   }
// ****************************************************************** //
// ************* Calcul du nombre de décimales pour la TP *********** //
// ****************************************************************** //
int Lots.Decimals()
   {
      int Pos.Point = StringFind(MarketInfo(Symbol(), MODE_LOTSTEP), ".");
      // ---   
      return (StringLen(MarketInfo(Symbol(), MODE_LOTSTEP)) - (1 + Pos.Point));
   }
// ****************************************************************** //
// ********************* Fonction Objet à Effacer ******************* //
// ****************************************************************** //
void Effacer.Objet(string _Nom.Objet.A.Effacer)
   {
      for(int i = 0; i < 4; i++)
      {
         if (i==0 && !ObjectDelete(_Nom.Objet.A.Effacer)) _Nom.Objet.A.Effacer = 
            StringSubstr(_Nom.Objet.A.Effacer, 0, StringLen(_Nom.Objet.A.Effacer) - 1);
         
         if (i==1 && !ObjectDelete(_Nom.Objet.A.Effacer)) _Nom.Objet.A.Effacer = 
            StringSubstr(_Nom.Objet.A.Effacer, 0, StringLen(_Nom.Objet.A.Effacer) - 2);
         
         if (i==2 && !ObjectDelete(_Nom.Objet.A.Effacer)) _Nom.Objet.A.Effacer = 
            StringSubstr(_Nom.Objet.A.Effacer, 0, StringLen(_Nom.Objet.A.Effacer) - 3);                        
         
         if (i==3) ObjectDelete(_Nom.Objet.A.Effacer);                           
      }
   }
// ****************************************************************** //
// ************************** Stops Suiveurs ************************ //
// ****************************************************************** //
int Stop_Suiveurs()
   {
      string   Dépendance        = "";
      int      Trail.Entry.Pips  = 0;
      
      // Parcours de l'ensemble des trades en cours
      for (int i = OrdersTotal(); i >= 0; i--)
         {
            OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
            
            // Extraction du numéro de la dépendance
            Dépendance = StringSubstr(DoubleToStr(OrderMagicNumber(), 0), 2, 3);  
            
            // Il s'agit d'un stop suiveur ...Et uniquement un STOP
            if (Dépendance == "4" && (OrderType() == OP_BUYSTOP || OrderType() == OP_SELLSTOP)) 
               {
                  // Initialisation du tableau Content[]
                  for (int j = 0; j < ArrayRange(Content, 1) ; j++) Content[j] = "";
                  
                  // Extraction des données de la chaîne Comment
                  Comment_Extract(OrderComment());
                  
                  // La valeur du Trail.Entry.Pips se trouve dans Content[2]
                  Trail.Entry.Pips  = StrToInteger(Content[2]);
                  
                  // Ajuster le stop suiveur en fonction de cette valeur
                  if (Trail.Entry.Pips != 0)
                     Ajust_Stops_Suiveurs_?(OrderTicket(), Trail.Entry.Pips);
                  else continue;
               }          
            else continue; 
         }
   }
// ****************************************************************** //
// ************ Extraction du commentaire de chaque ordre *********** //
// ****************************************************************** //
// 
// Architecture du commentaire :
// Info1#Info2#...#Info3 (séparateur = #)
//
// ****************************************************************** //
int Comment_Extract(string _Commentaire)
   {
      // Initialisations
      int      start = 0, pos = 0, n.sep = 0;
      string   chn = "";
      // Extraction du contenu de la chaîne commentaire dans Content[] 
      // Type commentaire : 0103#456#910     
      while (StringLen(chn) < StringLen(_Commentaire) - n.sep)
         {
            // Lecture de la position du caractère séparateur #
            pos = StringFind(_Commentaire, "#", start);
            
            // Extraction de la chaine de caract. comprise entre start et pos
            if (pos != -1)
               Content[n.sep] = StringSubstr(_Commentaire, start, pos - start);
            
            else if (pos == -1 && start > 0)
               Content[n.sep] = StringSubstr(_Commentaire, start, StringLen(_Commentaire) - start);               
            
            else break;
                        
            // Incrémentation de start à la position qui suit immédiatement Pos
            start = pos + 1;     
            
            // Incrémentation indice tableau
            n.sep++; 
            
            // Construction de chn (chaîne comparative)
            chn = StringConcatenate(chn, Content[n.sep]);   
         }   
   }
// ****************************************************************** //
// ******************* Ajustement Stops Suiveurs ? ****************** //
// ****************************************************************** //
bool Ajust_Stops_Suiveurs_?(int _Order.Ticket, int _Trail.Entry.Pips)
   {
      double bsl = 0, b.tsl.ent = 0, ssl = 0, s.tsl.ent = 0;
      int cnt = 0;
      // Sauvegarde de la distance entre le Stoploss et le point d'entrée théorique
      double dStop = MathAbs(OrderOpenPrice() - OrderStopLoss());
      // Rafraîchir les cotations
      RefreshRates();
      
      // Sélectionner l'ordre
      OrderSelect(_Order.Ticket, SELECT_BY_TICKET);       
      
      // Si on cherche à ajuster un stop pending achat
      if (OrderType() == OP_BUYSTOP)
         {
            bsl = _Trail.Entry.Pips * Point; 
            
            // Le point d'entrée achat doit-il être modifié ?
            if (Bid < (OrderOpenPrice() - bsl))
               {
                  // Ajustement du nouveau point d'entrée théorique
                  b.tsl.ent = NormalizeDouble(OrderOpenPrice() - ((OrderOpenPrice() - bsl) - Bid), Digits);
                  
                  // Attente aléatoire si les conditions ne sont pas réunies
                  Attendre_Disponibilité_pr_Ordre();
                  
                  // Tentative de modification de l'ordre
                  while (cnt < n.Tentatives.Connection && !OrderModify(_Order.Ticket, b.tsl.ent, 
                              b.tsl.ent - dStop, OrderTakeProfit(), OrderExpiration(), MediumVioletRed))
                     {
                        if (!IsTradeAllowed() || IsTradeContextBusy() || !IsConnected()) 
                           Temps_Attente_Aleatoire(); // Attente
                     }
               }
         }
      
      // Si on cherche à ajuster un stop pending vente
      if (OrderType() == OP_SELLSTOP)  
         {
            ssl = _Trail.Entry.Pips * Point;        
            // Le stop perte doit-il être modifié ?
            if (Ask > (OrderOpenPrice() + ssl))
               {
                  // Ajustement du nouveau point d'entrée théorique
                  s.tsl.ent = NormalizeDouble(OrderOpenPrice() + (Ask - (OrderOpenPrice() + ssl)), Digits);
                  // Attente aléatoire si les conditions ne sont pas réunies
                  Attendre_Disponibilité_pr_Ordre();
                  // Tentative de modification de l'ordre                  
                  while (cnt < n.Tentatives.Connection && !OrderModify(_Order.Ticket, s.tsl.ent, 
                              s.tsl.ent + dStop, OrderTakeProfit(), OrderExpiration(), MediumVioletRed))
                     {
                        if (!IsTradeAllowed() || IsTradeContextBusy() || !IsConnected()) 
                           Temps_Attente_Aleatoire(); // Attente
                     }
               }            
         }
   }
             
            
            
            
Comments