English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
MQL5 Cookbook : Comment éviter les erreurs lors de la définition/modification des niveaux de trade

MQL5 Cookbook : Comment éviter les erreurs lors de la définition/modification des niveaux de trade

MetaTrader 5Exemples | 13 janvier 2022, 09:43
163 0
Anatoli Kazharski
Anatoli Kazharski

Introduction

Dans la continuité de notre travail sur l'Expert Advisor de l'article précédent de la série intitulé "MQL5 Cookbook : Analyse des propriétés de position dans le testeur de stratégie MetaTrader 5", nous l'améliorerons avec de nombreuses fonctions utiles, ainsi que d'améliorer et d'optimiser celles existantes.

Les questions des débutants concernant les erreurs survenant lors de la définition/modification des niveaux de trade (Stop Loss, Take Profit et ordres en attente) sont loin d'être rares dans les forums de programmation MQL. Je pense que beaucoup d'entre vous doivent déjà être familiers avec le message du journal se terminant par [Arrêts non valides]. Dans cet article, nous allons créer des fonctions qui normalisent et vérifient l'exactitude des valeurs de niveau de transaction avant d'ouvrir/modifier une position.

L'Expert Advisor aura cette fois des paramètres externes qui peuvent être optimisés dans le testeur de stratégie MetaTrader 5 et ressemblera à certains égards à un simple système de trading. Nous avons certainement encore un long chemin à parcourir avant de pouvoir développer un véritable système de trading. Mais Rome ne s'est pas construite en un jour. Nous avons donc encore beaucoup à faire.

L'optimisation du code dans les fonctions existantes sera envisagée au fur et à mesure du déroulement de l'article. Le panneau d'informations ne sera pas traité à ce stade car nous devons encore examiner certaines propriétés de position qui ne peuvent pas être obtenues à l'aide d'identifiants standard (l'utilisation de l'historique des transactions est requise). Ce sujet sera néanmoins traité dans l'un des articles suivants de la série.


Développement d’Expert Advisor

Commençons. Comme d'habitude, nous commençons par insérer des énumérations, des variables, des tableaux et des fonctions auxiliaires supplémentaires au début du fichier. Nous aurons besoin d'une fonction qui nous permettra d'obtenir facilement les propriétés des symboles. La même approche simple sera nécessaire pour obtenir les propriétés de position.

Nous avons vu dans les articles précédents que toutes les propriétés de position étaient affectées aux variables globales à la fois dans la fonction GetPositionProperties. Cette fois, nous allons essayer de fournir une possibilité d'obtenir chaque propriété séparément. Vous trouverez ci-dessous deux énumérations pour la mise en œuvre de ce qui précède. Les fonctions elles-mêmes seront revues un peu plus tard.

//--- Enumeration of position properties
enum ENUM_POSITION_PROPERTIES
  {
   P_SYMBOL        = 0,
   P_MAGIC         = 1,
   P_COMMENT       = 2,
   P_SWAP          = 3,
   P_COMMISSION    = 4,
   P_PRICE_OPEN    = 5,
   P_PRICE_CURRENT = 6,
   P_PROFIT        = 7,
   P_VOLUME        = 8,
   P_SL            = 9,
   P_TP            = 10,
   P_TIME          = 11,
   P_ID            = 12,
   P_TYPE          = 13,
   P_ALL           = 14
  };
//--- Enumeration of symbol properties
enum ENUM_SYMBOL_PROPERTIES
  {
   S_DIGITS       = 0,
   S_SPREAD       = 1,
   S_STOPSLEVEL   = 2,
   S_POINT        = 3,
   S_ASK          = 4,
   S_BID          = 5,
   S_VOLUME_MIN   = 6,
   S_VOLUME_MAX   = 7,
   S_VOLUME_LIMIT = 8,
   S_VOLUME_STEP  = 9,
   S_FILTER       = 10,
   S_UP_LEVEL     = 11,
   S_DOWN_LEVEL   = 12,
   S_ALL          = 13
  };

L'énumération ENUM_SYMBOL_PROPERTIES ne contient pas toutes les propriétés de symbole mais elles peuvent être ajoutées à tout moment, si nécessaire. L'énumération contient également des propriétés définies par l'utilisateur (10, 11, 12) dont le calcul est basé sur d'autres propriétés de symbole. Il existe un identifiant qui peut être utilisé pour obtenir toutes les propriétés de l'énumération à la fois, comme dans l'énumération des propriétés de position.

Viennent ensuite les paramètres externes de l'Expert Advisor :

//--- External parameters of the Expert Advisor
input int            NumberOfBars=2;     // Number of Bullish/Bearish bars for a Buy/Sell
input double         Lot         =0.1;   // Lot
input double         StopLoss    =50;    // Stop Loss
input double         TakeProfit  =100;   // Take Profit
input double         TrailingStop=10;    // Trailing Stop
input bool           Reverse     =true;  // Position reverse

Regardons de plus près les paramètres externes :

  • NumberOfBars - ce paramètre définit le nombre de barres unidirectionnelles pour l'ouverture d'une position ;
  • Lot - position de volume ;
  • TakeProfit - Niveau de Take Profit en points. La valeur zéro signifie qu'aucun Take Profit n'a besoin d'être défini.
  • StopLoss - Niveau de Stop Loss en points. La valeur zéro signifie qu'aucun Stop Loss n'a besoin d'être défini.
  • TrailingStop - Valeur du Trailing Stop en points. Pour une position ACHAT, le calcul est basé sur le minimum de la barre (minimum moins le nombre de points du paramètre StopLoss). Pour une position VENTE, le calcul est basé sur le maximum de la barre (maximum plus le nombre de points du paramètre StopLoss). La valeur zéro indique que le Trailing Stop est désactivé.
  • L'inversion active/désactive la position inversée.

Seul le paramètre NumberOfBars a besoin d'être clarifié. Il ne sert à rien de régler cette valeur de paramètre sur, par exemple, plus de 5 car c'est assez rare et il serait déjà tard pour ouvrir une position après un tel mouvement. Nous aurons donc besoin d'une variable qui nous aidera à ajuster la valeur de ce paramètre :

//--- To check the value of the NumberOfBars external parameter
int                  AllowedNumberOfBars=0;

Ce paramètre déterminera également la quantité de données de barres qui seront stockées dans les tableaux de prix. Cela sera discuté dans un moment lorsque nous pourrons modifier les fonctions personnalisées.

Comme dans le cas des propriétés de position, nous déclarons des variables dans la portée globale pour les propriétés de symbole afin de fournir un accès à partir de n'importe quelle fonction :

//--- Symbol properties
int                  sym_digits=0;           // Number of decimal places
int                  sym_spread=0;           // Spread in points
int                  sym_stops_level=0;      // Stops level
double               sym_point=0.0;          // Point value
double               sym_ask=0.0;            // Ask price
double               sym_bid=0.0;            // Bid price
double               sym_volume_min=0.0;     // Minimum volume for a deal
double               sym_volume_max=0.0;     // Maximum volume for a deal
double               sym_volume_limit=0.0;   // Maximum permissible volume for a position and orders in one direction
double               sym_volume_step=0.0;    // Minimum volume change step for a deal
double               sym_offset=0.0;         // Offset from the maximum possible price for a transaction
double               sym_up_level=0.0;       // Upper Stop level price
double               sym_down_level=0.0;     // Lower Stop level price

Étant donné que le Trailing Stop doit être calculé en fonction du haut et du bas de la barre, nous aurons besoin de tableaux pour ces données de barre :

//--- Price data arrays
double               close_price[]; // Close (closing prices of the bar)
double               open_price[];  // Open (opening prices of the bar)
double               high_price[];  // High (bar's highs)
double               low_price[];   // Low (bar's lows)

Passons maintenant à la modification et à la création de fonctions. Nous avons déjà la fonction GetBarsData qui copie les prix d'ouverture et de clôture des barres dans des tableaux de prix. Maintenant, nous avons aussi besoin de hauts et de bas. De plus, la valeur obtenue à partir du paramètre NumberOfBars doit être ajustée. Voici à quoi ressemble la fonction après la modification :

//+------------------------------------------------------------------+
//| Getting bar values                                               |
//+------------------------------------------------------------------+
void GetBarsData()
  {
//--- Adjust the number of bars for the position opening condition
   if(NumberOfBars<=1)
      AllowedNumberOfBars=2;              // At least two bars are required
   if(NumberOfBars>=5)
      AllowedNumberOfBars=5;              // but no more than 5
   else
      AllowedNumberOfBars=NumberOfBars+1; // and always more by one
//--- Reverse the indexing order (... 3 2 1 0)
   ArraySetAsSeries(close_price,true);
   ArraySetAsSeries(open_price,true);
   ArraySetAsSeries(high_price,true);
   ArraySetAsSeries(low_price,true);
//--- Get the closing price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyClose(_Symbol,Period(),0,AllowedNumberOfBars,close_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Close price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the opening price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyOpen(_Symbol,Period(),0,AllowedNumberOfBars,open_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Open price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the bar's high
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyHigh(_Symbol,Period(),0,AllowedNumberOfBars,high_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the High price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the bar's low
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyLow(_Symbol,Period(),0,AllowedNumberOfBars,low_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Low price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
  }

Les conditions exigeant au moins deux barres et toujours plus d'une sont là car on ne passera que par des barres complétées qui commencent par l'index [1]. En fait, les ajustements peuvent dans ce cas être considérés comme inutiles car les données de la barre peuvent être copiées à partir de l'index spécifié dans le troisième paramètre des fonctions CopyOpen, CopyClose, CopyHigh et CopyLow. La limite de 5 barres peut également être modifiée (à la hausse/à la baisse) à votre discrétion.

La fonction GetTradingSignal est maintenant devenue un peu plus complexe car la condition sera générée différemment selon le nombre de barres spécifié dans le paramètre NumberOfBars. De plus, nous utilisons maintenant un type plus correct de la valeur renvoyée - type de la commande :

//+------------------------------------------------------------------+
//| Determining trading signals                                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetTradingSignal()
  {
//--- A Buy signal (ORDER_TYPE_BUY) :
   if(AllowedNumberOfBars==2 && 
      close_price[1]>open_price[1])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==3 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==4 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==5 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3] && 
      close_price[4]>open_price[4])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars>=6 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3] && 
      close_price[4]>open_price[4] && 
      close_price[5]>open_price[5])
      return(ORDER_TYPE_BUY);
//--- A Sell signal (ORDER_TYPE_SELL) :
   if(AllowedNumberOfBars==2 && 
      close_price[1]<open_price[1])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==3 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==4 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==5 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3] && 
      close_price[4]<open_price[4])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars>=6 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3] && 
      close_price[4]<open_price[4] && 
      close_price[5]<open_price[5])
      return(ORDER_TYPE_SELL);
//--- No signal (WRONG_VALUE):
   return(WRONG_VALUE);
  }

Modifions maintenant la fonction GetPositionProperties. Dans les articles précédents, cela nous a permis d'obtenir toutes les propriétés à la fois. Cependant, il se peut que vous n'ayez parfois besoin d'obtenir qu'une seule propriété. Pour ce faire, vous pouvez certainement utiliser les fonctions standards offertes par le langage, pourtant cela ne serait pas aussi pratique que nous le souhaitons. Ci-dessous le code de la fonction GetPositionProperties modifié. Désormais, lors de la transmission d'un certain identifiant de l'énumération ENUM_POSITION_PROPERTIES, vous pouvez soit obtenir une certaine propriété de position unique, soit toutes les propriétés à la fois.

//+------------------------------------------------------------------+
//| Getting position properties                                      |
//+------------------------------------------------------------------+
void GetPositionProperties(ENUM_POSITION_PROPERTIES position_property)
  {
//--- Check if there is an open position
   pos_open=PositionSelect(_Symbol);
//--- If an open position exists, get its properties
   if(pos_open)
     {
      switch(position_property)
        {
         case P_SYMBOL        : pos_symbol=PositionGetString(POSITION_SYMBOL);                  break;
         case P_MAGIC         : pos_magic=PositionGetInteger(POSITION_MAGIC);                   break;
         case P_COMMENT       : pos_comment=PositionGetString(POSITION_COMMENT);                break;
         case P_SWAP          : pos_swap=PositionGetDouble(POSITION_SWAP);                      break;
         case P_COMMISSION    : pos_commission=PositionGetDouble(POSITION_COMMISSION);          break;
         case P_PRICE_OPEN    : pos_price=PositionGetDouble(POSITION_PRICE_OPEN);               break;
         case P_PRICE_CURRENT : pos_cprice=PositionGetDouble(POSITION_PRICE_CURRENT);           break;
         case P_PROFIT        : pos_profit=PositionGetDouble(POSITION_PROFIT);                  break;
         case P_VOLUME        : pos_volume=PositionGetDouble(POSITION_VOLUME);                  break;
         case P_SL            : pos_sl=PositionGetDouble(POSITION_SL);                          break;
         case P_TP            : pos_tp=PositionGetDouble(POSITION_TP);                          break;
         case P_TIME          : pos_time=(datetime)PositionGetInteger(POSITION_TIME);           break;
         case P_ID            : pos_id=PositionGetInteger(POSITION_IDENTIFIER);                 break;
         case P_TYPE          : pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); break;
         case P_ALL           :
            pos_symbol=PositionGetString(POSITION_SYMBOL);
            pos_magic=PositionGetInteger(POSITION_MAGIC);
            pos_comment=PositionGetString(POSITION_COMMENT);
            pos_swap=PositionGetDouble(POSITION_SWAP);
            pos_commission=PositionGetDouble(POSITION_COMMISSION);
            pos_price=PositionGetDouble(POSITION_PRICE_OPEN);
            pos_cprice=PositionGetDouble(POSITION_PRICE_CURRENT);
            pos_profit=PositionGetDouble(POSITION_PROFIT);
            pos_volume=PositionGetDouble(POSITION_VOLUME);
            pos_sl=PositionGetDouble(POSITION_SL);
            pos_tp=PositionGetDouble(POSITION_TP);
            pos_time=(datetime)PositionGetInteger(POSITION_TIME);
            pos_id=PositionGetInteger(POSITION_IDENTIFIER);
            pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);                     break;
         default: Print("The passed position property is not listed in the enumeration!");               return;
        }
     }
//--- If there is no open position, zero out variables for position properties
   else
      ZeroPositionProperties();
  }

De même, nous implémentons la fonction GetSymbolProperties pour obtenir les propriétés des symboles :

//+------------------------------------------------------------------+
//| Getting symbol properties                                        |
//+------------------------------------------------------------------+
void GetSymbolProperties(ENUM_SYMBOL_PROPERTIES symbol_property)
  {
   int lot_offset=1; // Number of points for the offset from the Stops level
//---
   switch(symbol_property)
     {
      case S_DIGITS        : sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);                   break;
      case S_SPREAD        : sym_spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);                   break;
      case S_STOPSLEVEL    : sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);   break;
      case S_POINT         : sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);                           break;
      //---
      case S_ASK           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);                       break;
      case S_BID           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);                       break;
         //---
      case S_VOLUME_MIN    : sym_volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);                 break;
      case S_VOLUME_MAX    : sym_volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);                 break;
      case S_VOLUME_LIMIT  : sym_volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);             break;
      case S_VOLUME_STEP   : sym_volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);               break;
      //---
      case S_FILTER        :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*sym_point),sym_digits);        break;
         //---
      case S_UP_LEVEL      :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);
         sym_up_level=NormalizeDouble(sym_ask+sym_stops_level*sym_point,sym_digits);                     break;
         //---
      case S_DOWN_LEVEL    :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);
         sym_down_level=NormalizeDouble(sym_bid-sym_stops_level*sym_point,sym_digits);                   break;
         //---
      case S_ALL           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);
         sym_volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
         sym_volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
         sym_volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         sym_volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
         sym_offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*sym_point),sym_digits);
         sym_up_level=NormalizeDouble(sym_ask+sym_stops_level*sym_point,sym_digits);
         sym_down_level=NormalizeDouble(sym_bid-sym_stops_level*sym_point,sym_digits);                   break;
         //---
      default: Print("The passed symbol property is not listed in the enumeration!"); return;
     }
  }

Veuillez noter que certaines propriétés de symbole peuvent exiger que vous obteniez d'abord d'autres propriétés.

Nous avons une nouvelle fonction, CorrectValueBySymbolDigits. Il renvoie la valeur appropriée, en fonction du nombre de décimales dans le prix. Un entier ou un nombre réel peut être passé à la fonction. Le type des données transmises détermine la version de la fonction à utiliser. Cette fonctionnalité est appelée surcharge de fonction.

//+------------------------------------------------------------------+
//| Adjusting the value based on the number of digits in the price (int)|
//+------------------------------------------------------------------+
int CorrectValueBySymbolDigits(int value)
  {
   return (sym_digits==3 || sym_digits==5) ? value*=10 : value;
  }
//+------------------------------------------------------------------+
//| Adjusting the value based on the number of digits in the price (double)|
//+------------------------------------------------------------------+
double CorrectValueBySymbolDigits(double value)
  {
   return (sym_digits==3 || sym_digits==5) ? value*=10 : value;
  }

Notre Expert Advisor aura un paramètre externe pour spécifier le volume (Lot) de la position d'ouverture. Créons une fonction qui ajustera le lot en fonction de la spécification du symbole - CalculateLot :

//+------------------------------------------------------------------+
//| Calculating position lot                                         |
//+------------------------------------------------------------------+
double CalculateLot(double lot)
  {
//--- To adjust as per the step
   double corrected_lot=0.0;
//---
   GetSymbolProperties(S_VOLUME_MIN);  // Get the minimum possible lot
   GetSymbolProperties(S_VOLUME_MAX);  // Get the maximum possible lot
   GetSymbolProperties(S_VOLUME_STEP); // Get the lot increase/decrease step
//--- Adjust as per the lot step
   corrected_lot=MathRound(lot/sym_volume_step)*sym_volume_step;
//--- If less than the minimum, return the minimum
   if(corrected_lot<sym_volume_min)
      return(NormalizeDouble(sym_volume_min,2));
//--- If greater than the maximum, return the maximum
   if(corrected_lot>sym_volume_max)
      return(NormalizeDouble(sym_volume_max,2));
//---
   return(NormalizeDouble(corrected_lot,2));
  }

Passons maintenant aux fonctions directement liées au titre de l'article. Ils sont assez simples et directs et vous pouvez comprendre leur objectif sans aucune difficulté en utilisant les commentaires dans le code.

La fonction CalculateTakeProfit est utilisée pour calculer la valeur Take Profit :

//+------------------------------------------------------------------+
//| Calculating the Take Profit value                                |
//+------------------------------------------------------------------+
double CalculateTakeProfit(ENUM_ORDER_TYPE order_type)
  {
//--- If Take Profit is required
   if(TakeProfit>0)
     {
      //--- For the calculated Take Profit value
      double tp=0.0;
      //--- If you need to calculate the value for a SELL position
      if(order_type==ORDER_TYPE_SELL)
        {
         //--- Calculate the level
         tp=NormalizeDouble(sym_bid-CorrectValueBySymbolDigits(TakeProfit*sym_point),sym_digits);
         //--- Return the calculated value if it is lower than the lower limit of the Stops level
         //    If the value is higher or equal, return the adjusted value
         return(tp<sym_down_level ? tp : sym_down_level-sym_offset);
        }
      //--- If you need to calculate the value for a BUY position
      if(order_type==ORDER_TYPE_BUY)
        {
         //--- Calculate the level
         tp=NormalizeDouble(sym_ask+CorrectValueBySymbolDigits(TakeProfit*sym_point),sym_digits);
         //--- Return the calculated value if it is higher that the upper limit of the Stops level
         //    If the value is lower or equal, return the adjusted value
         return(tp>sym_up_level ? tp : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

La fonction CalculateStopLoss est utilisée pour calculer la valeur Stop Loss :

//+------------------------------------------------------------------+
//| Calculating the Stop Loss value                                  |
//+------------------------------------------------------------------+
double CalculateStopLoss(ENUM_ORDER_TYPE order_type)
  {
//--- If Stop Loss is required
   if(StopLoss>0)
     {
      //--- For the calculated Stop Loss value
      double sl=0.0;
      //--- If you need to calculate the value for a BUY position
      if(order_type==ORDER_TYPE_BUY)
        {
         // Calculate the level
         sl=NormalizeDouble(sym_ask-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- Return the calculated value if it is lower that the lower limit of the Stops level
         //    If the value is higher or equal, return the adjusted value
         return(sl<sym_down_level ? sl : sym_down_level-sym_offset);
        }
      //--- If you need to calculate the value for a SELL position
      if(order_type==ORDER_TYPE_SELL)
        {
         //--- Calculate the level
         sl=NormalizeDouble(sym_bid+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- Return the calculated value if it is higher than the upper limit of the Stops level
         //    If the value is lower or equal, return the adjusted value
         return(sl>sym_up_level ? sl : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

La fonction CalculateTrailingStop est utilisée pour calculer la valeur du Trailing Stop :

//+------------------------------------------------------------------+
//| Calculating the Trailing Stop value                              |
//+------------------------------------------------------------------+
double CalculateTrailingStop(ENUM_POSITION_TYPE position_type)
  {
//--- Variables for calculations
   double            level       =0.0;
   double            buy_point   =low_price[1];    // The Low value for a Buy
   double            sell_point  =high_price[1];   // The High value for a Sell
//--- Calculate the level for a BUY position
   if(position_type==POSITION_TYPE_BUY)
     {
      //--- Bar's low minus the specified number of points
      level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
      //--- If the calculated level is lower than the lower limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level<sym_down_level)
         return(level);
      //--- If it is not lower, try to calculate based on the bid price
      else
        {
         level=NormalizeDouble(sym_bid-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- If the calculated level is lower than the limit, return the current value of the level
         //    otherwise set the nearest possible value
         return(level<sym_down_level ? level : sym_down_level-sym_offset);
        }
     }
//--- Calculate the level for a SELL position
   if(position_type==POSITION_TYPE_SELL)
     {
      // Bar's high plus the specified number of points
      level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
      //--- If the calculated level is higher than the upper limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level>sym_up_level)
         return(level);
      //--- If it is not higher, try to calculate based on the ask price
      else
        {
         level=NormalizeDouble(sym_ask+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- If the calculated level is higher than the limit, return the current value of the level
         //    Otherwise set the nearest possible value
         return(level>sym_up_level ? level : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

Nous avons maintenant toutes les fonctions nécessaires qui renvoient des valeurs correctes pour les opérations de trading. Créons une fonction qui vérifiera une condition de modification du Trailing Stop et la modifiera si la condition spécifiée est remplie - ModifyTrailingStop. Vous trouverez ci-dessous le code de cette fonction avec des commentaires détaillés.

Veuillez faire attention à l'utilisation de toutes les fonctions créées/modifiées ci-dessus. Le switch commutateur détermine la condition pertinente en fonction du type de position actuelle et le résultat de la condition est ensuite stocké dans la variable de condition. Pour modifier une position, nous utilisons la méthode PositionModify de la CTrade de la bibliothèque standard.

//+------------------------------------------------------------------+
//| Modifying the Trailing Stop level                                |
//+------------------------------------------------------------------+
void ModifyTrailingStop()
  {
//--- If the Trailing Stop and Stop Loss are set
   if(TrailingStop>0 && StopLoss>0)
     {
      double         new_sl=0.0;       // For calculating the new Stop Loss level
      bool           condition=false;  // For checking the modification condition
      //--- Get the flag of presence/absence of the position
      pos_open=PositionSelect(_Symbol);
      //--- If the position exists
      if(pos_open)
        {
         //--- Get the symbol properties
         GetSymbolProperties(S_ALL);
         //--- Get the position properties
         GetPositionProperties(P_ALL);
         //--- Get the Stop Loss level
         new_sl=CalculateTrailingStop(pos_type);
         //--- Depending on the position type, check the relevant condition for the Trailing Stop modification
         switch(pos_type)
           {
            case POSITION_TYPE_BUY  :
               //--- If the new Stop Loss value is higher
               //    than the current value plus the set step
               condition=new_sl>pos_sl+CorrectValueBySymbolDigits(TrailingStop*sym_point);
               break;
            case POSITION_TYPE_SELL :
               //--- If the new Stop Loss value is lower
               //    than the current value minus the set step
               condition=new_sl<pos_sl-CorrectValueBySymbolDigits(TrailingStop*sym_point);
               break;
           }
         //--- If there is a Stop Loss, compare the values before modification
         if(pos_sl>0)
           {
            //--- If the condition for the order modification is met, i.e. the new value is lower/higher 
            //    than the current one, modify the Trailing Stop of the position
            if(condition)
              {
               if(!trade.PositionModify(_Symbol,new_sl,pos_tp))
                  Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
              }
           }
         //--- If there is no Stop Loss, simply set it
         if(pos_sl==0)
           {
            if(!trade.PositionModify(_Symbol,new_sl,pos_tp))
               Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
  }

Ajustons maintenant la fonction TradingBlock en fonction de tous les changements ci-dessus. Tout comme dans la fonction ModifyTrailingStop, toutes les valeurs des variables d'un ordre de trading seront déterminées à l'aide du switch commutateur. Il réduit considérablement la quantité de code et simplifie les modifications ultérieures puisqu'au lieu d'une branche pour deux types de positions, il n'en reste qu'une.

//+------------------------------------------------------------------+
//| Trading block                                                    |
//+------------------------------------------------------------------+
void TradingBlock()
  {
   ENUM_ORDER_TYPE      signal=WRONG_VALUE;                 // Variable for getting a signal
   string               comment="hello :)";                 // Position comment
   double               tp=0.0;                             // Take Profit
   double               sl=0.0;                             // Stop Loss
   double               lot=0.0;                            // Volume for position calculation in case of reverse position
   double               position_open_price=0.0;            // Position opening price
   ENUM_ORDER_TYPE      order_type=WRONG_VALUE;             // Order type for opening a position
   ENUM_POSITION_TYPE   opposite_position_type=WRONG_VALUE; // Opposite position type
//--- Get a signal
   signal=GetTradingSignal();
//--- If there is no signal, exit
   if(signal==WRONG_VALUE)
      return;
//--- Find out if there is a position
   pos_open=PositionSelect(_Symbol);
//--- Get all symbol properties
   GetSymbolProperties(S_ALL);
//--- Determine values for trade variables
   switch(signal)
     {
      //--- Assign values to variables for a BUY
      case ORDER_TYPE_BUY  :
         position_open_price=sym_ask;
         order_type=ORDER_TYPE_BUY;
         opposite_position_type=POSITION_TYPE_SELL;
         break;
         //--- Assign values to variables for a SELL
      case ORDER_TYPE_SELL :
         position_open_price=sym_bid;
         order_type=ORDER_TYPE_SELL;
         opposite_position_type=POSITION_TYPE_BUY;
         break;
     }
//--- Calculate the Take Profit and Stop Loss levels
   sl=CalculateStopLoss(order_type);
   tp=CalculateTakeProfit(order_type);
//--- If there is no position
   if(!pos_open)
     {
      //--- Adjust the volume
      lot=CalculateLot(Lot);
      //--- Open a position
      //    If the position failed to open, print the relevant message
      if(!trade.PositionOpen(_Symbol,order_type,lot,position_open_price,sl,tp,comment))
        {
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
     }
//--- If there is a position
   else
     {
      //--- Get the position type
      GetPositionProperties(P_TYPE);
      //--- If the position is opposite to the signal and the position reverse is enabled
      if(pos_type==opposite_position_type && Reverse)
        {
         //--- Get the position volume
         GetPositionProperties(P_VOLUME);
         //--- Adjust the volume
         lot=pos_volume+CalculateLot(Lot);
         //--- Open the position. If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,position_open_price,sl,tp,comment))
           {
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
//---
   return;
  }

Nous devons également apporter une autre correction importante à la fonction SetInfoPanel mais préparons d'abord quelques fonctions auxiliaires qui indiquent comment/où le programme est actuellement utilisé :

//+------------------------------------------------------------------+
//| Returning the testing flag                                       |
//+------------------------------------------------------------------+
bool IsTester()
  {
   return(MQL5InfoInteger(MQL5_TESTER));
  }
//+------------------------------------------------------------------+
//| Returning the optimization flag                                  |
//+------------------------------------------------------------------+
bool IsOptimization()
  {
   return(MQL5InfoInteger(MQL5_OPTIMIZATION));
  }
//+------------------------------------------------------------------+
//| Returning the visual testing mode flag                           |
//+------------------------------------------------------------------+
bool IsVisualMode()
  {
   return(MQL5InfoInteger(MQL5_VISUAL_MODE));
  }
//+------------------------------------------------------------------+
//| Returning the flag for real time mode outside the Strategy Tester|
//| if all conditions are met                                        |
//+------------------------------------------------------------------+
bool IsRealtime()
  {
   if(!IsTester() && !IsOptimization() && !IsVisualMode())
      return(true);
   else
      return(false); 
  }

La seule chose que nous devons ajouter à la fonction SetInfoPanel est une condition indiquant au programme que le panneau d'informations ne doit être affiché que dans les modes de visualisation et en temps réel. Si cela est ignoré, le temps de test sera 4 à 5 fois plus long. Ceci est particulièrement important lors de l'optimisation des paramètres.

//+------------------------------------------------------------------+
//| Setting the info panel                                           |
//|------------------------------------------------------------------+
void SetInfoPanel()
  {
//--- Visualization or real time modes
   if(IsVisualMode() || IsRealtime())
     {
     // The remaining code of the SetInfoPanel() function
     // ...
     }
  }

Il ne nous reste plus qu'à apporter quelques modifications aux principales fonctions du programme pour pouvoir procéder à l'optimisation des paramètres et aux tests de l'Expert Advisor.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the new bar
   CheckNewBar();
//--- Get the properties and set the panel
   GetPositionProperties(P_ALL);
//--- Set the info panel
   SetInfoPanel();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If the bar is not new, exit
   if(!CheckNewBar())
      return;
//--- If there is a new bar
   else
     {
      GetBarsData();          // Get bar data
      TradingBlock();         // Check the conditions and trade
      ModifyTrailingStop();   // Modify the Trailing Stop level
     }
//--- Get the properties and update the values on the panel
   GetPositionProperties(P_ALL);
//--- Update the info panel
   SetInfoPanel();
  }
//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Get position properties and update the values on the panel
   GetPositionProperties(P_ALL);
//--- Update the info panel
   SetInfoPanel();
  }


Optimisation des paramètres et test Expert Advisor

Optimisons maintenant les paramètres. Nous allons définir les paramètres du testeur de stratégie comme indiqué ci-dessous :

Fig. 1. Paramètres du Strategy Tester pour l'optimisation des paramètres.

Fig. 1. Paramètres du testeur de stratégie pour l'optimisation des paramètres.

Les paramètres de l'Expert Advisor se verront attribuer une large plage de valeurs :

Fig. 2. Paramètres de l'Expert Advisor pour l'optimisation des paramètres.

Fig. 2. Paramètres de l'Expert Advisor pour l'optimisation des paramètres.

L'optimisation a pris environ 7 minutes sur un processeur dual-core (Intel Core2 Duo P7350 @ 2.00GHz). Les résultats du test du facteur de récupération maximal sont les suivants :

Fig. 3. Résultats du test du facteur de récupération maximal.

Fig. 3. Résultats du test du facteur de récupération maximal.


Conclusion

C'est tout pour l'instant. Étudiez, testez, optimisez, expérimentez et épatez. Le code source de l'Expert Advisor présenté dans l'article peut être téléchargé en utilisant le lien ci-dessous pour une étude plus approfondie.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/643

Fichiers joints |
MQL5 Cookbook : L'historique des transactions et la bibliothèque de fonctions pour obtenir les propriétés de position MQL5 Cookbook : L'historique des transactions et la bibliothèque de fonctions pour obtenir les propriétés de position
Il est temps de résumer brièvement les informations fournies dans les articles précédents sur les propriétés de position. Dans cet article, nous allons créer quelques fonctions supplémentaires pour obtenir les propriétés qui ne peuvent être obtenues qu'après avoir accédé à l'historique des transactions. Nous nous familiariserons également avec les structures de données qui nous permettront d'accéder aux propriétés de position et de symbole de manière plus pratique.
MQL5 Cookbook : Analyse des propriétés de position dans le testeur de stratégie MetaTrader 5 MQL5 Cookbook : Analyse des propriétés de position dans le testeur de stratégie MetaTrader 5
Nous vous présenterons une version modifiée de l'Expert Advisor de l'article précédent "MQL5 Cookbook : Propriétés de la position dans le panneau d'informations personnalisé". Certains des problèmes que nous aborderons incluent l'obtention de données à partir de barres, la vérification de nouveaux événements de barre sur le symbole actuel, y compris une classe de trade de la bibliothèque standard dans un fichier, la création d'une fonction pour rechercher des signaux de trading et une fonction pour exécuter des opérations de trading , ainsi que la détermination des événements de trade dans la fonction OnTrade().
MQL5 Cookbook : Utilisation d'indicateurs pour définir les conditions de trading dans les Expert Advisors MQL5 Cookbook : Utilisation d'indicateurs pour définir les conditions de trading dans les Expert Advisors
Dans cet article, nous continuerons à modifier l'Expert Advisor sur lequel nous avons travaillé tout au long des articles précédents de la série MQL5 Cookbook. Cette fois, l'Expert Advisor sera enrichi d'indicateurs dont les valeurs serviront à vérifier les conditions d'ouverture des positions. Pour le pimenter, nous allons créer une liste déroulante dans les paramètres externes pour pouvoir sélectionner un des trois indicateurs de trading.
MQL5 Cookbook : Propriétés de position dans le panneau d'informations personnalisé MQL5 Cookbook : Propriétés de position dans le panneau d'informations personnalisé
Cette fois, nous allons créer un simple Expert Advisor qui obtiendra les propriétés de position sur le symbole actuel et les affichera sur le panneau d'informations personnalisé pendant le trading manuel. Le panneau d'informations sera créé à l'aide d'objets graphiques et les informations affichées seront actualisées à chaque tick. Cela va être beaucoup plus pratique que d'avoir tout le temps à exécuter manuellement le script décrit dans l'article précédent de la série intitulé "MQL5 Cookbook : Getting Position Properties".