Come chiamare gli indicatori in MQL5

KlimMalgin | 9 dicembre, 2021

Introduzione

In MQL5 ci sono diversi modi per chiamare gli indicatori e sono per lo più eseguiti utilizzando le funzioni IndicatorCreate() e iCustom(). Inoltre, queste funzioni restituiscono solo l'handle dell'indicatore e, tramite esso, viene svolto ulteriore lavoro sugli indicatori. Dunque, cosa è un handle? Come gestire le funzioni IndicatorCreate() e iCustom()? E in che modo il tuo expert otterrà i dati degli indicatori? Tutte queste domande vengono trattate in questo articolo.

Creazione di un file sorgente

Per iniziare con il nostro expert, creiamo il suo file sorgente. Lo faremo più o meno come nel MetaEditor4 chiamando la procedura guidata MQL5 dal menu File -> Nuovo. Al primo passaggio, seleziona l'Expert Advisor e premi Avanti.


Nella seconda fase, ci viene offerto di inserire il nome, i dati di contatto dell'autore e i parametri di input per l'Expert Advisor. Inserisci solo il Nome dell’advisor (campo obbligatorio) e l'Autore. Se necessario, puoi possibile aggiungere i parametri di input in un secondo momento direttamente nel codice del programma.


Dopo aver fatto clic su Fine la procedura guidata creerà il seguente codice sorgente:

//+------------------------------------------------------------------+
//|                                                   SampleMQL5.mq5 |
//|                                             Copyright KlimMalgin |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "KlimMalgin"
#property link      ""
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Struttura del file sorgente

Analizziamo brevemente la struttura del file sorgente di un expert e andiamo oltre.

Il codice del programma inizia con un'intestazione. È un commento, contenente il nome del file e l'autore. Dopo il titolo vengono le Proprietà del Programma (#property) di un expert. Qui, viene indicata automaticamente la versione del programma, nonché il nome dell'autore e il link alla tua pagina, se li hai inseriti. Dopo aver descritto le proprietà, vengono specificati i parametri di input (noi non li abbiamo ancora specificati) e, successivamente, vengono dichiarate le variabili globali.

Voglio sottolineare che le proprietà dovrebbero essere impostate nel file mql5 principale. Tutte le proprietà dei file inclusi verranno ignorate!

Successivamente, ci sono tre funzioni che elaborano gli eventi:

Collegare gli indicatori

In MQL5, i metodi di lavoro con gli indicatori sono cambiati! In MQL4, per ottenere il valore dell'indicatore su una barra particolare, l'indice di quella barra è passato alla funzione iCustom() come parametro. Ora, non otteniamo il valore del buffer dell'indicatore, ma otteniamo il suo handle.  E usando questo handle, possiamo ottenere i valori dell'indicatore su un dato intervallousando una funzione speciale chiamata CopyBuffer(). Allora, cos'è un handle?

L’ Handleè un identificatore univoco, creato per essere utilizzato con determinati indicatori. È rappresentato come un numero intero ed è memorizzato nel tipo int della variabile. Esistono 2 modi per ottenere l'handle dell'indicatore in MQL5:\

Entrambi i metodi sono ugualmente validi e sta a te decidere quale di essi utilizzare. Analizziamo nel dettaglio entrambi i metodi.

Ottenere l'handle dell'indicatore usando la funzione IndicatorCreate()

Quindi, come risultato della corretta esecuzione della funzione IndicatorCreate() otterremo l'handle dell'indicatore e, in caso di errore, la funzione restituirà il valore INVALID_HANDLE uguale a -1. Se l'indicatore chiamato ha parametri, allora, prima di chiamare la funzione, dobbiamo riempire un array di parametri di tipo MqlParam. Ogni elemento di questo array è rappresentato come una struttura di tipo MqlParam contenente 4 campi. Il primo campo di questa struttura indica quale dei tre campi rimanenti viene utilizzato come parametro per l'indicatore.

Scriviamo il codice che creerà gli handle degli indicatori Moving Average e ParabolicSAR. Inizieremo con la dichiarazione delle variabili globali, che memorizzeranno gli handle, e dichiareremo un array di tipo MqlParam per impostare i parametri per gli indicatori:

int MA1 = 0,            // Declaring variable to store fast MA handle
    MA2 = 0,            // Declaring variable to store slow MA handle
    SAR = 0;            // Declaring variable to store SAR handle
    
MqlParam params[];      // Array for storing indicators parameters

Questo codice deve essere inserito immediatamente dopo aver definito le proprietà dell'expert.

Ora, quando le variabili sono dichiarate, puoi riempire l'array dei parametri e chiamare la funzione IndicatorCreate(). È meglio farlo nella funzione OnInit(), poiché l'indicatore verrà creato una volta all'inizio del programma e non su ogni tick!

Compiliamo l'array di parametri e chiamiamo il primo indicatore:

int OnInit()
{
//---
   // Setting the params size equal to number of called indicator parameters
   ArrayResize(params,4);

   // Setting the period of fast MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Offset
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), // Symbol used to calculate the indicator
                         0,        // Timeframe. 0 means current
                         IND_MA,   // Indicator type from the ENUM_INDICATOR enumeration
                         4,        // Number of parameters passed in the params array
                         params    // Parameters array
                         );   
//---
return(0);
}

Poiché l'array params è stato dichiarato come un array dinamico, ovvero la dimensione dell'array non è stata impostata tra parentesi, allora prima di utilizzarlo dobbiamo impostarne la dimensione. Usando la funzione ArrayResize(), definiremo la dimensione uguale al numero di parametri che trasferiremo nell'array. Per la media mobile sono necessari quattro parametri.

Il primo parametro è il periodo MA. È impostato come numero intero. Pertanto, nel campo type del primo parametro imposteremo il valore TYPE_INT, quindi la nostra funzione IndicatorCreate() "capirà" che il valore dovrebbe essere preso dal campo integer_value. Poiché tutti i parametri per la media mobile sono di tipo int, verranno impostati di conseguenza.

Una volta che l'array è stato compilato, puoi chiamare la funzione IndicatorCreate(). Il suo output verrà memorizzato nella variabile MA1. Non ci dovrebbero essere difficoltà a chiamare la funzione e solo cinque parametri devono essere trasferiti in essa:

Questo è tutto! Abbiamo l’handle della MA veloce e dobbiamo ottenere gli handle degli indicatori della MA lenta e SAR. Di conseguenza, la funzione OnInit() sarà simile a:

int OnInit()
  {
//---

   // Setting the params size equal to number of called indicator's parameters
   ArrayResize(params,4);

//*    Calling indicators   *
//***************************
   // Setting the period of fast MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Offset
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   // Setting the period of slow MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=21;
   // Offset   
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA2 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   
   // Changing array size to store the SAR indicator parameters
   ArrayResize(params,2);
   // Step
   params[0].type         =TYPE_DOUBLE;
   params[0].double_value = 0.02;
   // Maximum
   params[1].type         =TYPE_DOUBLE;
   params[1].double_value = 0.2;
   
   SAR = IndicatorCreate(Symbol(), 0, IND_SAR, 2, params);
   
//---
   return(0);
  }

Per ottenere l'handle della MA lenta, puoi modificare solo il periodo da 5 a 21. E quando viene chiamato il ParabolicSAR, i numeri in virgola mobile (doppi) vengono trasferiti come parametri. Quando si riempie l'array è necessario tenerne conto e inserire rispettivamente TYPE_DOUBLE nel campo type e il parametro stesso nel campo double_value. Inoltre, il SAR richiede solo due parametri, quindi la dimensione dell'array cambia a 2 usando ArrayResize().

Per ora, la funzione IndicatorCreate() termina. A mio parere, questo metodo per chiamare gli indicatori è più conveniente nell'approccio orientato agli oggetti, quando la libreria di classi viene creata per funzionare con gli indicatori. Questa libreria semplificherà l'elaborazione di progetti su larga scala. E per quanto riguarda la scrittura e il test di expert rapidi, è meglio utilizzare il seguente metodo.

Ottenere un handle utilizzando le funzioni degli indicatori tecnici

Il secondo metodo, che consente di ottenere l'handle dell'indicatore, è chiamare una delle funzioni degli indicatori tecnici. Ognuna di queste funzioni ha lo scopo di ottenere la gestione di uno degli indicatori standard, forniti con MetaTrader 5. Hanno tutti un certo insieme di parametri, a seconda dell'indicatore a cui sono destinati. Ad esempio, l'indicatore per CCI viene chiamata la funzione iCCI() e ha il seguente aspetto:

   CCI = iCCI(
              Symbol(),            // symbol name
              PERIOD_CURRENT,      // period
              20,                  // averaging period
              PRICE_CLOSE          // price type or handle
              );

L'handle di altri indicatori può essere inviato come ultimo parametro nella funzione degli indicatori tecnici invece del tipo di prezzo. Quindi l'indicatore, chiamato in questo modo, verrà calcolato utilizzando non i dati del prezzo, ma i dati di un altro indicatore.

La funzione iCustom() suscita particolare interesse tra le altre funzioni di indicatori tecnici, perché può chiamare qualsiasi indicatore, inclusi quelli personalizzati. Scriviamo il codice simile al precedente, ma utilizzando la funzione iCustom():

int OnInit()
  {
//---

   MA1 = iCustom(NULL,0,"Custom Moving Average",
                          5,          // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   MA2 = iCustom(NULL,0,"Custom Moving Average",
                          21,         // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   SAR = iCustom(NULL,0,"ParabolicSAR",
                          0.02,        // Step
                          0.2          // Maximum
                 );

//---
   return(0);
  }

Tenete a mente che è meglio ottenere l’handle una volta durante l'avvio dell’expert, quindi facciamo una chiamata nella funzione OnInit().

la funzione iCustom() contiene i seguenti parametri:

  1. Nome dello strumento di lavoro, quali dati vengono utilizzati per calcolare l'indicatore. NULL - indica lo strumento corrente. Puoi usare Symbol () invece di NULL, non c'è molta differenza. In caso di Symbol(), il nome di uno strumento viene trasferito a iCustom(), ma in caso di NULL, iCustom() trova lo strumento finanziario stesso.
  2. Periodo del grafico. Per specificare il periodo, puoi utilizzare intervalli predefiniti del grafico oltre a 0 - per fare riferimento all'intervallo corrente.
  3. Nome dell’indicatore. L'indicatore chiamato dovrebbe essere compilato e si dovrebbe trovare nella cartella "MQL5\Indicators\" o in una delle sue sottocartelle.
  4. Restanti parametri. Questi valori verranno utilizzati come parametri di input per l'indicatore chiamato, quindi il tipo e la sequenza devono corrispondere tra loro. Se non sono specificati, verranno utilizzati i valori predefiniti.

Come è stato detto, questo metodo è più comodo da usare con gli indicatori se non hai una libreria che hai usato per lavorare. Ora, quando vengono ricevuti gli handle, puoi iniziare a lavorare con i valori degli indicatori!

Ottenere i dati dell'indicatore

La cosa bella di lavorare con i dati dell'indicatore in MQL5 è che ora abbiamo l'opportunità di ottenere i dati solo nel dato intervallo di cui abbiamo bisogno. Puoi impostare l'intervallo in diversi modi:

Per leggere la data dell'indicatore viene utilizzata la funzione CopyBuffer(). Quale metodo per ottenere i dati verrà utilizzato dipende da quali parametri di input verranno trasferiti a questa funzione.

Scriviamo il codice che copia i dati dell'indicatore (abbiamo ottenuto i loro handle in precedenza) negli array:

void OnTick()
  {
//---

// Dynamic arrays to store indicators values
double _ma1[],
       _ma2[],
       _sar[];

// Setting the indexing in arrays the same as in timeseries, i.e. array element with zero
// index will store the values of the last bar, with 1th index - the last but one, etc.
   ArraySetAsSeries(_ma1, true);
   ArraySetAsSeries(_ma2, true);
   ArraySetAsSeries(_sar, true);

// Using indicators handles, let's copy the values of indicator
// buffers to arrays, specially prepared for this purpose
   if (CopyBuffer(MA1,0,0,20,_ma1) < 0){Print("CopyBufferMA1 error =",GetLastError());}
   if (CopyBuffer(MA2,0,0,20,_ma2) < 0){Print("CopyBufferMA2 error =",GetLastError());}
   if (CopyBuffer(SAR,0,0,20,_sar) < 0){Print("CopyBufferSAR error =",GetLastError());}


//---
  }

Ora tutto il nostro lavoro si concentrerà sulla funzione OnTick(), perché su ogni tick i dati degli indicatori cambiano e devono essere aggiornati.

Per prima cosa, dichiariamo gli array dinamici per ogni buffer dell'indicatore. A mio parere, è più conveniente lavorare con i dati dell'indicatore, se l'indicizzazione dell'array sarà la stessa delle serie temporali, ovvero nell'elemento dell'array 0 ci saranno i dati dell'ultima barra, nell'elemento numero 1 - il penultimo , eccetera. Il metodo di indicizzazione viene impostato utilizzando la funzione ArraySetAsSeries().

Una volta impostato il metodo di indicizzazione, possiamo iniziare a riempire gli array. Per calcolare ciascun indicatore chiameremo la funzione CopyBuffer() con il seguente set di parametri:

  1. L’Indicatore handle, quali dati si desidera ottenere.
  2. Numero indicatore buffer. Poiché gli indicatori MA e SAR hanno un buffer, li imposteremo a 0. Se ci sono più di un buffer, il secondo sarà il numero 1, il terzo sarà il numero 2, ecc.
  3. Barra iniziale, da cui parte il conteggio.
  4. Il numero di battute che vuoi copiare.
  5. L'array{/0} in cui vengono copiati i dati.

In caso di chiamata riuscita, la funzione CopyBuffer() restituirà il numero di elementi copiati, in caso di errore restituirà -1. In caso di errore, l'errore deve essere tracciato, quindi se il valore restituito è inferiore a 0, scriveremo l'errore nel log degli expert utilizzando la funzione Print().

Conclusione

Per ora, il nostro lavoro con gli indicatori è ben lungi dall'essere concluso. Questo articolo riguarda solo i metodi di base per accedere ai propri dati. E per comodità, è meglio usare un approccio orientato agli oggetti che è implementato in MQL5 a un livello abbastanza buono!