English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
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

MetaTrader 5Esempi | 11 gennaio 2022, 16:50
313 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

Credo che molti trader siano perplessi sui parametri ottimali per i loro sistemi di trading. In effetti, un algoritmo di trading da solo non è sufficiente. Bisogna vedere come può essere utilizzato. Qualunque sia la strategia di trading che utilizzi, che sia semplice o complessa, con uno o più strumenti, non puoi evitare di chiederti quali parametri scegliere per garantire profitti futuri.

Tendiamo a controllare i sistemi di trading con parametri che hanno mostrato buoni risultati nel periodo di ottimizzazione (backtesting) e nel periodo successivo (forward testing). Il forward testing non è infatti così necessario. I risultati rilevanti possono essere ottenuti utilizzando dati storici.

Questo metodo dà origine ad una domanda a cui non è possibile dare una risposta definitiva: quale quantità di dati storici dovrebbe essere utilizzata per ottimizzare un sistema di trading? Il fatto è che ci sono molte opzioni. Tutto dipende dalla gamma di fluttuazioni dei prezzi su cui ci si aspetta di capitalizzare.

Tornando alla quantità di cronologia richiesta per l'ottimizzazione, concludiamo che i dati disponibili potrebbero essere sufficienti per il trading intra-hour. Questo non è sempre vero per intervalli di tempo più lunghi. Maggiore è il numero di ripetizioni di un modello coerente, cioè più scambi abbiamo, più veritiere sono le prestazioni del sistema di trading testato che possiamo aspettarci di vedere in futuro.

Cosa succede se i dati sui prezzi di un determinato strumento non sono sufficienti per ottenere un numero sufficiente di ripetizioni e sentirsi più sicuri? Risposta: utilizzare i dati di tutti gli strumenti disponibili.


Esempio in NeuroShell DayTrader Professional

Prima di procedere alla programmazione in MetaTrader 5, esaminiamo un esempio in NeuroShell DayTrader Professional. Offre grandi funzionalità per l'ottimizzazione dei parametri di un sistema di trading (compilato con un costruttore) per più simboli. È possibile impostare i parametri richiesti nelle impostazioni del modulo di trading, ottimizzare i parametri per ciascun simbolo separatamente o trovare un set di parametri ottimale per tutti i simboli contemporaneamente. Questa opzione è disponibile nella scheda Ottimizzazione:

La scheda Ottimizzazione nel modulo di trading di NeuroShell DayTrader Professional

Fig. 1. La scheda Ottimizzazione nel modulo di trading di NeuroShell DayTrader Professional

Nel nostro caso, possiamo usare qualsiasi semplice sistema di trading in quanto abbiamo solo bisogno di confrontare i risultati di due metodi di ottimizzazione, quindi la scelta del sistema è attualmente di poca importanza.

Puoi trovare informazioni su come compilare strategie di trading in NeuroShell DayTrader Professional in altri articoli del mio blog (puoi cercare o utilizzare i tag per individuare le informazioni pertinenti). Ti consiglio anche di leggere un articolo intitolato "Come preparare le quotazioni MetaTrader 5 per altre applicazioni" che descrive e dimostra come utilizzando uno script sia possibile scaricare citazioni da MetaTrader 5 nel formato compatibile con NeuroShell DayTrader Professional.

Per fare questo test, ho preparato i dati ottenuti dalle barre giornaliere per otto simboli dall'anno 2000 fino a gennaio 2013:

Elenco dei simboli per un test in NeuroShell DayTrader Professional

Fig. 2. Elenco dei simboli per un test in NeuroShell DayTrader Professional

La figura seguente mostra due risultati di ottimizzazione. La parte superiore visualizza il risultato dell'ottimizzazione in cui ogni simbolo ottiene i propri parametri, mentre la parte inferiore mostra il risultato in cui i parametri sono comuni per tutti i simboli.

Confronto dei risultati di due modalità di ottimizzazione dei parametri

Fig. 3. Confronto dei risultati di due modalità di ottimizzazione dei parametri

Il risultato che mostra i parametri comuni non sembra buono come quello in cui i parametri sono diversi per ogni simbolo. Eppure ispira più fiducia in quanto il sistema di trading passa attraverso una serie di vari modelli di comportamento dei prezzi (volatilità, numero di tendenze / piatti) con gli stessi parametri per tutti i simboli.

Continuando sullo stesso argomento, possiamo logicamente trovare un altro argomento a favore dell'ottimizzazione utilizzando una maggiore quantità di dati. Può darsi che il comportamento dei prezzi di una certa coppia di valute, ad esempio EURUSD, sarà molto diverso in seguito (tra due, cinque o dieci anni). Ad esempio, le tendenze dei prezzi GBPUSD saranno simili al comportamento dei prezzi passato di EURUSD e viceversa. Dovresti essere pronto per questo, poiché questo è vero per qualsiasi strumento.


Un esempio in MetaTrader 5

Vediamo ora quali modalità di ottimizzazione dei parametri sono offerte in MetaTrader 5. Di seguito puoi vedere tutti i simboli selezionati nella modalità di ottimizzazione di Market Watch contrassegnati con una freccia nell'elenco a tendina delle modalità di ottimizzazione.

Fig. 4. Modalità di ottimizzazione nel MetaTrader 5 Strategy Tester

Fig. 4. Modalità di ottimizzazione nel MetaTrader 5 Strategy Tester

Questa modalità consente di testare un EA solo con i parametri correnti su ciascun simbolo uno per uno. I simboli utilizzati nei test sono quelli attualmente selezionati nella finestra Market Watch. In altre parole, l'ottimizzazione dei parametri non viene eseguita in questo caso. Tuttavia, MetaTrader 5 e MQL5 ti consentono di implementare questa idea da solo.

Ora, dobbiamo vedere come implementare un tale EA. L'elenco dei simboli verrà fornito in un file di testo (*.txt). Inoltre, implementeremo la possibilità di memorizzare diversi set di elenchi di simboli. Ogni set sarà in una sezione separata con la propria intestazione con un numero di sezione. I numeri sono necessari per facilitare il controllo visivo.

Si noti che è importante avere # davanti al numero in modo da consentire all'Expert Advisor di ottenere il set di dati giusto quando si riempie l'array di simboli. Generalmente, l'intestazione può contenere simboli ma deve avere sempre #. Il segno numerico può essere sostituito con qualsiasi altro simbolo in base al quale l'Expert Advisor determinerà/conterà le sezioni. In tal caso la sostituzione dovrà riflettersi nel codice.

Di seguito puoi vedere il file SymbolsList.txt che contiene tre set di simboli per il test. Questo file, come mostrato, verrà ulteriormente utilizzato durante il test del metodo.

Fig. 5. Diversi set di simboli forniti in un file di testo per il test

Fig. 5. Diversi set di simboli forniti in un file di testo per il test

Nei parametri esterni, aggiungeremo un altro parametro, SectionOfSymbolList, per indicare l'insieme di simboli che l'Expert Advisor dovrebbe utilizzare nel test corrente. Questo parametro assume il valore (da zero in su) che definisce il set di simboli. Se il valore supera il numero di set disponibili, Expert Advisor scriverà una voce corrispondente nel registro e il test verrà eseguito solo sul simbolo corrente.

SymbolsList.txt deve trovarsi nella directory del terminale locale in Metatrader 5\MQL5\Files. Può anche essere inserito nella cartella comune ma in questo caso non sarà disponibile per l'ottimizzazione dei MQL5 Cloud Network. Inoltre, per consentire l'accesso al file e ai relativi indicatori personalizzati per il test, è necessario scrivere le seguenti righe all'inizio del file:

//--- Allow access to the external file and indicator for optimization in the cloud
#property tester_file      "SymbolsList.txt"
#property tester_indicator "EventsSpy.ex5"

Il nostro Expert Advisor si baserà sull'Expert Advisor multi-valuta già pronto presentato nell'articolo "Manuale MQL5: Sviluppo di un Consulente Esperto Multi-Valuta con Numero Illimitato di Parametri". La sua strategia di trading sottostante è abbastanza semplice ma sarà sufficiente per testare l'efficienza del metodo. Rimuoveremo solo le parti non necessarie, aggiungeremo ciò di cui abbiamo bisogno e correggeremo il codice pertinente esistente. Miglioreremo sicuramente il nostro Expert Advisor con la funzione di salvataggio dei report ampiamente descritta nel precedente articolo della serie "MQL5 Cookbook: Scrivere la cronologia delle offerte in un file e creare grafici di equilibrio per ogni simbolo in Excel". Saranno inoltre richiesti grafici di bilanciamento per tutti i simboli per valutare l'efficienza del metodo in esame.

I parametri esterni dell'Expert Advisor devono essere modificati come segue:

//--- External parameters of the Expert Advisor
sinput int    SectionOfSymbolList = 1;     // Section number in the symbol lists
sinput bool   UpdateReport        = false; // Report update
sinput string delimeter_00="";   // --------------------------------
sinput long   MagicNumber         = 777;   // Magic number
sinput int    Deviation           = 10;    // Slippage
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

Tutte le matrici associate ai parametri esterni devono essere eliminate in quanto non saranno necessarie e devono essere ulteriormente sostituite dalle variabili esterne in tutto il codice. Dovremmo lasciare solo la matrice dinamica di simboli, InputSymbols[], la cui dimensione dipenderà dal numero di simboli utilizzati da uno degli insiemi nel file SymbolsList.txt. Se l'Expert Advisor viene utilizzato al di fuori dello Strategy Tester, la dimensione di tale matrice sarà pari a 1 poiché in modalità in tempo reale Expert Advisor funzionerà con un solo simbolo.

Le modifiche corrispondenti devono essere apportate anche nel file di inizializzazione dell'array - InitializeArrays.mqh. Cioè, tutte le funzioni responsabili dell'inizializzazione delle matrici di variabili esterne dovrebbero essere eliminate. La funzione InitializeArraySymbols() ha ora l'aspetto illustrato di seguito:

//+------------------------------------------------------------------+
//| Filling the array of symbols                                     |
//+------------------------------------------------------------------+
void InitializeArraySymbols()
  {
   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
//--- Test mode message
   string message_01="<--- All symbol names in the <- SymbolsList.txt -> file are incorrect ... --->\n"
                     "<--- ... or the value of the \"Section of List Symbols\" parameter is greater, "
                     "than the number of file sections! --->\n"
                     "<--- Therefore we will test only the current symbol. --->";
//--- Real-time mode message
   string message_02="<--- In real-time mode, we only work with the current symbol. --->";
//--- If in real-time mode
   if(!IsRealtime())
     {
      //--- Get the number of strings from the specified symbol set in the file and fill the temporary array of symbols
      strings_count=ReadSymbolsFromFile("SymbolsList.txt");
      //--- Iterate over all symbols from the specified set
      for(int s=0; s<strings_count; s++)
        {
         //--- If the correct string is returned following the symbol check
         if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="")
           {
            //--- increase the counter
            SYMBOLS_COUNT++;
            //--- set/increase the array size
            ArrayResize(InputSymbols,SYMBOLS_COUNT);
            //--- index with the symbol name
            InputSymbols[SYMBOLS_COUNT-1]=checked_symbol;
           }
        }
     }
//--- If all symbol names were not input correctly or if currently working in real-time mode
   if(SYMBOLS_COUNT==0)
     {
      //--- Real-time mode message
      if(IsRealtime())
         Print(message_02);
      //--- Test mode message
      if(!IsRealtime())
         Print(message_01);
      //--- We will work with the current symbol only
      SYMBOLS_COUNT=1;
      //--- set the array size and
      ArrayResize(InputSymbols,SYMBOLS_COUNT);
      //--- index with the current symbol name
      InputSymbols[0]=_Symbol;
     }
  }

Anche il codice della funzione ReadSymbolsFromFile() deve essere modificato. Prima leggeva l'intero elenco dei simboli, mentre ora vogliamo che legga solo il set di simboli specificato. Di seguito è riportato il codice funzione modificato:

//+------------------------------------------------------------------+
//| Returning the number of strings (symbols) from the specified     |
//| set in the file and filling the temporary array of symbols       |
//+------------------------------------------------------------------+
//--- When preparing the file, symbols in the list should be separated with a line break
int ReadSymbolsFromFile(string file_name)
  {
   ulong  offset         =0;   // Offset for determining the position of the file pointer
   string delimeter      ="#"; // Identifier of the section start
   string read_line      ="";  // For the check of the read string
   int    limit_count    =0;   // Counter limiting the number of the possibly open charts
   int    strings_count  =0;   // String counter
   int    sections_count =-1;  // Section counter
   
//--- Message 01
   string message_01="<--- The <- "+file_name+" -> file has not been prepared appropriately! --->\n"
                     "<--- The first string does not contain the section number identifier ("+delimeter+")! --->";
//--- Message 02
   string message_02="<--- The <- "+file_name+" -> file has not been prepared appropriately! --->\n"
                     "<--- There is no line break identifier in the last string, --->\n"
                     "<--- so only the current symbol will be involved in testing. --->";
//--- Message 03
   string message_03="<--- The <- "+file_name+" -> file could not be found! --->"
                     "<--- Only the current symbol will be involved in testing. --->";
                     
//--- Open the file (get the handle) for reading in the local directory of the terminal
   int file_handle=FileOpen(file_name,FILE_READ|FILE_ANSI,'\n');
//--- If the file handle has been obtained
   if(file_handle!=INVALID_HANDLE)
     {
      //--- 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
            read_line=FileReadString(file_handle);
            //--- If the section number identifier has been found
            if(StringFind(read_line,delimeter,0)>-1)
               //--- Increase the section counter
               sections_count++;
            //--- If the section has been read, exit the function
            if(sections_count>SectionOfSymbolList)
              {
               FileClose(file_handle); // Close the file
               return(strings_count);  // Return the number of strings in the file
              }
            //--- If this is the first iteration and the first string does not contain the section number identifier
            if(limit_count==0 && sections_count==-1)
              {
               PrepareArrayForOneSymbol(strings_count,message_01);
               //--- Close the file
               FileClose(file_handle);
               //--- Return the number of strings in the file
               return(strings_count);
              }
            //--- Increase the counter limiting the number of the possibly open charts
            limit_count++;
            //--- If the limit has been reached
            if(limit_count>=CHARTS_MAX)
              {
               PrepareArrayForOneSymbol(strings_count,message_02);
               //--- Close the file
               FileClose(file_handle);
               //--- Return the number of strings in the file
               return(strings_count);
              }
            //--- Get the position of the pointer
            offset=FileTell(file_handle);
            //--- If this is the end of the string
            if(FileIsLineEnding(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 we are not in the specified section of the file, exit the loop
               if(sections_count!=SectionOfSymbolList)
                  break;
               //--- Otherwise,
               else
                 {
                  //--- if the string is not empty
                  if(read_line!="")
                    {
                     //--- increase the string counter
                     strings_count++;
                     //--- increase the size of the array of strings,
                     ArrayResize(temporary_symbols,strings_count);
                     //--- write the string to the current index
                     temporary_symbols[strings_count-1]=read_line;
                    }
                 }
               //--- Exit the loop
               break;
              }
           }
         //--- If this is the end of the file, terminate the entire loop
         if(FileIsEnding(file_handle))
            break;
        }
      //--- Close the file
      FileClose(file_handle);
     }
   else
      PrepareArrayForOneSymbol(strings_count,message_03);
//--- Return the number of strings in the file
   return(strings_count);
  }

Puoi vedere che alcune stringhe nel codice sopra sono evidenziate. Tali stringhe contengono la funzione PrepareArrayForOneSymbol() che prepara semplicemente una matrice per un simbolo (corrente) in caso di errore.

//+------------------------------------------------------------------+
//| Preparing an array for one symbol                                |
//+------------------------------------------------------------------+
void PrepareArrayForOneSymbol(int &strings_count,string message)
  {
//--- Print the message to the log
   Print(message);
//--- Array size
   strings_count=1;
//--- Set the size of the array of symbols
   ArrayResize(temporary_symbols,strings_count);
//--- Write the string with the current symbol name to the current index
   temporary_symbols[0]=_Symbol;
  }

Ora tutto è pronto per testare il metodo di ottimizzazione dei parametri. Ma prima di procedere al test, aggiungiamo un'altra serie di dati al report. In precedenza, oltre ai saldi di tutti i simboli, il file di report conteneva tutti i prelievi dai massimi locali espressi in percentuale. Ora la relazione coprirà anche tutti i prelievi in termini monetari. Allo stesso tempo, modificheremo la funzione CreateSymbolBalanceReport() in cui viene generato il report.

Il codice della funzione CreateSymbolBalanceReport() è fornito di seguito:

//+------------------------------------------------------------------+
//| Creating test report on deals in .csv format                     |
//+------------------------------------------------------------------+
void CreateSymbolBalanceReport()
  {
   int    file_handle =INVALID_HANDLE; // File handle
   string path        ="";             // File path

//--- If an error occurred when creating/getting the folder, exit
   if((path=CreateInputParametersFolder())=="")
      return;
//--- Create a file to write data in the common folder of the terminal
   file_handle=FileOpen(path+"\\LastTest.csv",FILE_CSV|FILE_WRITE|FILE_ANSI|FILE_COMMON);
//--- If the handle is valid (file created/opened)
   if(file_handle>0)
     {
      int           digits           =0;   // Number of decimal places in the price
      int           deals_total      =0;   // Number of deals in the specified history
      ulong         ticket           =0;   // Deal ticket
      double        drawdown_max     =0.0; // Drawdown
      double        balance          =0.0; // Balance
      string        delimeter        =","; // Delimiter
      string        string_to_write  ="";  // To generate the string for writing
      static double percent_drawdown =0.0; // Drawdown expressed as percentage
      static double money_drawdown   =0.0; // Drawdown in monetary terms

      //--- Generate the header string
      string headers="TIME,SYMBOL,DEAL TYPE,ENTRY TYPE,VOLUME,"
                     "PRICE,SWAP($),PROFIT($),DRAWDOWN(%),DRAWDOWN($),BALANCE";
      //--- If more than one symbol is involved, modify the header string
      if(SYMBOLS_COUNT>1)
        {
         for(int s=0; s<SYMBOLS_COUNT; s++)
            StringAdd(headers,","+InputSymbols[s]);
        }
      //--- Write the report headers
      FileWrite(file_handle,headers);
      //--- Get the complete history
      HistorySelect(0,TimeCurrent());
      //--- Get the number of deals
      deals_total=HistoryDealsTotal();
      //--- Resize the array of balances according to the number of symbols
      ArrayResize(symbol_balance,SYMBOLS_COUNT);
      //--- Resize the array of deals for each symbol
      for(int s=0; s<SYMBOLS_COUNT; s++)
         ArrayResize(symbol_balance[s].balance,deals_total);
      //--- Iterate in a loop and write the data
      for(int i=0; i<deals_total; i++)
        {
         //--- Get the deal ticket
         ticket=HistoryDealGetTicket(i);
         //--- Get all the deal properties
         GetHistoryDealProperties(ticket,D_ALL);
         //--- Get the number of digits in the price
         digits=(int)SymbolInfoInteger(deal.symbol,SYMBOL_DIGITS);
         //--- Calculate the overall balance
         balance+=deal.profit+deal.swap+deal.commission;
         //--- Calculate the max drawdown from the local maximum
         TesterDrawdownMaximum(i,balance,percent_drawdown,money_drawdown);
         //--- Generate a string for writing using concatenation
         StringConcatenate(string_to_write,
                           deal.time,delimeter,
                           DealSymbolToString(deal.symbol),delimeter,
                           DealTypeToString(deal.type),delimeter,
                           DealEntryToString(deal.entry),delimeter,
                           DealVolumeToString(deal.volume),delimeter,
                           DealPriceToString(deal.price,digits),delimeter,
                           DealSwapToString(deal.swap),delimeter,
                           DealProfitToString(deal.symbol,deal.profit),delimeter,
                           DrawdownToString(percent_drawdown),delimeter,
                           DrawdownToString(money_drawdown),delimeter,
                           DoubleToString(balance,2));
         //--- If more than one symbol is involved, write their balance values
         if(SYMBOLS_COUNT>1)
           {
            //--- Iterate over all symbols
            for(int s=0; s<SYMBOLS_COUNT; s++)
              {
               //--- If the symbols are equal and the deal result is non-zero
               if(deal.symbol==InputSymbols[s] && deal.profit!=0)
                 {
                  //--- Display the deal in the balance for the corresponding symbol
                  //    Take into consideration swap and commission
                  symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1]+
                                               deal.profit+
                                               deal.swap+
                                               deal.commission;
                  //--- Add to the string
                  StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2));
                 }
               //--- Otherwise write the previous value
               else
                 {
                  //--- If the deal type is "Balance" (the first deal)
                  if(deal.type==DEAL_TYPE_BALANCE)
                    {
                     //--- the balance is the same for all symbols
                     symbol_balance[s].balance[i]=balance;
                     StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2));
                    }
                  //--- Otherwise write the previous value to the current index
                  else
                    {
                     symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1];
                     StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2));
                    }
                 }
              }
           }
         //--- Write the generated string
         FileWrite(file_handle,string_to_write);
         //--- Mandatory zeroing out of the variable for the next string
         string_to_write="";
        }
      //--- Close the file
      FileClose(file_handle);
     }
//--- If the file could not be created/opened, print the appropriate message
   else
      Print("Error creating the file! Error: "+IntegerToString(GetLastError())+"");
  }

Abbiamo calcolato i drawdown nella funzione DrawdownMaximumToString(). Questa installazione viene ora eseguita dalla funzione TesterDrawdownMaximum(), mentre il valore drawdown viene convertito in una stringa utilizzando la funzione DrawdownToString() di base.

Il codice della funzione TesterDrawdownMaximum() è il seguente:

//+------------------------------------------------------------------+
//| Returning the max drawdown from the local maximum                |
//+------------------------------------------------------------------+
void TesterDrawdownMaximum(int deal_number,
                           double balance,
                           double &percent_drawdown,
                           double &money_drawdown)
  {
   ulong         ticket =0;   // Deal ticket
   string        str    ="";  // The string to be displayed in the report
//--- To calculate the local maximum and drawdown
   static double max    =0.0;
   static double min    =0.0;
   
//--- If this is the first deal
   if(deal_number==0)
     {
      //--- There is no drawdown yet
      percent_drawdown =0.0;
      money_drawdown   =0.0;
      //--- Set the initial point as the local maximum
      max=balance;
      min=balance;
     }
   else
     {
      //--- If the current balance is greater than in the memory, then...
      if(balance>max)
        {
         //--- Calculate the drawdown using the previous values:
         //    in monetary terms
         money_drawdown=max-min;
         //    expressed as percentage
         percent_drawdown=100-((min/max)*100);
         //--- Update the local maximum
         max=balance;
         min=balance;
        }
      //--- Otherwise
      else
        {
         //--- Return zero value of the drawdown
         money_drawdown=0.0;
         percent_drawdown=0.0;
         //--- Update the minimum
         min=fmin(min,balance);
         //--- If the deal ticket by its position in the list has been obtained, then...
         if((ticket=HistoryDealGetTicket(deal_number))>0)
           {
            //--- ...get the deal comment
            GetHistoryDealProperties(ticket,D_COMMENT);
            //--- Flag of the last deal
            static bool last_deal=false;
            //--- The last deal in the test can be identified by the "end of test" comment
            if(deal.comment=="end of test" && !last_deal)
              {
               //--- Set the flag
               last_deal=true;
               //--- Update the drawdown values:
               //    in monetary terms
               money_drawdown=max-min;
               //    expressed as percentage
               percent_drawdown+=100-((min/max)*100);
              }
           }
        }
     }
  }

Il codice della funzione DrawdownToString() è fornito di seguito:

//+------------------------------------------------------------------+
//| Converting drawdown to a string                                  |
//+------------------------------------------------------------------+
string DrawdownToString(double drawdown)
  {
   return((drawdown<=0) ? "" : DoubleToString(drawdown,2));
  }

Ora tutto è pronto e pronto per il test dell'Expert Advisor e l'analisi dei risultati. All'inizio dell'articolo, abbiamo visto un esempio del file già pronto. Facciamo come segue: ottimizzare i parametri per i simboli nel secondo set (ci sono tre simboli: EURUSD, AUDUSD e USDCHF) e in seguito all'ottimizzazione eseguire il test utilizzando tutti i simboli del terzo set (sette simboli in totale) per vedere i risultati per i simboli i cui dati non sono stati coinvolti nell'ottimizzazione dei parametri.


Ottimizzazione dei parametri e test expert advisor

Il tester strategico deve essere impostato come mostrato di seguito:

Fig. 6. Impostazioni di Strategy Tester per l'ottimizzazione

Fig. 6. Impostazioni di Strategy Tester per l'ottimizzazione

Le impostazioni di Expert Advisor per l'ottimizzazione dei parametri sono fornite di seguito:

Fig. 7. Impostazioni di Expert Advisor per l'ottimizzazione dei parametri

Fig. 7. Impostazioni di Expert Advisor per l'ottimizzazione dei parametri

Poiché l'ottimizzazione coinvolge tre simboli e l'aumento del volume della posizione è abilitato per ciascuno di essi, impostiamo il lotto minimo allo scopo di aprire una posizione e aumentare il volume della posizione. Nel nostro caso, il valore è 0.01.

Dopo l'ottimizzazione, selezioniamo il risultato migliore in funzione del fattore di recupero massimo e impostiamo il parametro VolumeIncrease su 0,1 per il lotto. Il risultato è mostrato di seguito:

Fig. 8. Il risultato del test in MetaTrader 5

Fig. 8. Il risultato del test in MetaTrader 5

Di seguito, è possibile visualizzare il risultato come mostrato in Excel 2010:

Il risultato del test per tre simboli come mostrato in Excel 2010

Fig. 9. Risultato del test per tre simboli come illustrato in Excel 2010

Il drawdown in termini monetari viene visualizzato come segni verdi nel grafico inferiore in termini di seconda scala (ausiliaria).

È inoltre necessario essere a conoscenza dei limiti dei grafici in Excel 2010 (l'elenco completo delle specifiche e dei limiti è disponibile nella pagina specifiche e limiti di Excel del sito Web Microsoft Office).

Fig. 10. Tabelle delle specifiche e dei limiti in Excel 2010

Fig. 10. Tabelle delle specifiche e dei limiti in Excel 2010

La tabella mostra che possiamo eseguire il test per 255 simboli contemporaneamente e visualizzare tutti i risultati nel grafico! Siamo limitati solo dalle risorse del computer.

Eseguiamo ora il test per sette simboli del terzo set con i parametri correnti e controlliamo il risultato:

Il risultato del test per sette simboli come mostrato in Excel 2010

Fig. 11. Risultato del test per sette simboli come illustrato in Excel 2010

Con sette simboli in esame, abbiamo 6901 offerte. I dati nel grafico vengono aggiornati abbastanza rapidamente in Excel 2010.


Conclusione

Credo che il metodo introdotto sia degno di nota per il fatto che anche una semplice strategia di trading come quella che abbiamo usato ha mostrato buoni risultati. Qui, dovremmo tenere a mente che l'ottimizzazione è stata eseguita solo per tre simboli su sette. Possiamo provare a migliorare il risultato ottimizzando i parametri per tutti i simboli contemporaneamente. Tuttavia, prima di tutto, dovremmo mirare a migliorare il sistema di trading, o meglio ancora, ad avere un portafoglio di vari sistemi di trading. Torneremo su questa idea più tardi.

Questo è tutto. Abbiamo uno strumento abbastanza utile per studiare i risultati delle strategie di trading multi-valuta. Di seguito è riportato il file zip scaricabile con i file di Expert Advisor per la vostra considerazione.

Dopo aver estratto i file, inserire la cartella ReduceOverfittingEA nella directory MetaTrader 5\MQL5\Experts. Inoltre, l'indicatore EventsSpy.mq5 deve essere inserito in MetaTrader 5\MQL5\Indicators. SymbolsList.txt deve trovarsi in MetaTrader 5\MQL5\Files.

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

File allegati |
symbolslist.txt (0.1 KB)
eventsspy__1.mq5 (7.59 KB)
Debug dei programmi MQL5 Debug dei programmi MQL5
Questo articolo è destinato principalmente ai programmatori che hanno già imparato il linguaggio ma non padroneggiano completamente lo sviluppo del programma. Rivela alcune tecniche di debug e presenta un'esperienza combinata dell'autore e di molti altri programmatori.
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.
Indicatore per grafici a point and figure Indicatore per grafici a point and figure
Esistono molti tipi di grafici che forniscono informazioni sulla situazione attuale del mercato. Molti di loro, come il grafico Point e Figure, sono l'eredità del passato remoto. L'articolo descrive un esempio di grafici a point and figure utilizzando un indicatore in tempo reale.
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
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. In questo modo saremo in grado di aggirare la restrizione del terminale sul numero di parametri di input di un Expert Advisor.