English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Manuale MQL5: Analisi delle proprietà di posizione nel tester di strategia MetaTrader 5

Manuale MQL5: Analisi delle proprietà di posizione nel tester di strategia MetaTrader 5

MetaTrader 5Esempi | 11 gennaio 2022, 16:42
130 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

In questo articolo, modificheremo l'Expert Advisor creato nel precedente articolo "Manuale MQL5: Proprietà posizione nel pannello Informazioni personalizzate" e risolvere i seguenti problemi:

  • Verifica della presenza di nuovi eventi della barra sul simbolo corrente;
  • Ottenere dati dalle barre;
  • Inclusione di una classe commerciale della Libreria standard in un file;
  • Creazione di una funzione per la ricerca di segnali di trading;
  • Creazione di una funzione per l'esecuzione di operazioni di trading;
  • Determinazione degli eventi commerciali nella funzione OnTrade().

In effetti, ognuna delle questioni di cui sopra potrebbe meritare un articolo a sé stante, ma a mio parere un tale approccio complicherebbe solo lo studio del linguaggio.

Userò esempi molto semplici per mostrarti come queste funzionalità possono essere implementate. In altre parole, l'implementazione di ciascuna delle attività sopra elencate si inserirà letteralmente in una funzione semplice e diretta. Quando sviluppiamo una certa idea nei futuri articoli della serie, renderemo gradualmente queste funzioni più complesse, se necessario e nella misura richiesta dal compito a portata di mano.

Per prima cosa, copiamo l'Expert Advisor dall'articolo precedente in quanto avremo bisogno di tutte le sue funzioni.


Sviluppo di un Expert Advisor

Iniziamo con l'inclusione della della classe CTrade dalla libreria standard al nostro file. Questa classe ha tutte le funzioni necessarie per l'esecuzione di operazioni di trading. Per cominciare, possiamo facilmente usarli, senza nemmeno guardarsi dentro, che è quello che faremo.

Per includere la classe, dobbiamo scrivere quanto segue:

//--- Include a class of the Standard Library
#include <Trade/Trade.mqh>

È possibile inserire questo codice all'inizio del file per poterlo trovare facilmente in seguito, ad esempio dopo la direttiva #define. Il comando #include indica che il Trade.Il file mqh deve essere preso dalla directory del terminale <MetaTrader 5>\MQL5\Include\Trade\. Lo stesso approccio può essere utilizzato per includere qualsiasi altro file che contiene funzioni. Ciò è particolarmente utile quando la quantità di codice del progetto diventa più grande e diventa difficile da navigare.

Ora, abbiamo bisogno di creare un'istanza della classe per ottenere l'accesso a tutte le sue funzioni. Questo può essere fatto scrivendo il nome dell'istanza dopo il nome della classe:

//--- Load the class
CTrade trade;

In questa versione di Expert Advisor, utilizzeremo solo una funzione commerciale tra tutte le funzioni disponibili nella classe CTrade. È la funzione PositionOpen() che viene utilizzata per aprire una posizione. Può anche essere utilizzato per l'inversione di una posizione aperta esistente. Come questa funzione può essere chiamata dalla classe verrà mostrato più avanti in questo articolo quando si crea una funzione responsabile dell'esecuzione delle operazioni di trading.

Inoltre, aggiungiamo due array dinamici nell'ambito globale. Queste matrici assumeranno valori di barra.

//--- Price data arrays
double               close_price[]; // Close (closing prices of the bar)
double               open_price[];  // Open (opening prices of the bar)

Quindi, crea una funzione CheckNewBar() utilizzando la quale il programma controllerà la ricerca di nuovi eventi bar poiché le operazioni di trading verranno eseguite solo su barre completate.

Di seguito è riportato il codice della funzione CheckNewBar() con commenti dettagliati:

//+------------------------------------------------------------------+
//| CHECKING FOR THE NEW BAR                                         |
//+------------------------------------------------------------------+
bool CheckNewBar()
  {
//--- Variable for storing the opening time of the current bar
   static datetime new_bar=NULL;
//--- Array for getting the opening time of the current bar
   static datetime time_last_bar[1]={0};
//--- Get the opening time of the current bar
//    If an error occurred when getting the time, print the relevant message
   if(CopyTime(_Symbol,Period(),0,1,time_last_bar)==-1)
     { Print(__FUNCTION__,": Error copying the opening time of the bar: "+IntegerToString(GetLastError())+""); }
//--- If this is a first function call
   if(new_bar==NULL)
     {
      // Set the time
      new_bar=time_last_bar[0];
      Print(__FUNCTION__,": Initialization ["+_Symbol+"][TF: "+TimeframeToString(Period())+"]["
            +TimeToString(time_last_bar[0],TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"]");
      return(false); // Return false and exit 
     }
//--- If the time is different
   if(new_bar!=time_last_bar[0])
     {
      new_bar=time_last_bar[0]; // Set the time and exit 
      return(true); // Store the time and return true
     }
//--- If we have reached this line, then the bar is not new, return false
   return(false);
  }

Come si può vedere nel codice precedente, la funzione CheckNewBar() restituisce true se la barra è nuova o false se non è ancora presente una nuova barra. In questo modo puoi controllare la situazione durante il trading / test, eseguendo solo operazioni di trading su barre completate.

All'inizio della funzione, dichiariamo una variabile statica e una matrice statica del tipo datetime. Le variabili locali statiche mantengono i loro valori anche dopo l'uscita della funzione. Ad ogni successiva chiamata di funzione, tali variabili locali conterranno i valori che hanno preso alla chiamata precedente della funzione.

Inoltre, si prega di notare la funzione CopyTime(). Ci aiuta a ottenere l'ora dell'ultima barra nell'array time_last_bar. Assicurati di controllare la sintassi della funzione in Riferimento MQL5.

Si può anche notare la funzione TimeframeToString() definita dall'utente che non è mai stata menzionata in questa serie di articoli prima. Converte i valori dell'intervallo di tempo in una stringa chiara per l'utente:

string TimeframeToString(ENUM_TIMEFRAMES timeframe)
  {
   string str="";
   //--- If the passed value is incorrect, take the time frame of the current chart
   if(timeframe==WRONG_VALUE || timeframe == NULL)
      timeframe = Period();
   switch(timeframe)
     {
      case PERIOD_M1  : str="M1";  break;
      case PERIOD_M2  : str="M2";  break;
      case PERIOD_M3  : str="M3";  break;
      case PERIOD_M4  : str="M4";  break;
      case PERIOD_M5  : str="M5";  break;
      case PERIOD_M6  : str="M6";  break;
      case PERIOD_M10 : str="M10"; break;
      case PERIOD_M12 : str="M12"; break;
      case PERIOD_M15 : str="M15"; break;
      case PERIOD_M20 : str="M20"; break;
      case PERIOD_M30 : str="M30"; break;
      case PERIOD_H1  : str="H1";  break;
      case PERIOD_H2  : str="H2";  break;
      case PERIOD_H3  : str="H3";  break;
      case PERIOD_H4  : str="H4";  break;
      case PERIOD_H6  : str="H6";  break;
      case PERIOD_H8  : str="H8";  break;
      case PERIOD_H12 : str="H12"; break;
      case PERIOD_D1  : str="D1";  break;
      case PERIOD_W1  : str="W1";  break;
      case PERIOD_MN1 : str="MN1"; break;
     }
//---
   return(str);
  }

Il modo in cui viene utilizzata la funzione CheckNewBar() verrà mostrato più avanti nell'articolo quando avremo tutte le altre funzioni necessarie pronte. Diamo ora un'occhiata alla funzione GetBarsData() che assume valori del numero richiesto di barre.

//+------------------------------------------------------------------+
//| GETTING BAR VALUES                                               |
//+------------------------------------------------------------------+
void GetBarsData()
  {
//--- Number of bars for getting their data in an array
   int amount=2;
//--- Reverse the time series ... 3 2 1 0
   ArraySetAsSeries(close_price,true);
   ArraySetAsSeries(open_price,true);
//--- Get the closing price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyClose(_Symbol,Period(),0,amount,close_price)<amount)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Close price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the opening price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyOpen(_Symbol,Period(),0,amount,open_price)<amount)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Open price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
  }

Diamo un'occhiata più da vicino al codice sopra. Innanzitutto, nella variabile amount, specifichiamo il numero di barre di cui dobbiamo ottenere i dati. Quindi impostiamo l'ordine di indicizzazione dell'array in modo che il valore dell'ultima barra (corrente) sia nell'indice zero dell'array, utilizzando la funzione ArraySetAsSeries(). Ad esempio, se si desidera utilizzare il valore dell'ultima barra nei calcoli, può essere scritto come segue, se esemplificato dal prezzo di apertura: open_price[0]. La notazione per la penultima barra sarà allo stesso modo: open_price[1].

Il meccanismo per ottenere i prezzi di chiusura e apertura è simile a quello della funzione CheckNewBar() in cui dovevamo ottenere l'ora dell'ultima barra. È solo che in questo caso usiamo le funzioni CopyClose() e CopyOpen(). Allo stesso modo, CopyHigh() e CopyLow() vengono utilizzati rispettivamente per ottenere i prezzi alti e bassi della barra.

Andiamo avanti e consideriamo un esempio molto semplice che mostra come determinare i segnali per l'apertura / inversione di una posizione. Le matrici di prezzi memorizzano i dati per due barre (barra corrente e quella precedente completata). Utilizzeremo i dati della barra completata.

  • Un segnale di acquisto si verifica quando il prezzo di chiusura è superiore al prezzo di apertura (barra rialzista);
  • Un segnale di vendita si verifica quando il prezzo di chiusura è inferiore al prezzo di apertura (barra ribassista).

Il codice per l'implementazione di queste semplici condizioni è fornito di seguito:

//+------------------------------------------------------------------+
//| DETERMINING TRADING SIGNALS                                      |
//+------------------------------------------------------------------+
int GetTradingSignal()
  {
//--- A Buy signal (0) :
   if(close_price[1]>open_price[1])
      return(0);
//--- A Sell signal (1) :
   if(close_price[1]<open_price[1])
      return(1);
//--- No signal (3):
   return(3);
  }

Come puoi vedere, è molto semplice. Si può facilmente capire come gestire condizioni più complesse in modo simile. La funzione restituisce zero se una barra completata è su o una se una barra completata è giù. Se per qualsiasi motivo non c'è segnale, la funzione restituirà 3.

Ora abbiamo solo bisogno di creare una funzione TradingBlock() per l'implementazione delle attività di trading. Di seguito è riportato il codice della funzione con commenti dettagliati:

//+------------------------------------------------------------------+
//| TRADING BLOCK                                                    |
//+------------------------------------------------------------------+
void TradingBlock()
  {
   int               signal=-1;           // Variable for getting a signal
   string            comment="hello :)";  // Position comment
   double            start_lot=0.1;       // Initial volume of a position
   double            lot=0.0;             // Volume for position calculation in case of reverse position
   double            ask=0.0;             // Ask price
   double            bid=0.0;             // Bid price
//--- Get a signal
   signal=GetTradingSignal();
//--- Find out if there is a position
   pos_open=PositionSelect(_Symbol);
//--- If it is a Buy signal
   if(signal==0)
     {
      //--- Get the Ask price
      ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
      //--- If there is no position
      if(!pos_open)
        {
         //--- Open a position. If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,start_lot,ask,0,0,comment))
           { Print("Error opening a BUY position: ",GetLastError()," - ",ErrorDescription(GetLastError())); }
        }
      //--- If there is a position
      else
        {
         //--- Get the position type
         pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         //--- If it is a SELL position
         if(pos_type==POSITION_TYPE_SELL)
           {
            //--- Get the position volume
            pos_volume=PositionGetDouble(POSITION_VOLUME);
            //--- Adjust the volume
            lot=NormalizeDouble(pos_volume+start_lot,2);
            //--- Open a position. If the position failed to open, print the relevant message
            if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,lot,ask,0,0,comment))
              { Print("Error opening a SELL position: ",GetLastError()," - ",ErrorDescription(GetLastError())); }
           }
        }
      //---
      return;
     }
//--- If there is a Sell signal
   if(signal==1)
     {
      //-- Get the Bid price
      bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
      //--- If there is no position
      if(!pos_open)
        {
         //--- Open a position. If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,start_lot,bid,0,0,comment))
           { Print("Error opening a SELL position: ",GetLastError()," - ",ErrorDescription(GetLastError())); }
        }
      //--- If there is a position
      else
        {
         //--- Get the position type
         pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         //--- If it is a BUY position
         if(pos_type==POSITION_TYPE_BUY)
           {
            //--- Get the position volume
            pos_volume=PositionGetDouble(POSITION_VOLUME);
            //--- Adjust the volume
            lot=NormalizeDouble(pos_volume+start_lot,2);
            //--- Open a position. If the position failed to open, print the relevant message
            if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,lot,bid,0,0,comment))
              { Print("Error opening a SELL position: ",GetLastError()," - ",ErrorDescription(GetLastError())); }
           }
        }
      //---
      return;
     }
  }

Credo che tutto debba essere chiaro fino al punto in cui si apre una posizione. Come puoi vedere nel codice sopra, il puntatore(trade)è seguito da un punto che a sua volta è seguito dal metodo PositionOpen(). Questo è il modo in cui puoi chiamare un determinato metodo da una classe. Dopo aver inserito un punto, verrà visualizzato un elenco contenente tutti i metodi di classe. Tutto ciò che serve è selezionare il metodo richiesto dall'elenco:

Fig. 1. Chiamata di un metodo di classe.

Fig. 1. Chiamata di un metodo di classe.

Ci sono due blocchi principali nella funzione TradingBlock() - comprare e vendere. Subito dopo aver determinato la direzione del segnale, otteniamo il prezzo ask in caso di segnale buy e il prezzo bid in caso di segnale sell.

Tutti i prezzi/livelli utilizzati negli ordini commerciali devono essere normalizzati utilizzando la funzione NormalizeDouble(), altrimenti un tentativo di aprire o modificare una posizione porterà a un errore. L'uso di questa funzione è consigliabile anche nel calcolo del lotto. Inoltre, si prega di notare che i parametri Stop Loss e Take Profit hanno valori zero. Maggiori informazioni sull'impostazione dei livelli di trading saranno fornite nel prossimo articolo della serie.

Quindi, ora che tutte le funzioni definite dall'utente sono pronte, possiamo disporle nell'ordine corretto:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the new bar
   CheckNewBar();
//--- Get position properties and update the values on the panel
   GetPositionProperties();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Print the deinitialization reason to the journal
   Print(GetDeinitReasonText(reason));
//--- When deleting from the chart
   if(reason==REASON_REMOVE)
      //--- Delete all objects relating to the info panel from the chart
      DeleteInfoPanel();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If the bar is not new, exit
   if(!CheckNewBar())
      return;
//--- If there is a new bar
   else
     {
      GetBarsData();  // Get bar data
      TradingBlock(); // Check the conditions and trade
     }
//--- Get the properties and update the values on the panel
   GetPositionProperties();
  }

C'è solo una cosa da considerare: determinare gli eventi commerciali utilizzando la funzione OnTrade(). Qui, lo toccheremo solo brevemente per darti un'idea generale. Nel nostro caso, dobbiamo implementare il seguente scenario: quando si apre / chiude / modifica una posizione manualmente, i valori nell'elenco delle proprietà di posizione sul pannello informativo devono essere aggiornati non appena l'operazione è completata, piuttosto che dopo aver ricevuto un nuovo segno di spunta. A tale scopo, dobbiamo solo aggiungere il seguente codice:

//+------------------------------------------------------------------+
//| TRADE EVENT                                                      |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Get position properties and update the values on the panel
   GetPositionProperties();
  }

Fondamentalmente, tutto è pronto e possiamo procedere ai test. Strategy Tester consente di eseguire rapidamente un test in modalità di visualizzazione e di trovare eventuali errori. L'uso dello Strategy Tester può anche essere visto come vantaggioso a causa del fatto che puoi continuare a sviluppare il tuo programma anche nei fine settimana quando i mercati sono chiusi.

Impostare Strategy Tester, abilitare la modalità di visualizzazione e fare clic su Start. L'Expert Advisor inizierà a fare trading nello Strategy Tester e vedrai un'immagine simile a quella mostrata di seguito:

Fig. 2. Modalità di visualizzazione nel MetaTrader 5 Strategy Tester.

Fig. 2. Modalità di visualizzazione nel MetaTrader 5 Strategy Tester.

È possibile sospendere i test in modalità di visualizzazione in qualsiasi momento e continuare il test passo dopo passo premendo F12. Il passaggio sarà uguale a una barra se si imposta il tester strategico sulla modalità Solo prezzi di apertura o a un segno di spunta se è stata selezionata la modalità Ogni segno di spunta. È inoltre possibile controllare la velocità di test.

Per assicurarsi che i valori sul pannello informativo vengano aggiornati subito dopo l'apertura / chiusura manuale di una posizione o l'aggiunta / modifica dei livelli di Stop Loss/Take Profit, l'Expert Advisor deve essere testato in modalità in tempo reale. Per non aspettare troppo a lungo, è sufficiente eseguire l'Expert Advisor su un intervallo di tempo di 1 minuto in modo che le operazioni di trading vengano eseguite ogni minuto.

Oltre a ciò, ho aggiunto un altro array per i nomi delle proprietà di posizione nel pannello delle informazioni:

// Array of position property names
string pos_prop_texts[INFOPANEL_SIZE]=
  {
   "Symbol :",
   "Magic Number :",
   "Comment :",
   "Swap :",
   "Commission :",
   "Open Price :",
   "Current Price :",
   "Profit :",
   "Volume :",
   "Stop Loss :",
   "Take Profit :",
   "Time :",
   "Identifier :",
   "Type :"
  };

Nell'articolo precedente, ho detto che avremmo bisogno di questo array per ridurre il codice della funzione SetInfoPanel(). Ora puoi vedere come questo può essere fatto Se non l'hai ancora implementato o capito da solo. La nuova implementazione dell'elenco di creazione degli oggetti relativi alle proprietà di posizione è la seguente:

//--- List of the names of position properties and their values
   for(int i=0; i<INFOPANEL_SIZE; i++)
     {
      //--- Property name
      CreateLabel(0,0,pos_prop_names[i],pos_prop_texts[i],anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[i],2);
      //--- Property value
      CreateLabel(0,0,pos_prop_values[i],GetPropertyValue(i),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[i],2);
     }

All'inizio della funzione SetInfoPanel() è possibile notare la seguente riga:

//--- Testing in the visualization mode
   if(MQL5InfoInteger(MQL5_VISUAL_MODE))
     {
      y_bg=2;
      y_property=16;
     }

Trasmette al programma che le coordinate Y degli oggetti sul pannello informativo devono essere regolate se il programma è attualmente in fase di test in modalità di visualizzazione. Ciò è dovuto al fatto che durante il test nella modalità di visualizzazione di Strategy Tester, il nome dell'Expert Advisor non viene visualizzato nell'angolo in alto a destra del grafico come in tempo reale. Pertanto l'indentazione non necessaria può essere eliminata.


Conclusione

Per ora abbiamo finito. Nel prossimo articolo, ci concentreremo sull'impostazione e la modifica dei livelli di trading. Di seguito è possibile scaricare il codice sorgente dell'Expert Advisor, PositionPropertiesTesterEN.mq5.

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

Manuale MQL5: Come evitare errori durante l'impostazione/modifica dei livelli di trading Manuale MQL5: Come evitare errori durante l'impostazione/modifica dei livelli di trading
In continuazione del nostro lavoro sull'Expert Advisor dal precedente articolo della serie chiamata "Manuale MQL5: Analizzando le proprietà della posizione nel tester di strategia MetaTrader 5", lo miglioreremo con un sacco di funzioni utili, oltre a migliorare e ottimizzare quelle esistenti. L'Expert Advisor questa volta avrà parametri esterni che possono essere ottimizzati nel MetaTrader 5 Strategy Tester e in qualche modo assomiglierà a un semplice sistema di trading.
Manuale MQL5: Proprietà di posizione nel pannello delle informazioni personalizzate Manuale MQL5: Proprietà di posizione nel pannello delle informazioni personalizzate
Questa volta creeremo un semplice Expert Advisor che otterrà le proprietà della posizione sul simbolo corrente e le visualizzerà sul pannello informativo personalizzato durante il trading manuale. Il pannello informativo verrà creato utilizzando oggetti grafici e le informazioni visualizzate verranno aggiornate ad ogni tick. Questo sarà molto più conveniente rispetto al dover eseguire manualmente lo script descritto nel precedente articolo della serie chiamata "Manuale MQL5: Ottenere le Proprietà di posizione
Manuale MQL5: La cronologia delle offerte e la libreria di funzioni per ottenere proprietà di posizione Manuale MQL5: La cronologia delle offerte e la libreria di funzioni per ottenere proprietà di posizione
È il momento di riassumere brevemente le informazioni fornite nei precedenti articoli sulle proprietà della posizione. In questo articolo creeremo alcune funzioni aggiuntive per ottenere le proprietà che possono essere ottenute solo dopo aver effettuato l'accesso alla cronologia delle offerte. Acquisiremo anche familiarità con le strutture dati che ci consentiranno di accedere alle proprietà di posizione e simbolo in modo più comodo.
Manuale MQL5: Ottenere Proprietà di Posizione Manuale MQL5: Ottenere Proprietà di Posizione
In questo articolo, creeremo uno script che ottiene tutte le proprietà di posizione e le mostra all'utente in una finestra di dialogo. Durante l'esecuzione dello script, sarà possibile selezionare tra due modalità disponibili nell'elenco a discesa nei parametri esterni: visualizzare le proprietà di posizione solo sul simbolo corrente o visualizzare le proprietà di posizione su tutti i simboli.