English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
MQL5 Cookbook : Développement d’un Expert Advisor Multi-devises avec un nombre illimité de paramètres

MQL5 Cookbook : Développement d’un Expert Advisor Multi-devises avec un nombre illimité de paramètres

MetaTrader 5Exemples | 13 janvier 2022, 10:44
166 0
Anatoli Kazharski
Anatoli Kazharski

Introduction

L'Expert Advisor multidevises considéré dans l'article précédent "MQL5 Cookbook : Expert Advisor multi-devises - Une approche simple, nette et rapide", peut être très utile si le nombre de symboles et de paramètres de stratégie de trading utilisés est faible. Cependant, il existe une restriction sur le nombre de paramètres d'entrée d'un Expert Advisor dans MQL5 : ils ne doivent pas dépasser 1024.

Et même si ce nombre sera très souvent suffisant, il est très gênant d'utiliser une liste de paramètres aussi énorme. Chaque fois qu'une modification ou une optimisation des paramètres d'un symbole donné est requise, vous devez rechercher les paramètres de ce symbole spécifique dans la longue liste de paramètres.

Vous pouvez vous familiariser avec d'autres restrictions dans la section Inputs de l'aide du terminal client.

Dans cet article, nous allons créer un modèle qui utilise un seul ensemble de paramètres pour l'optimisation d'un système de trading, tout en permettant un nombre illimité de paramètres. La liste des symboles sera créée dans un fichier texte standard (*.txt). Les paramètres d'entrée pour chaque symbole seront également stockés dans des fichiers.

Il convient de mentionner ici que l'Expert Advisor travaillera sur un symbole en mode de fonctionnement normal, mais vous pourrez le tester dans le Strategy Tester sur une variété de symboles sélectionnés (sur chaque symbole séparément).

Il serait en fait encore plus pratique de créer la liste de symboles directement dans la fenêtre Market Watch, étant donné que cela permet même de sauvegarder des jeux de symboles prêts à l'emploi. Nous pourrions même demander à l'Expert Advisor d'obtenir la liste des symboles dans la fenêtre Market Watch directement à partir du Strategy Tester. Mais malheureusement, il n'est actuellement pas possible d'accéder à la fenêtre Market Watch depuis le Strategy Tester, nous devrons donc créer la liste de symboles manuellement au préalable ou à l'aide d'un script.


Développement d’Expert Advisor

L'Expert Advisor multidevises présenté dans l'article précédent "MQL5 Cookbook : Expert Advisor multi-devises - Une approche simple, nette et rapide" sera pris comme modèle. Décidons d'abord des paramètres d'entrée. Comme mentionné ci-dessus, nous ne laisserons qu'un seul ensemble de paramètres. Ci-dessous la liste des paramètres d'entrée de l'Expert Advisor :

//--- Input parameters of the Expert Advisor
sinput long                      MagicNumber          =777;      // Magic number
sinput int                       Deviation            =10;       // Slippage
sinput string                    delimeter_00=""; // --------------------------------
sinput int                       SymbolNumber         =1;        // Number of the tested symbol
sinput bool                      RewriteParameters    =false;    // Rewriting parameters
sinput ENUM_INPUTS_READING_MODE  ParametersReadingMode=FILE;     // Parameter reading mode
sinput string                    delimeter_01=""; // --------------------------------
input  int                       IndicatorPeriod      =5;        // Indicator period
input  double                    TakeProfit           =100;      // Take Profit
input  double                    StopLoss             =50;       // Stop Loss
input  double                    TrailingStop         =10;       // Trailing Stop
input  bool                      Reverse              =true;     // Position reversal
input  double                    Lot                  =0.1;      // Lot
input  double                    VolumeIncrease       =0.1;      // Position volume increase
input  double                    VolumeIncreaseStep   =10;       // Volume increase step

Seuls les paramètres avec le modificateur d'entrée seront écrits dans un fichier. De plus, nous devrions développer les trois nouveaux paramètres que nous n'avons jamais rencontrés auparavant.

  • SymbolNumber - ce paramètre indique le numéro du symbole du fichier qui contient la liste des symboles. S'il est défini sur 0, tous les symboles de la liste seront testés. Si vous dépassez la liste, le test ne sera pas effectué.
  • RewriteParameters - si ce paramètre a la valeur true, le fichier avec les paramètres du symbole spécifié (numéro dans le paramètre SymbolNumber) sera réécrit en utilisant les valeurs actuelles des paramètres d'entrée. Sinon, s'il est défini sur false, les paramètres seront lus à partir d'un fichier.
  • ParametersReadingMode - ce paramètre indique le mode de lecture par rapport aux paramètres d'entrée. Le type de paramètre est l'énumération personnalisée ENUM_INPUTS_READING_MODE qui offre deux options : lire à partir d'un fichier et utiliser les paramètres actuels.

Le code d'énumération ENUM_INPUTS_READING_MODE :

//+------------------------------------------------------------------+
//| Input parameter reading modes                                    |
//+------------------------------------------------------------------+
enum ENUM_INPUTS_READING_MODE
  {
   FILE             = 0, // File
   INPUT_PARAMETERS = 1  // Input parameters
  };

Le nombre de symboles était précédemment déterminé à l'aide de la constante NUMBER_OF_SYMBOLS. Cette valeur dépend maintenant de différents modes, nous allons donc la remplacer par la variable globale SYMBOLS_COUNT :

//--- Number of traded symbols. It is calculated and depends on the testing mode and the number of symbols in the file
int SYMBOLS_COUNT=0;

Au début du code de l'Expert Advisor, nous déclarons une autre constante, TESTED_PARAMETERS_COUNT, qui détermine la taille des tableaux et le nombre d'itérations de boucle lors de l'itération sur les paramètres d'entrée :

//--- Number of tested/optimized parameters
#define TESTED_PARAMETERS_COUNT 8

Le fichier avec la liste des symboles, ainsi que le dossier contenant les paramètres de chaque symbole doivent être placés dans le dossier commun du terminal car il est accessible au niveau du programme à la fois pendant le fonctionnement normal de l'Expert Advisor et lors de son test .

Maintenant, nous devons préparer les tableaux avec lesquels travailler plus tard. Tous les tableaux de l'Expert Advisor multi-devises de l'article précédent que nous allons modifier sont statiques, c'est-à-dire avec une taille d'éléments prédéfinie. Nous devons tous les rendre dynamiques car la taille dépendra désormais du nombre de symboles utilisés dans le fichier et du mode de lecture des paramètres d'entrée. Nous allons également ajouter de nouveaux tableaux (surlignés en jaune) qui sont nécessaires pour travailler avec des fichiers :

//--- Arrays of input parameters
string InputSymbols[];            // Symbol names
//---
int    InputIndicatorPeriod[];    // Indicator periods
double InputTakeProfit[];         // Take Profit values
double InputStopLoss[];           // Stop Loss values
double InputTrailingStop[];       // Trailing Stop values
bool   InputReverse[];            // Values of position reversal flags
double InputLot[];                // Lot values
double InputVolumeIncrease[];     // Position volume increases
double InputVolumeIncreaseStep[]; // Volume increase steps
//--- Array of handles for indicator agents
int spy_indicator_handles[];
//--- Array of signal indicator handles
int signal_indicator_handles[];
//--- Data arrays for checking trading conditions
struct PriceData
  {
   double            value[];
  };
PriceData open[];      // Opening price of the bar
PriceData high[];      // High price of the bar
PriceData low[];       // Low price of the bar
PriceData close[];     // Closing price of the bar
PriceData indicator[]; // Array of indicator values
//--- Arrays for getting the opening time of the current bar
struct Datetime
  {
   datetime          time[];
  };
Datetime lastbar_time[];
//--- Array for checking the new bar for each symbol
datetime new_bar[];
//--- Array of input parameter names for writing to the file
string input_parameters[TESTED_PARAMETERS_COUNT]=
  {
   "IndicatorPeriod",   // Indicator period
   "TakeProfit",        // Take Profit
   "StopLoss",          // Stop Loss
   "TrailingStop",      // Trailing Stop
   "Reverse",           // Position reversal
   "Lot",               // Lot
   "VolumeIncrease",    // Position volume increase
   "VolumeIncreaseStep" // Volume increase step
  };
//--- Array for untested symbols
string temporary_symbols[];
//--- Array for storing input parameters from the file of the symbol selected for testing or trading
double tested_parameters_from_file[];
//--- Array of input parameter values for writing to the file
double tested_parameters_values[TESTED_PARAMETERS_COUNT];

Ensuite, nous devons déterminer les tailles des tableaux et les initialiser à des valeurs.

Le tableau de valeurs de paramètres d'entrée créé ci-dessus sera initialisé dans la fonction InitializeTestedParametersValues() que nous allons créer dans le fichier InitializeArrays.mqh. Ce tableau sera utilisé pour écrire les valeurs des paramètres d'entrée dans le fichier.

//+------------------------------------------------------------------+
//| Initializing the array of tested input parameters                |
//+------------------------------------------------------------------+
void InitializeTestedParametersValues()
  {
   tested_parameters_values[0]=IndicatorPeriod;
   tested_parameters_values[1]=TakeProfit;
   tested_parameters_values[2]=StopLoss;
   tested_parameters_values[3]=TrailingStop;
   tested_parameters_values[4]=Reverse;
   tested_parameters_values[5]=Lot;
   tested_parameters_values[6]=VolumeIncrease;
   tested_parameters_values[7]=VolumeIncreaseStep;
  }

Considérons maintenant les opérations sur les fichiers. Tout d'abord, créez une autre bibliothèque de fonctions, FileFunctions.mqh, dans le dossier UnlimitedParametersEA\Include de votre Expert Advisor. Cette bibliothèque sera utilisée pour créer des fonctions liées à la lecture et à l'écriture de données dans un fichier. Incluez-le dans le fichier principal de l'Expert Advisor et d'autres fichiers du projet.

//---Include custom libraries
#include "Include\Enums.mqh"
#include "Include\InitializeArrays.mqh"
#include "Include\Errors.mqh"
#include "Include\FileFunctions.mqh"
#include "Include\TradeSignals.mqh"
#include "Include\TradeFunctions.mqh"
#include "Include\ToString.mqh"
#include "Include\Auxiliary.mqh"

Nous commençons par créer une fonction pour lire la liste des symboles à partir d'un fichier texte - ReadSymbolsFromFile(). Ce fichier (appelons-le TestedSymbols.txt) doit être placé dans le sous-dossier \Files du dossier commun du terminal client MetaTrader 5. Dans mon cas, ce sera C:\ProgramData\MetaQuotes\Terminal\Common mais vous devez vérifier soigneusement le chemin à l'aide de TERMINAL_COMMONDATA_PATH :

TerminalInfoString(TERMINAL_COMMONDATA_PATH)
Tous les fichiers personnalisés doivent être placés dans le sous-dossier \Files du dossier commun du terminal client ou dans <Terminal Data Folder>\MQL5\Files\. File functions ne peuvent accéder qu'à ces dossiers.

Pour vérifier la fonctionnalité, ajoutez quelques symboles au fichier texte créé, en séparant chacun des symboles par un saut de ligne ("\r\n") :

Fig. 1. Liste des symboles dans le fichier du dossier commun du terminal.

Fig. 1. Liste des symboles dans le fichier du dossier commun du terminal.

De plus, examinons le code de la fonction ReadSymbolsFromFile() :

//+------------------------------------------------------------------+
//| Returning the number of strings (symbols) in the file and        |
//| filling the temporary array of symbols temporary_symbols[]       |
//+------------------------------------------------------------------+
//--- When preparing the file, symbols in the list should be separated with a line break
int ReadSymbolsFromFile(string file_name)
  {
   int strings_count=0; // String counter
//--- Open the file for reading from the common folder of the terminal
   int file_handle=FileOpen(file_name,FILE_READ|FILE_ANSI|FILE_COMMON);
//--- If the file handle has been obtained
   if(file_handle!=INVALID_HANDLE)
     {
      ulong  offset =0; // Offset for determining the position of the file pointer
      string text ="";  // The read string will be written to this variable
      //--- Read until the current position of the file pointer reaches the end of the file or until the program is deleted
      while(!FileIsEnding(file_handle) || !IsStopped())
        {
         //--- Read until the end of the string or until the program is deleted
         while(!FileIsLineEnding(file_handle) || !IsStopped())
           {
            //--- Read the whole string
            text=FileReadString(file_handle);
            //--- Get the position of the pointer
            offset=FileTell(file_handle);
            //--- Go to the next string if this is not the end of the file
            //    For this purpose, increase the offset of the file pointer
            if(!FileIsEnding(file_handle))
               offset++;
            //--- Move it to the next string
            FileSeek(file_handle,offset,SEEK_SET);
            //--- If the string is not empty
            if(text!="")
              {
               //--- Increase the string counter
               strings_count++;
               //--- Increase the size of the array of strings,
               ArrayResize(temporary_symbols,strings_count);
               //--- Write the read string to the current index
               temporary_symbols[strings_count-1]=text;
              }
            //--- Exit the nested loop
            break;
           }
         //--- If this is the end of the file, terminate the main loop
         if(FileIsEnding(file_handle))
            break;
        }
      //--- Close the file
      FileClose(file_handle);
     }
//--- Return the number of strings in the file
   return(strings_count);
  }

Ici, le fichier texte est lu ligne par ligne. Le nom de chaque instrument financier qui a été lu est écrit dans le tableau temporaire Temporary_symbols[] créé précédemment (l'accessibilité réelle du symbole sur le serveur de trading sera vérifiée ultérieurement). De plus, à la fin la fonction retourne le nombre de chaînes lues, c'est à dire le nombre de symboles sur lesquels notre Expert Advisor sera testé.

Référez-vous toujours à MQL5 Reference pour obtenir des informations détaillées sur les fonctions et les identifiants que vous n'avez jamais rencontrés auparavant. Les commentaires fournis dans le code simplifieront le processus d'apprentissage.

Revenons au fichier InitializeArrays.mqh où nous allons créer la fonction InitializeInputSymbols() pour remplir le tableau InputSymbols[] des noms de symboles déclarés précédemment. Les débutants le trouveront probablement assez complexe, j'ai donc fourni des commentaires détaillés sur le code :

//+------------------------------------------------------------------+
//| Filling the InputSymbol[] array of symbols                       |
//+------------------------------------------------------------------+
void InitializeInputSymbols()
  {
   int    strings_count=0;    // Number of strings in the symbol file
   string checked_symbol="";  // To check the accessibility of the symbol on the trade server
//--- Get the number of symbols from the "TestedSymbols.txt" file
   strings_count=ReadSymbolsFromFile("TestedSymbols.txt");
//--- In optimization mode or in one of the two modes (testing or visualization), provided that the symbol NUMBER IS SPECIFIED
   if(IsOptimization() || ((IsTester() || IsVisualMode()) && SymbolNumber>0))
     {
      //--- Determine the symbol to be involved in parameter optimization
      for(int s=0; s<strings_count; s++)
        {
         //--- If the number specified in the parameters and the current loop index match
         if(s==SymbolNumber-1)
           {
            //--- Check whether the symbol is on the trade server
            if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="")
              {
               //--- Set the number of symbols
               SYMBOLS_COUNT=1;
               //--- Set the size of the array of symbols
               ArrayResize(InputSymbols,SYMBOLS_COUNT);
               //--- Write the symbol name
               InputSymbols[0]=checked_symbol;
              }
            //--- Exit
            return;
           }
        }
     }
//--- In testing or visualization mode, if you need to test ALL symbols from the list in the file
   if((IsTester() || IsVisualMode()) && SymbolNumber==0)
     {
      //--- Parameter reading mode: from the file
      if(ParametersReadingMode==FILE)
        {
         //--- Iterate over all symbols in the file
         for(int s=0; s<strings_count; s++)
           {
            //--- Check if the symbol is on the trade server
            if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="")
              {
               //--- Increase the symbol counter
               SYMBOLS_COUNT++;
               //--- Set the size of the array of symbols
               ArrayResize(InputSymbols,SYMBOLS_COUNT);
               //--- Write the symbol name
               InputSymbols[SYMBOLS_COUNT-1]=checked_symbol;
              }
           }
         //--- Exit
         return;
        }
      //--- Parameter reading mode: from input parameters of the Expert Advisor
      if(ParametersReadingMode==INPUT_PARAMETERS)
        {
         //--- Set the number of symbols
         SYMBOLS_COUNT=1;
         //--- Set the size of the array of symbols
         ArrayResize(InputSymbols,SYMBOLS_COUNT);
         //--- Write the current symbol name
         InputSymbols[0]=Symbol();
         //--- Exit
         return;
        }
     }
//--- In normal operation mode of the Expert Advisor, use the current chart symbol
   if(IsRealtime())
     {
      //--- Set the number of symbols
      SYMBOLS_COUNT=1;
      //--- Set the size of the array of symbols
      ArrayResize(InputSymbols,SYMBOLS_COUNT);
      //--- Write the symbol name
      InputSymbols[0]=Symbol();
     }
//---
  }

Une fois que la taille du tableau de paramètres d'entrée a été déterminée par le nombre de symboles utilisés, vous devez définir la taille de tous les autres tableaux de paramètres d'entrée. Implémentons-le en tant que fonction distincte - ResizeInputParametersArrays() :

//+------------------------------------------------------------------+
//| Setting the new size for arrays of input parameters              |
//+------------------------------------------------------------------+
void ResizeInputParametersArrays()
  {
   ArrayResize(InputIndicatorPeriod,SYMBOLS_COUNT);
   ArrayResize(InputTakeProfit,SYMBOLS_COUNT);
   ArrayResize(InputStopLoss,SYMBOLS_COUNT);
   ArrayResize(InputTrailingStop,SYMBOLS_COUNT);
   ArrayResize(InputReverse,SYMBOLS_COUNT);
   ArrayResize(InputLot,SYMBOLS_COUNT);
   ArrayResize(InputVolumeIncrease,SYMBOLS_COUNT);
   ArrayResize(InputVolumeIncreaseStep,SYMBOLS_COUNT);
  }

Maintenant, nous devons créer la fonctionnalité qui nous permettra de lire les valeurs des paramètres d'entrée pour chaque symbole, ainsi que d'écrire ces valeurs de paramètres dans un fichier séparé (pour chaque symbole) en fonction du mode d'entrée sélectionné et des paramètres de l'Expert Advisor. Cela se fera de la même manière que nous lisons la liste des symboles à partir du fichier. C'est une tâche complexe, alors divisons-la en plusieurs procédures.

Tout d'abord, nous devons apprendre à lire les valeurs des paramètres d'entrée d'un fichier dans un tableau. Le tableau contiendra des valeurs de type double. Nous convertirons plus tard certains d'entre eux (période de l'indicateur et indicateur d'inversion de position) en types int et bool, respectivement. Le descripteur de fichier ouvert et le tableau dans lequel les valeurs des paramètres du fichier sont stockées sont transmis à la fonction ReadInputParametersValuesFromFile() :

//+----------------------------------------------------------------------+
//| Reading parameters from the file and storing them in the passed array|
//| Text file format: key=value
//+----------------------------------------------------------------------+
bool ReadInputParametersValuesFromFile(int handle,double &array[])
  {
   int    delimiter_position  =0;  // Number of the symbol position "=" in the string
   int    strings_count       =0;  // String counter
   string read_string         =""; // The read string
   ulong  offset              =0;  // Position of the pointer (offset in bytes)
//--- Move the file pointer to the beginning of the file
   FileSeek(handle,0,SEEK_SET);
//--- Read until the current position of the file pointer reaches the end of the file
   while(!FileIsEnding(handle))
     {
      //--- If the user deleted the program
      if(IsStopped())
         return(false);
      //--- Read until the end of the string
      while(!FileIsLineEnding(handle))
        {
         //--- If the user deleted the program
         if(IsStopped())
            return(false);
         //--- Read the string
         read_string=FileReadString(handle);
         //--- Get the index of the separator ("=") in the read string
         delimiter_position=StringFind(read_string,"=",0);
         //--- Get everything that follows the separator ("=") until the end of the string
         read_string=StringSubstr(read_string,delimiter_position+1);
         //--- Place the obtained value converted to the double type in the output array
         array[strings_count]=StringToDouble(read_string);
         //--- Get the current position of the file pointer
         offset=FileTell(handle);
         //--- If it's the end of the string
         if(FileIsLineEnding(handle))
           {
            //--- Go to the next string if it's not the end of the file
            if(!FileIsEnding(handle))
               //--- Increase the offset of the file pointer by 1 to go to the next string
               offset++;
            //--- Move the file pointer relative to the beginning of the file
            FileSeek(handle,offset,SEEK_SET);
            //--- Increase the string counter
            strings_count++;
            //--- Exit the nested loop for reading the string
            break;
           }
        }
      //--- If it's the end of the file, exit the main loop
      if(FileIsEnding(handle))
         break;
     }
//--- Return the fact of successful completion
   return(true);
  }

Quels descripteurs de fichiers et tableaux allons-nous passer à cette fonction ? Cela dépendra des symboles avec lesquels nous travaillerons après les avoir lus à partir du fichier "TestedSymbols.txt". Chaque symbole correspondra à un certain fichier texte contenant les valeurs des paramètres d'entrée. Il y a deux cas de figure à considérer :

  1. Le fichier existe et nous lisons les valeurs des paramètres d'entrée à partir de ce fichier à l'aide de la fonction ReadInputParametersValuesFromFile() décrite ci-dessus.
  2. Le fichier n'existe pas ou nous devons réécrire les valeurs des paramètres d'entrée existants.

Le format du fichier texte (un fichier .ini, bien que vous puissiez choisir toute autre extension que vous jugerez nécessaire) contenant les valeurs des paramètres d'entrée sera simple :

input_parameter_name1=value
input_parameter_name2=value
....
input_parameter_nameN=value

Combinons cette logique en une seule fonction, ReadWriteInputParameters(), dont le code est fourni ci-dessous :

//+------------------------------------------------------------------+
//| Reading/writing input parameters from/to a file for a symbol     |
//+------------------------------------------------------------------+
void ReadWriteInputParameters(int symbol_number,string path)
  {
   string file_name=path+InputSymbols[symbol_number]+".ini"; // File name
//---
   Print("Find the file '"+file_name+"' ...");
//--- Open the file with input parameters of the symbol
   int file_handle_read=FileOpen(file_name,FILE_READ|FILE_ANSI|FILE_COMMON);
   
//--- Scenario #1: the file exists and parameter values do not need to be rewritten
   if(file_handle_read!=INVALID_HANDLE && !RewriteParameters)
     {
      Print("The file '"+InputSymbols[symbol_number]+".ini' exists, reading...");
      //--- Set the array size
      ArrayResize(tested_parameters_from_file,TESTED_PARAMETERS_COUNT);
      //--- Fill the array with file values
      ReadInputParametersValuesFromFile(file_handle_read,tested_parameters_from_file);
      //--- If the array size is correct
      if(ArraySize(tested_parameters_from_file)==TESTED_PARAMETERS_COUNT)
        {
         //--- Write parameter values to arrays
         InputIndicatorPeriod[symbol_number]    =(int)tested_parameters_from_file[0];
         Print("InputIndicatorPeriod[symbol_number] = "+(string)InputIndicatorPeriod[symbol_number]);
         InputTakeProfit[symbol_number]         =tested_parameters_from_file[1];
         InputStopLoss[symbol_number]           =tested_parameters_from_file[2];
         InputTrailingStop[symbol_number]       =tested_parameters_from_file[3];
         InputReverse[symbol_number]            =(bool)tested_parameters_from_file[4];
         InputLot[symbol_number]                =tested_parameters_from_file[5];
         InputVolumeIncrease[symbol_number]     =tested_parameters_from_file[6];
         InputVolumeIncreaseStep[symbol_number] =tested_parameters_from_file[7];
        }
      //--- Close the file and exit
      FileClose(file_handle_read);
      return;
     }
//--- Scenario #2: If the file does not exist or the parameters need to be rewritten
   if(file_handle_read==INVALID_HANDLE || RewriteParameters)
     {
      //--- Close the handle of the file for reading
      FileClose(file_handle_read);
      //--- Get the handle of the file for writing
      int file_handle_write=FileOpen(file_name,FILE_WRITE|FILE_CSV|FILE_ANSI|FILE_COMMON,"");
      //--- If the handle has been obtained
      if(file_handle_write!=INVALID_HANDLE)
        {
         string delimiter="="; // Separator
         //--- Write the parameters
         for(int i=0; i<TESTED_PARAMETERS_COUNT; i++)
           {
            FileWrite(file_handle_write,input_parameters_names[i],delimiter,tested_parameters_values[i]);
            Print(input_parameters_names[i],delimiter,tested_parameters_values[i]);
           }
         //--- Write parameter values to arrays
         InputIndicatorPeriod[symbol_number]    =(int)tested_parameters_values[0];
         InputTakeProfit[symbol_number]         =tested_parameters_values[1];
         InputStopLoss[symbol_number]           =tested_parameters_values[2];
         InputTrailingStop[symbol_number]       =tested_parameters_values[3];
         InputReverse[symbol_number]            =(bool)tested_parameters_values[4];
         InputLot[symbol_number]                =tested_parameters_values[5];
         InputVolumeIncrease[symbol_number]     =tested_parameters_values[6];
         InputVolumeIncreaseStep[symbol_number] =tested_parameters_values[7];
         //--- Depending on the indication, print the relevant message
         if(RewriteParameters)
            Print("The file '"+InputSymbols[symbol_number]+".ini' with parameters of the '"+EXPERT_NAME+".ex5 Expert Advisor has been rewritten'");
         else
            Print("The file '"+InputSymbols[symbol_number]+".ini' with parameters of the '"+EXPERT_NAME+".ex5 Expert Advisor has been created'");
        }
      //--- Close the handle of the file for writing
      FileClose(file_handle_write);
     }
  }

La dernière fonction de fichier CreateInputParametersFolder() va créer un dossier avec le nom de l'Expert Advisor dans le dossier commun du terminal client. Il s'agit du dossier à partir duquel les fichiers texte (dans notre cas, les fichiers .ini) avec les valeurs des paramètres d'entrée seront lus/écrits. Tout comme dans la fonction précédente, nous allons vérifier si le dossier existe. Si le dossier a été créé avec succès ou existe déjà, la fonction renverra le chemin ou une chaîne vide en cas d'erreur :

//+----------------------------------------------------------------------------------+
//| Creating a folder for files of input parameters in case the folder does not exist|
//| and returns the path in case of success                                          |
//+----------------------------------------------------------------------------------+
string CreateInputParametersFolder()
  {
   long   search_handle       =INVALID_HANDLE;   // Folder/file search handle
   string EA_root_folder      =EXPERT_NAME+"\\"; // Root folder of the Expert Advisor
   string returned_filename   ="";               // Name of the found object (file/folder)
   string search_path         ="";               // Search path
   string folder_filter       ="*";              // Search filter (* - check all files/folders)
   bool   is_root_folder      =false;            // Flag of existence/absence of the root folder of the Expert Advisor

//--- Find the root folder of the Expert Advisor
   search_path=folder_filter;
//--- Set the search handle in the common folder of the terminal
   search_handle=FileFindFirst(search_path,returned_filename,FILE_COMMON);
//--- If the first folder is the root folder, flag it
   if(returned_filename==EA_root_folder)
      is_root_folder=true;
//--- If the search handle has been obtained
   if(search_handle!=INVALID_HANDLE)
     {
      //--- If the first folder is not the root folder
      if(!is_root_folder)
        {
         //--- Iterate over all files to find the root folder
         while(FileFindNext(search_handle,returned_filename))
           {
            //--- Process terminated by the user
            if(IsStopped())
               return("");
            //--- If it is found, flag it
            if(returned_filename==EA_root_folder)
              {
               is_root_folder=true;
               break;
              }
           }
        }
      //--- Close the root folder search handle
      FileFindClose(search_handle);
      //search_handle=INVALID_HANDLE;
     }
//--- Otherwise print an error message
   else
      Print("Error when getting the search handle or "
            "the folder '"+TerminalInfoString(TERMINAL_COMMONDATA_PATH)+"' is empty: ",ErrorDescription(GetLastError()));

//--- Based on the check results, create the necessary folder
   search_path=EXPERT_NAME+"\\";
//--- If the root folder of the Expert Advisor does not exist
   if(!is_root_folder)
     {
      //--- Create it. 
      if(FolderCreate(EXPERT_NAME,FILE_COMMON))
        {
         //--- If the folder has been created, flag it
         is_root_folder=true;
         Print("The root folder of the '..\\"+EXPERT_NAME+"\\ Expert Advisor has been created'");
        }
      else
        {
         Print("Error when creating "
               "the root folder of the Expert Advisor: ",ErrorDescription(GetLastError()));
         return("");
        }
     }
//--- If the required folder exists
   if(is_root_folder)
      //--- Return the path to create a file for writing parameters of the Expert Advisor
      return(search_path+"\\");
//--- In case of errors, return an empty string
   return("");
  }

Regroupons maintenant les appels de fonction ci-dessus dans une seule fonction - InitializeInputParametersArrays(). Cette fonction couvre 4 options d'initialisation des paramètres d'entrée lorsque vous travaillez avec l'Expert Advisor :

  1. Le mode de fonctionnement standard (ou optimisation des paramètres pour un symbole sélectionné) utilisant les valeurs des paramètres d'entrée actuels
  2. La réécriture des paramètres dans les fichiers lors du test ou de l'optimisation
  3. Le test d’un symbole sélectionné
  4. Le teste de tous les symboles de la liste à partir du fichier

Toutes les opérations sont expliquées dans les commentaires détaillés du code :

//+-------------------------------------------------------------------+
//| Initializing arrays of input parameters depending on the mode     |
//+-------------------------------------------------------------------+
void InitializeInputParametersArrays()
  {
   string path=""; // To determine the folder that contains files with input parameters
//--- Mode #1 :
//    - standard operation mode of the Expert Advisor OR
//    - optimization mode OR
//    - reading from input parameters of the Expert Advisor without rewriting the file
   if(IsRealtime() || IsOptimization() || (ParametersReadingMode==INPUT_PARAMETERS && !RewriteParameters))
     {
      //--- Initialize parameter arrays to current values
      InitializeWithCurrentValues();
      return;
     }
//--- Mode #2 :
//    - rewriting parameters in the file for the specified symbol
   if(RewriteParameters)
     {
      //--- Initialize parameter arrays to current values
      InitializeWithCurrentValues();
      //--- If the folder of the Expert Advisor exists or in case no errors occurred when it was being created
      if((path=CreateInputParametersFolder())!="")
         //--- Write/read the file of symbol parameters
         ReadWriteInputParameters(0,path);
      //---
      return;
     }
//--- Mode #3 :
//    - testing (it may be in visualization mode, without optimization) the Expert Advisor on a SELECTED symbol
   if((IsTester() || IsVisualMode()) && !IsOptimization() && SymbolNumber>0)
     {
      //--- If the folder of the Expert Advisor exists or in case no errors occurred when it was being created
      if((path=CreateInputParametersFolder())!="")
        {
         //--- Iterate over all symbols (in this case, the number of symbols = 1)
         for(int s=0; s<SYMBOLS_COUNT; s++)
            //--- Write or read the file of symbol parameters
            ReadWriteInputParameters(s,path);
        }
      return;
     }
//--- Mode #4 :
//    - testing (it may be in visualization mode, without optimization) the Expert Advisor on ALL symbols
   if((IsTester() || IsVisualMode()) && !IsOptimization() && SymbolNumber==0)
     {
      //--- If the folder of the Expert Advisor exists and
      //    no errors occurred when it was being created
      if((path=CreateInputParametersFolder())!="")
        {
         //--- Iterate over all symbols
         for(int s=0; s<SYMBOLS_COUNT; s++)
            //--- Write or read the file of symbol parameters
            ReadWriteInputParameters(s,path);
        }
      return;
     }
  }

Dans les modes #1 et #2, nous utilisons la fonction InitializeWithCurrentValues(). Il initialise zéro (unique) index aux valeurs actuelles des paramètres d'entrée. En d'autres termes, cette fonction est utilisée lorsqu'un seul symbole est requis :

//+------------------------------------------------------------------+
//| Initializing arrays of input parameters to current values        |
//+------------------------------------------------------------------+
void InitializeWithCurrentValues()
  {
   InputIndicatorPeriod[0]=IndicatorPeriod;
   InputTakeProfit[0]=TakeProfit;
   InputStopLoss[0]=StopLoss;
   InputTrailingStop[0]=TrailingStop;
   InputReverse[0]=Reverse;
   InputLot[0]=Lot;
   InputVolumeIncrease[0]=VolumeIncrease;
   InputVolumeIncreaseStep[0]=VolumeIncreaseStep;
  }

Maintenant, nous devons faire la chose la plus simple, mais la plus importante : l’implémentation des appels consécutifs des fonctions ci-dessus depuis le point d'entrée, les OnInit() :

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- Initialize the array of tested input parameters for writing to the file
   InitializeTestedParametersValues();
//--- Fill the array of symbol names
   InitializeInputSymbols();
//--- Set the size of arrays of input parameters
   ResizeInputParametersArrays();
//--- Initialize arrays of indicator handles
   InitializeIndicatorHandlesArrays();
//--- Initialize arrays of input parameters depending on the operation mode of the Expert Advisor
   InitializeInputParametersArrays();
//--- Get agent handles from the "EventsSpy.ex5" indicator
   GetSpyHandles();
//--- Get indicator handles
   GetIndicatorHandles();
//--- Initialize the new bar
   InitializeNewBarArray();
//--- Initialize price arrays and indicator buffers
   ResizeDataArrays();
  }

Donc, nous en avons fini avec le code. Vous pouvez vous familiariser avec les fonctions décrites à l'aide des fichiers joints à l'article, ils n'ont rien de compliqué. Maintenant, allons plus loin pour voir ce que nous avons comme résultat et comment il peut être utilisé.


Optimisation des paramètres et test Expert Advisor

Comme déjà mentionné, vous devriez avoir le fichier TestedSymbols.txt avec la liste des symboles dans le dossier commun du terminal client. À titre d'exemple/à des fins de test, nous allons créer une liste de trois symboles : AUDUSD, EURUSD et NZDUSD. Nous allons maintenant optimiser consécutivement les paramètres d'entrée pour chaque symbole séparément. Le testeur de stratégie doit être configuré comme indiqué ci-dessous :

Fig. 2. Paramètres du testeur de stratégie.

Fig. 2. Paramètres du testeur de stratégie.

Vous pouvez définir n'importe quel symbole (dans notre cas, EURUSD) dans l'onglet "Paramètres" car cela n'affecte pas l'Expert Advisor. Ensuite, nous sélectionnons les paramètres d'optimisation de l'Expert Advisor :

Fig. 3. Paramètres d'entrée de l'Expert Advisor.

Fig. 3. Paramètres d'entrée de l'Expert Advisor.

La figure ci-dessus montre que le paramètre SymbolNumber (Numéro du symbole testé) est défini sur 1. Cela signifie que lors de l'exécution de l'optimisation, l'Expert Advisor utilisera le premier symbole de la liste du fichier TestedSymbols.txt. Dans notre cas, il s'agit de l'AUDUSD.

Remarque : en raison des particularités de cet Expert Advisor (la liste des symboles est définie par lecture du fichier texte), l'optimisation avec des agents distants ne sera pas possible.

Nous essaierons de contourner cette restriction dans l'un des articles suivants de cette série.

Une fois l'optimisation terminée, vous pouvez exécuter des tests, en étudiant les résultats de différentes passes d'optimisation. Si vous souhaitez que l'Expert Advisor lise les paramètres du fichier, vous devez sélectionner File dans la liste déroulante du paramètre ParametersReadingMode (mode de lecture des paramètres). Pour pouvoir utiliser les paramètres actuels de l'Expert Advisor (définis dans l'onglet "Paramètres"), vous devez sélectionner l'option Input parameters.

L'option Input parameters est certainement requise pour visualiser les résultats de l'optimisation. Lors de la première exécution du test, l'Expert Advisor créera un dossier portant le nom correspondant dans le dossier commun du terminal. Le dossier créé contiendra un fichier avec les paramètres actuels du symbole testé. Dans notre cas, il s'agit de AUDUSD.ini. Vous pouvez voir le contenu de ce fichier dans la figure ci-dessous :

Fig. 4. Liste des paramètres d'entrée dans le fichier du symbole.

Fig. 4. Liste des paramètres d'entrée dans le fichier du symbole.

Lorsque la combinaison de paramètres requise a été trouvée, vous devez définir true dans le paramètre RewriteParameters (Réécriture des paramètres) et relancer le test. Le fichier de paramètres sera mis à jour. Vous pouvez ensuite définir à nouveau false et vérifier les autres résultats des passes d'optimisation. Il est également pratique de comparer les résultats des valeurs écrites dans le fichier avec celles définies dans les paramètres d'entrée en basculant simplement entre les options du paramètre Parameter reading mode.

Ensuite, nous exécutons l'optimisation pour EURUSD, qui est le deuxième symbole de la liste du fichier de liste de symboles. Pour ce faire, nous devons définir la valeur du paramètre Numéro du symbole testé égale à 2. Suite à l'optimisation, et après avoir déterminé les paramètres et les avoir écrits dans le fichier, il faudra également faire la même chose pour le troisième symbole de la liste.

Une fois que les paramètres de tous les symboles ont été écrits dans le fichier, vous pouvez soit afficher les résultats pour chaque symbole séparément, en spécifiant le numéro de symbole, soit afficher le résultat cumulé pour tous les symboles, en définissant le Numéro du symbole testé sur 0. J'ai le résultat cumulé suivant pour tous les symboles :

Fig. 5. Le résultat cumulé de l'Expert Advisor multi-devises.

Fig. 5. Le résultat cumulé de l'Expert Advisor multi-devises.


Conclusion

En conséquence, nous avons un modèle assez pratique pour les Expert Advisors multi-devises. Il peut être développé davantage, si vous le souhaitez. L'archive téléchargeable avec les fichiers de l'Expert Advisor est jointe à l'article pour votre considération. Après avoir décompressé, placez le dossier UnlimitedParametersEA sous <MetaTrader 5 terminal folder>\MQL5\Experts. L'indicateur EventsSpy.mq5 doit être placé dans <MetaTrader 5 terminal folder>\MQL5\Indicators. En plus de cela, n'oubliez pas de créer le fichier texte TestedSymbols.txt dans le dossier commun du terminal client.

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

Fichiers joints |
eventsspy.mq5 (7.61 KB)
MQL5 Cookbook : Écriture de l'historique des transactions dans un fichier et création des graphiques d’équilibre pour chaque symbole dans Excel MQL5 Cookbook : Écriture de l'historique des transactions dans un fichier et création des graphiques d’équilibre pour chaque symbole dans Excel
Lorsque je communiquais dans divers forums, j'utilisais souvent des exemples de mes résultats de test affichés sous forme de captures d'écran de graphiques Microsoft Excel. On m'a souvent demandé d'expliquer comment de tels graphiques peuvent être créés. Enfin, j'ai maintenant un peu de temps pour tout expliquer dans cet article.
MQL5 Cookbook : Expert Advisor multi-devises - Approche simple, nette et rapide MQL5 Cookbook : Expert Advisor multi-devises - Approche simple, nette et rapide
Cet article décrira une mise en œuvre d'une approche simple adaptée à un Expert Advisor multi-devises. Cela signifie que vous pourrez configurer l'Expert Advisor pour les tests/trading dans des conditions identiques mais avec des paramètres différents pour chaque symbole. A titre d'exemple, nous allons créer un motif pour deux symboles mais de manière à pouvoir ajouter des symboles supplémentaires, si nécessaire, en apportant de petites modifications au code.
MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations
Quelle que soit la stratégie de trading que vous utilisez, il y aura toujours une question de quels paramètres choisir pour assurer des bénéfices futurs. Cet article donne un exemple d'Expert Advisor avec la possibilité d'optimiser plusieurs paramètres de symboles en même temps. Cette méthode est destinée à réduire l'effet du surajustement des paramètres et à traiter les situations où les données d'un seul symbole ne sont pas suffisantes pour l'étude.
MQL5 Cookbook : Développement d'un cadre pour un système de trading basé sur la stratégie du triple écran MQL5 Cookbook : Développement d'un cadre pour un système de trading basé sur la stratégie du triple écran
Dans cet article, nous allons développer un cadre pour un système de trading basé sur la stratégie Triple Screen dans MQL5. L’Expert Advisor ne sera pas développé à partir de zéro. Au lieu de cela, nous allons simplement modifier le programme de l’article précédent « MQL5 Cookbook : Utilisation des indicateurs pour définir les conditions de trading dans l’Expert Advisors" qui répond déjà largement à notre objectif. Ainsi, l’article montrera également comment vous pouvez facilement modifier des modèles de programmes prêts à l’emploi.