English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Manuale MQL5: Sviluppo di un Expert Advisor multi-valuta con un numero illimitato di parametri

Manuale MQL5: Sviluppo di un Expert Advisor multi-valuta con un numero illimitato di parametri

MetaTrader 5Esempi | 11 gennaio 2022, 16:47
161 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

L'Expert Advisor multi-valuta considerato nel precedente articolo "MQL5 Cookbook: Manuale MQL5: Multi-Currency Expert Advisor - Approccio semplice, ordinato e veloce", può essere molto utile se il numero di simboli e parametri della strategia di trading utilizzati è piccolo. Tuttavia, esiste una restrizione sul numero di parametri di input di un Expert Advisor in MQL5: non dovrebbero essere superiori a 1024.

E anche se questo numero sarà molto spesso sufficiente, è molto scomodo usare un elenco così enorme di parametri. Ogni volta che è richiesta una modifica o un'ottimizzazione dei parametri per un determinato simbolo, è necessario cercare i parametri per quel simbolo specifico nell'elenco dei parametri lunghi.

È possibile acquisire familiarità con ulteriori restrizioni nella sezione input della Guida del terminale client.

In questo articolo, creeremo un modello che utilizza un singolo set di parametri per l'ottimizzazione di un sistema di trading, consentendo al contempo un numero illimitato di parametri. L'elenco dei simboli verrà creato in un file di testo standard (*.txt). Anche i parametri di input per ciascun simbolo verranno memorizzati nei file.

Va detto qui che l'Expert Advisor lavorerà su un simbolo nella normale modalità operativa, ma sarai in grado di testarlo nello Strategy Tester su una varietà di simboli selezionati (su ciascun simbolo separatamente).

Sarebbe, infatti, ancora più conveniente creare l'elenco dei simboli direttamente nella finestra Market Watch, considerando che consente anche di salvare set di simboli già pronti. Potremmo anche fare in modo che l'Expert Advisor ottenga l'elenco dei simboli nella finestra Market Watch direttamente dallo Strategy Tester. Ma sfortunatamente, al momento non è possibile accedere alla finestra Market Watch dallo Strategy Tester, quindi dovremo creare manualmente l'elenco dei simboli in anticipo o utilizzando uno script.


Sviluppo dell’ Expert Advisor

L'Expert Advisor multi-valuta descritto nel precedente articolo Manuale MQL5: Multi-Currency Expert Advisor - Approccio semplice, ordinato e veloce" sarà preso come modello. Decidiamo prima i parametri di input. Come accennato in precedenza, lasceremo solo una serie di parametri. Di seguito è riportato l'elenco dei parametri di input dell'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

Solo i parametri con il modificatore di input verranno scritti in un file. Inoltre, dovremmo espandere i tre nuovi parametri che non abbiamo mai incontrato prima.

  • SymbolNumber - questo parametro indica il numero del simbolo dal file che contiene l'elenco dei simboli. Se è impostato su 0, tutti i simboli dell'elenco verranno testati. Se si va oltre l'elenco, il test non verrà eseguito.
  • RewriteParameters - se questo valore del parametro è impostato su true, il file con i parametri del simbolo specificato (numero nel parametro SymbolNumber) verrà riscritto utilizzando i valori dei parametri di input correnti. In alternativa, se è impostato su false, i parametri verranno letti da un file.
  • ParametersReadingMode - questo parametro indica la modalità di lettura rispetto ai parametri di input. Il tipo di parametro è il ENUM_INPUTS_READING_MODE enumerazione personalizzata che offre due opzioni: leggere da un file e utilizzare i parametri correnti.

Codice dell'enumerazione ENUM_INPUTS_READING_MODE:

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

Il numero di simboli è stato precedentemente determinato utilizzando la costante NUMBER_OF_SYMBOLS. Questo valore ora dipende da diverse modalità, quindi lo cambieremo nella variabile 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;

All'inizio del codice di Expert Advisor, dichiariamo un'altra costante, TESTED_PARAMETERS_COUNT, che determina le dimensioni delle matrici e il numero di iterazioni di loop durante l'iterazione sui parametri di input:

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

Il file con l'elenco dei simboli, così come la cartella dei file contenente i parametri per ciascun simbolo devono essere collocati nella cartella comune del terminale in quanto è possibile accedervi a livello di programma sia durante il normale funzionamento di Expert Advisor che durante il test.

Ora dobbiamo preparare gli array con cui lavorare in seguito. Tutti gli array nell'Expert Advisor multi-valuta dell'articolo precedente che modificheremo sono statici, cioè con una dimensione preimpostata degli elementi. Dovremmo renderli tutti dinamici poiché la dimensione dipenderà ora dal numero di simboli utilizzati nel file e dalla modalità di lettura dei parametri di input. Aggiungeremo anche nuovi array (evidenziati in giallo) necessari per lavorare con i file:

//--- 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];

Quindi dobbiamo determinare le dimensioni delle matrici e inizializzarle ai valori.

La matrice di valori dei parametri di input creata in precedenza verrà inizializzata nella funzione InitializeTestedParametersValues() che verrà creata nel file InitializeArrays.mqh. Tale matrice verrà utilizzata per scrivere i valori dei parametri di input nel file.

//+------------------------------------------------------------------+
//| 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;
  }

Ora consideriamo le operazioni sui file. Innanzitutto, crea un'altra libreria di funzioni, FileFunctions.mqh, nella cartella UnlimitedParametersEA\Include del tuo Expert Advisor. Questa libreria verrà utilizzata per creare funzioni relative alla lettura e alla scrittura di dati in un file. Includerlo nel file principale dell'Expert Advisor e in altri file del progetto.

//---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"

Iniziamo con la creazione di una funzione per la lettura dell'elenco dei simboli da un file di testo - ReadSymbolsFromFile(). Questo file (nomizziamolo TestedSymbols.txt) deve essere inserito nella sottocartella \Files della cartella comune del terminale client MetaTrader 5. Nel mio caso sarà C:\ProgramData\MetaQuotes\Terminal\Common ma è necessario controllare attentamente il percorso utilizzando lo TERMINAL_COMMONDATA_PATH costante:

TerminalInfoString(TERMINAL_COMMONDATA_PATH)
Tutti i file personalizzati devono essere inseriti nella sottocartella \Files della cartella comune del terminale client o in <Terminal Data Folder>\MQL5\Files\. nelle funzioni mqL5dei file possono solo accedere a queste cartelle.

Per verificare la funzionalità, aggiungere alcuni simboli al file di testo creato, separando ciascuno dei simboli con un'interruzione di riga ("\r\n"):

Fig. 1. Elenco dei simboli nel file dalla cartella comune del terminale.

Fig. 1. Elenco dei simboli nel file dalla cartella comune del terminale.

Inoltre, diamo un'occhiata al codice della funzione 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);
  }

Qui, il file di testo viene letto riga per riga. Il nome di ogni strumento finanziario che è stato letto è scritto nell'array temporaneo temporary_symbols[] creato in precedenza (l'effettiva accessibilità del simbolo sul server commerciale verrà verificata in seguito). Inoltre, alla fine la funzione restituisce il numero di stringhe lette, ovvero il numero di simboli su cui verrà testato il nostro Expert Advisor.

Fai sempre riferimento alla MQL5 Reference per informazioni dettagliate su funzioni e identificatori che non hai mai incontrato prima. I commenti forniti nel codice semplificheranno il processo di apprendimento.

Torniamo al file InitializeArrays.mqh in cui creeremo la funzione InitializeInputSymbols() per riempire la matrice InputSymbols[] di nomi di simboli dichiarati in precedenza. I principianti probabilmente lo troveranno piuttosto complesso, quindi ho fornito commenti dettagliati al codice:

//+------------------------------------------------------------------+
//| 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();
     }
//---
  }

Una volta che la dimensione della matrice di parametri di input è stata determinata dal numero di simboli utilizzati, è necessario impostare la dimensione di tutte le altre matrici di parametri di input. Implementiamolo come funzione separata - 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);
  }

Ora, dobbiamo creare la funzionalità che ci consentirà di leggere i valori dei parametri di input per ciascun simbolo, nonché di scrivere quei valori di parametro in un file separato (per ciascun simbolo) a seconda della modalità di input selezionata e delle impostazioni di Expert Advisor. Questo sarà fatto in modo simile a come leggiamo l'elenco dei simboli dal file. È un compito complesso, quindi spezzeremolo in diverse procedure.

Innanzitutto, dovremmo imparare a leggere i valori dei parametri di input da un file a un array. La matrice conterrà valori di tipo doppio. In seguito convertiremo alcuni di essi (periodo indicatore e flag di inversione di posizione) rispettivamente in tipi int e bool. L'handle del file aperto e la matrice in cui sono archiviati i valori dei parametri del file vengono passati alla funzione 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);
  }

Quali handle di file e array passeremo a questa funzione? Dipenderà da quali simboli lavoreremo dopo averli letti dal file "TestedSymbols.txt". Ogni simbolo corrisponderà a un determinato file di testo contenente i valori dei parametri di input. Esistono due scenari di casi da considerare:

  1. Il file esiste e leggiamo i valori dei parametri di input da tale file utilizzando la funzione ReadInputParametersValuesFromFile() descritta in precedenza.
  2. Il file non esiste o è necessario riscrivere i valori dei parametri di input esistenti.

Il formato del file di testo (un file .ini, sebbene sia possibile scegliere qualsiasi altra estensione ritenuta necessaria) contenente i valori dei parametri di input sarà semplice:

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

Combiniamo questa logica in un'unica funzione, ReadWriteInputParameters(), il cui codice è fornito di seguito:

//+------------------------------------------------------------------+
//| 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);
     }
  }

L'ultima funzione di file CreateInputParametersFolder() creerà una cartella con il nome di Expert Advisor nella cartella comune del terminale client. Questa è la cartella da cui verranno letti/scritti i file di testo (nel nostro caso, i file .ini) con i valori dei parametri di input. Proprio come nella funzione precedente, controlleremo se la cartella esiste. Se la cartella è stata creata correttamente o esiste già, la funzione restituirà il percorso o una stringa vuota in caso di errore:

//+----------------------------------------------------------------------------------+
//| 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("");
  }

Mettiamo ora insieme le chiamate di funzione sopra in una singola funzione - InitializeInputParametersArrays(). Questa funzione copre 4 opzioni di inizializzazione dei parametri di input quando si lavora con Expert Advisor:

  1. Modalità operativa standard (o ottimizzazione dei parametri per un simbolo selezionato) utilizzando i valori dei parametri di input correnti
  2. Riscrittura dei parametri nei file durante il test o l'ottimizzazione
  3. Test di un simbolo selezionato
  4. Test di tutti i simboli nell'elenco dal file

Tutte le operazioni sono spiegate in commenti dettagliati al codice:

//+-------------------------------------------------------------------+
//| 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;
     }
  }

Nelle modalità #1 e #2 usiamo la funzione InitializeWithCurrentValues(). Inizializza l'indice zero (unico) ai valori dei parametri di input correnti. In altre parole, questa funzione viene utilizzata quando è richiesto un solo simbolo:

//+------------------------------------------------------------------+
//| 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;
  }

Ora dobbiamo fare la cosa più semplice, ma la più importante: implementare chiamate consecutive delle funzioni di cui sopra dal punto di ingresso, le OnInit() funzione:

//+------------------------------------------------------------------+
//| 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();
  }

Quindi, abbiamo finito con il codice. Puoi familiarizzare con le funzioni descritte usando i file allegati all'articolo, non c'è nulla di complicato in essi. Ora, andiamo oltre per vedere cosa abbiamo ottenuto come risultato e come può essere utilizzato.


Ottimizzazione dei parametri e test expert advisor

Come già accennato, dovresti avere il file TestedSymbols.txt con l'elenco dei simboli nella cartella comune del terminale client. Come esempio/a scopo di test, creeremo un elenco di tre simboli: AUDUSD, EURUSD e NZDUSD. Ora ottimizzeremo uno dopo l’altro i parametri di input per ciascun simbolo. Il tester strategico deve essere impostato come mostrato di seguito:

Fig. 2. Impostazioni di Strategy Tester.

Fig. 2. Impostazioni di Strategy Tester.

È possibile impostare qualsiasi simbolo (nel nostro caso, EURUSD) nella scheda "Impostazioni" poiché non influisce sull'Expert Advisor. Quindi selezioniamo i parametri per l'ottimizzazione dell'Expert Advisor:

Fig. 3. Parametri di input dell'Expert Advisor.

Fig. 3. Parametri di input dell'Expert Advisor.

La figura precedente mostra che il parametro SymbolNumber (Numero del simbolo testato) è impostato su 1. Ciò significa che durante l'esecuzione dell'ottimizzazione Expert Advisor utilizzerà il primo simbolo dell'elenco dal file TestedSymbols.txt. Nel nostro caso, è AUDUSD.

Nota:a causa delle peculiarità di questo Expert Advisor (l'elenco dei simboli è impostato leggendo dal file di testo), l'ottimizzazione con agenti remoti non sarà possibile.

Cercheremo di aggirare questa restrizione in uno dei seguenti articoli di questa serie.

Dopo aver completato l'ottimizzazione, è possibile eseguire test, studiando i risultati di diversi passaggi di ottimizzazione. Se si desidera che Expert Advisor legga i parametri dal file, è necessario selezionare File nell'elenco a discesa del parametro ParametersReadingMode (modalità di lettura dei parametri). Per poter utilizzare i parametri correnti di Expert Advisor (impostati nella scheda "Impostazioni", è necessario selezionare l'opzione Parametri di input.

L'opzione Parametri di input è certamente necessaria per visualizzare i risultati dell'ottimizzazione. Quando si esegue il test per la prima volta, Expert Advisor creerà una cartella con il nome corrispondente nella cartella comune del terminale. La cartella creata conterrà un file con i parametri correnti del simbolo testato. Nel nostro caso, questo è AUDUSD.ini. Puoi vedere il contenuto di questo file nella figura seguente:

Fig. 4. Elenco dei parametri di input nel file del simbolo.

Fig. 4. Elenco dei parametri di input nel file del simbolo.

Una volta trovata la combinazione di parametri richiesta, è necessario impostaretrue nel parametro RewriteParameters (Riscrittura dei parametri) ed eseguire nuovamente il test. Il file dei parametri verrà aggiornato. In seguito è possibile impostare nuovamente false e controllare altri risultati dei passaggi di ottimizzazione. È inoltre conveniente confrontare i risultati dei valori scritti nel file con quelli impostati nei parametri di input semplicemente passando da un'opzione all'altro del parametro Modalità di lettura dei parametri.

Quindi, eseguiamo l'ottimizzazione per EURUSD, che è il secondo simbolo nell'elenco dal file dell'elenco dei simboli. Per fare ciò, dobbiamo impostare il valore del parametro del Numero del simbolo testato uguale a 2. Dopo l'ottimizzazione, e dopo aver determinato i parametri e averli scritti nel file, lo stesso dovrà essere fatto anche per il terzo simbolo della lista.

Una volta che i parametri per tutti i simboli sono stati scritti nel file, è possibile visualizzare i risultati per ciascun simbolo separatamente, specificando il numero del simbolo, oppure visualizzare il risultato cumulativo per tutti i simboli, impostando il numero del simbolo testato su 0. Ho ottenuto il seguente risultato cumulativo per tutti i simboli:

Fig. 5. Risultato cumulativo dell'Expert Advisor multivaluta.

Fig. 5. Risultato cumulativo dell'Expert Advisor multivaluta.


Conclusione

Di conseguenza, abbiamo un modello abbastanza conveniente per gli Expert Advisor multi-valuta. Può essere sviluppato ulteriormente, se lo si desidera. Allegato all'articolo è l'archivio scaricabile con i file dell'Expert Advisor per la vostra considerazione. Dopo aver decompresso, posizionare la cartella UnlimitedParametersEA nella cartella terminale <MetaTrader 5>\MQL5\Experts. L'indicatore EventsSpy.mq5 deve essere posizionato nella cartella terminale <MetaTrader 5>\MQL5\Indicators. Oltre a ciò, non dimenticare di creare il file di testo TestedSymbols.txt nella cartella comune del terminale client.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/650

File allegati |
eventsspy.mq5 (7.61 KB)
Manuale MQL5: Scrittura della cronologia delle offerte in un file e creazione di grafici di bilanciamento per ogni simbolo in Excel Manuale MQL5: Scrittura della cronologia delle offerte in un file e creazione di grafici di bilanciamento per ogni simbolo in Excel
Quando comunicavo in vari forum, usavo spesso esempi dei risultati dei miei test visualizzati come schermate di grafici di Microsoft Excel. Molte volte mi è stato chiesto di spiegare come tali grafici possono essere creati. Infine, ora ho un po 'di tempo per spiegare tutto in questo articolo.
Manuale MQL5: Expert Advisor multivaluta: approccio semplice, accurato e rapido Manuale MQL5: Expert Advisor multivaluta: approccio semplice, accurato e rapido
Questo articolo descriverà l'implementazione di un approccio semplice, adatto a un Expert Advisor multivaluta. Ciò significa che sarai in grado di impostare l'Expert Advisor per testare/tradare in condizioni identiche ma con parametri diversi per ogni simbolo. Ad esempio creeremo un pattern per due simboli ma in modo tale da poter aggiungere ulteriori simboli, se necessario, apportando piccole modifiche al codice.
Manuale MQL5: Ridurre l'effetto dell'overfitting e gestire la mancanza di preventivi Manuale MQL5: Ridurre l'effetto dell'overfitting e gestire la mancanza di preventivi
Qualunque sia la strategia di trading che utilizzi, rimarrà sempre la domanda: “quali parametri scegliere per garantire profitti futuri?” Questo articolo fornisce un esempio di Expert Advisor con la possibilità di ottimizzare più parametri di simboli contemporaneamente. Questo metodo ha lo scopo di ridurre l'effetto dei parametri di overfitting e gestire situazioni in cui i dati di un singolo simbolo non sono sufficienti per lo studio.
Manuale MQL5: Sviluppo di un framework per un sistema di trading basato sulla strategia a triplo schermo Manuale MQL5: Sviluppo di un framework per un sistema di trading basato sulla strategia a triplo schermo
In questo articolo, svilupperemo un framework per un sistema di trading basato sulla strategia Triple Screen in MQL5. L'Expert Advisor non sarà sviluppato da zero. Invece, modificheremo semplicemente il programma dal precedente articolo "Manuale MQL5: Utilizzo di indicatori per impostare le condizioni di trading in Expert Advisors" che già sostanzialmente serve al nostro scopo. Quindi l'articolo dimostrerà anche come è possibile modificare facilmente i modelli di programmi già pronti.