English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Indicatori Personalizzati in MQL5 per Principianti

Indicatori Personalizzati in MQL5 per Principianti

MetaTrader 5Esempi | 8 dicembre 2021, 17:20
579 1
Nikolay Kositsin
Nikolay Kositsin

Introduzione

La base della profonda comprensione di qualsiasi materia intellettuale (non importa se matematica, musica o programmazione) è lo studio dei suoi fondamenti. È fantastico quando uno studio simile viene avviato in un'età abbastanza giovane, quindi la comprensione dei fondamenti è molto più semplice e la conoscenza è specifica e completa.

Sfortunatamente, la maggior parte delle persone inizia a studiare i mercati finanziari e azionari verso la mezza età, quindi lo studio non è facile. In questo articolo, cercherò di aiutare a superare questa barriera iniziale nella comprensione di MQL5 e nella scrittura di indicatori personalizzati per il client terminal MetaTrader 5.

Indicatore SMA come Semplice Esempio

Il modo più efficace e razionale per studiare qualcosa è la risoluzione di problemi pratici. Poiché stiamo considerando indicatori personalizzati, inizieremo con lo studio dell'indicatore semplice che contiene un codice che illustra i fondamenti del funzionamento dell'indicatore in MQL5.

Ad esempio, consideriamo l'indicatore più famoso dell'analisi tecnica: la Media Mobile Semplice (SMA). Il suo calcolo è semplice:

SMA = SUM (CLOSE (i), MAPeriod) / MAPeriod

dove:

  • SUM — somma dei valori;
  • CLOSE (i) — prezzo di chiusura dell'i-esima barra;
  • MAPeriod — numero di barre di cui calcolare la media (periodo medio).

Ecco un codice di questo indicatore privo di eccessi:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
  
input int MAPeriod = 13;
input int MAShift = 0; 
  
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_SHIFT, MAShift);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MAPeriod - 1);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
  {
   if (rates_total < MAPeriod - 1)
    return(0);
    
   int first, bar, iii;
   double Sum, SMA;
   
   if (prev_calculated == 0)
    first = MAPeriod - 1 + begin;
   else first = prev_calculated - 1;

   for(bar = first; bar < rates_total; bar++)
    {
     Sum = 0.0;
     for(iii = 0; iii < MAPeriod; iii++)
      Sum += price[bar - iii];
     
     SMA = Sum / MAPeriod;
      
     ExtLineBuffer[bar] = SMA;
    }
     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Ed ecco un risultato del suo lavoro nel client terminal MetaTrader 5:

Per prima cosa, dobbiamo considerare due cose: lo scopo di ogni stringa del codice, da un lato, e l'interazione tra questo codice del programma e il client terminal, dall'altro.

Utilizzo dei Commenti

Ad un primo sguardo, il codice dell'indicatore, l'occhio cattura oggetti come questi:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

È necessario notare che non sono correlati direttamente al codice, sono solo commenti, sono progettati per la leggibilità del codice e mostrano un certo contenuto semantico di alcune parti di questo codice. Certo, potrebbero essere rimossi dal codice senza alcun danno per la sua ulteriore semplificazione, ma il codice perderebbe, quindi, la sua laconica brevità di comprensione. Nel nostro caso, abbiamo a che fare con commenti a linea singola che iniziano sempre da una coppia di caratteri "//" e terminano con un carattere di nuova riga.

È chiaro che nei commenti l'autore può scrivere tutte le informazioni necessarie che aiuteranno a capire questo codice dopo un po' di tempo. Nel nostro caso, nella prima parte delle stringhe commentate, c'è un nome dell'indicatore e informazioni sul suo autore, la seconda e la terza parte dei commenti stanno dividendo le funzioni OnInit() e OnCalculate(). L'ultima riga alla fine chiude semplicemente il codice del programma.

Struttura del Codice SMA

Quindi, come vediamo, l'intero codice del nostro indicatore può essere diviso in 3 parti:

1. Il codice, che si scrive senza parentesi a livello globale, si trova tra i primi due commenti.
2. Descrizione della funzione OnInit().

3. Descrizione della funzione OnCalculate().

È necessario notare che in programmazione il significato di funzione è molto più ampio che in matematica. Ad esempio, nei linguaggi di programmazione le funzioni matematiche ricevono sempre alcuni parametri di input e restituiscono valori calcolati. Inoltre, le funzioni in MQL5 possono anche eseguire alcune operazioni sui grafici, operazioni di trading, operazioni sui file, ecc.

In effetti, qualsiasi indicatore scritto in MQL5 ha sempre un insieme minimo di parti scritte dall'utente, i cui contenuti sono individuali e dipendono dalle caratteristiche di un indicatore creato.

Oltre a questi componenti, il set minimo di funzioni può contenere la descrizione di un'altra funzione MQL5 - OnDeInit():

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+  
void OnDeinit(const int reason)
  {

  }

Nel nostro caso, non è necessario e, quindi, qui è assente.

Interazione tra SMA e il client terminal MetaTrader

Consideriamo ora il lavoro del file compilato SMA.ex5 che abbiamo ottenuto dopo aver premuto il tasto "Compile" in MetaEditor con SMA.mq5 aperto. È necessario notare che i file di testo con estensione .mq5 sono solo un codice sorgente in formato testo; esso dovrebbe essere compilato prima per essere utilizzato nel client terminal.

Dopo aver collegato questo indicatore ad un grafico dalla finestra del Navigatore, il MetaTrader eseguirà il codice della prima parte dell'indicatore. Successivamente chiamerà la funzione OnInit() per una singola esecuzione di questa funzione e inoltre, ad ogni nuovo tick (dopo l'arrivo della nuova quotazione) chiamerà la funzione OnCalculate() e, di conseguenza, eseguirà il codice di questa funzione.  SE OnDeInit() fosse presente nell'indicatore, MetaTrader chiamerebbe questa funzione una volta dopo aver staccato l'indicatore dal grafico o dopo la modifica dell’intervallo.

Il significato e lo scopo di tutte le parti dell'indicatore sono chiari dopo questa spiegazione. Nella prima parte del codice a livello globale, sono presenti alcuni semplici operatori che vengono eseguiti una volta dopo l'avvio dell'indicatore. Inoltre c'è una dichiarazione di variabili che sono "visibili" in tutti i blocchi dell'indicatore e che ricordano i loro valori mentre l'indicatore è sul grafico.

Le costanti e le funzioni che vengono eseguite una volta dovrebbero trovarsi all'interno della funzione OnInit(), perché sarebbe inopportuno inserirle nel blocco della funzione OnCalculate(). Il codice per il calcolo dell'indicatore, che permette di calcolarne i valori per ogni barra, andrebbe collocato nella funzione OnCalculate().

Le procedure che eliminano la spazzatura inutile (laddove presente) dal grafico dopo che l'indicatore è stato rimosso da esso, dovrebbero essere posizionate all'interno di OnDeInit(). Ad esempio, è necessario per la cancellazione degli oggetti grafici creati dall'indicatore.

Dopo queste precisazioni, siamo pronti ad esaminare in dettaglio il codice dell'indicatore che è stato considerato in precedenza.

Codice del programma dell'Indicatore SMA

Il primo gruppo di righe di codice inizia con l'operatore #property che consente di specificare i parametri aggiuntivi delle impostazioni dell'indicatore. L'elenco completo delle possibili proprietà del programma è disponibile nella documentazione di MQL5. Se necessario, è possibile scrivere proprietà aggiuntive dell'indicatore. Nel nostro caso, abbiamo 5 righe di codice. Lo scopo di ogni riga è descritto nei commenti:

//---- the indicator will be plotted in the main window
#property indicator_chart_window
//---- one buffer will be used for the calculations and plot of the indicator
#property indicator_buffers 1
//---- only one graphic plot is used 
#property indicator_plots   1
//---- the indicator should be plotted as a line
#property indicator_type1   DRAW_LINE
//---- the color of the indicator's line is red 
#property indicator_color1  Red 

Notare che non ci sono punti e virgola (";") alla fine delle righe. Il motivo è il seguente: infatti nel nostro caso si tratta della definizione di costanti, ma presentate in un altro modo.

La nostra media mobile semplice ha solo 2 parametri che possono essere modificati da un utente: è un periodo di media e uno spostamento orizzontale (in barre) dell'indicatore lungo gli assi temporali. Questi due parametri dovrebbero essere dichiarati come variabili di input dell'indicatore come è stato dichiarato in due ulteriori righe di codice:

//---- indicator input parameters
input int MAPeriod = 13; //averaging period
nput int MAShift = 0; //horizontal shift (in bars)

Si noti che dopo la dichiarazione di questi parametri di input ci sono commenti e questi commenti saranno visibili come nomi di parametri di input nella finestra "Proprietà" dell'indicatore:


Nel nostro caso, questi nomi sono molto più chiari dei nomi delle variabili dell'indicatore. Quindi, questi commenti dovrebbero essere semplici.

L'ultima riga di codice che non ha parentesi è la dichiarazione dell'array dinamico ExtLineBuffer[].

//---- the declaration of the dynamic array
//that will be used further as an indicator's buffer
double ExtLineBuffer[];  

È stata dichiarata come variabile globale per diversi motivi.

Prima di tutto, questo array dovrebbe essere convertito nel buffer dell'indicatore; esso è implementato nel blocco della funzione OnInit(). In secondo luogo, il buffer dell'indicatore stesso verrà utilizzato all'interno della funzione OnCalculate(). Terzo, questo array memorizzerà i valori dell'indicatore che verranno tracciati come una curva sul grafico. A causa del fatto che è stata dichiarata come variabile globale, essa è disponibile per tutti i blocchi dell'indicatore e conserva i suoi valori in ogni momento fino a quando l'indicatore non viene staccato dal grafico.

Il contenuto della funzione OnInit() è presentato da soli 3 operatori, sono funzioni integrate del client terminal MetaTrader.

La chiamata della prima funzione assegna il buffer indicatore zero ad un array dinamico unidimensionale ExtLineBuffer[]. Due chiamate di un'altra funzione con valori diversi dei parametri di input consentono di spostare l'indicatore lungo l'asse dei prezzi e di specificarne il tracciato dalla barra con il numero MAPeriod.

void OnInit()
  {
//----+
//---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- set plot shift along the horizontal axis by MAShift bars
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- set plot begin from the bar with number MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);
//----+
  }

L'ultima chiamata della funzione PlotIndexSetInteger() passa il valore pari a MAPeriod (tramite il parametro inizio della funzione OnCalculate()) ad un altro indicatore, se applicato ai valori del nostro indicatore. La logica è semplice, non c'è niente da calcolare nelle prime barre di MaPeriod-1, ecco perché il tracciamento di questo indicatore è inutile. Tuttavia, questo valore dovrebbe essere passato per spostare l'origine dei calcoli di un altro indicatore.

Non è un elenco completo delle funzioni integrate che vengono utilizzate negli indicatori personalizzati e che possono essere localizzate in questo blocco dell'indicatore. Vedere la documentazione MQL5 per i dettagli.

Infine, consideriamo il codice della funzione OnCalculate(). Non ci sono chiamate personalizzate in questa funzione come la funzione OnInit() perché queste funzioni sono chiamate dal client terminal MetaTrader. Per questo motivo, i parametri di input della funzione vengono dichiarati come costanti.

int OnCalculate(
                const int rates_total,    // number of available bars in history at the current tick
                const int prev_calculated,// number of bars, calculated at previous tick
                const int begin,          // index of the first bar
                const double &price[]     // price array for the calculation
                )

Questi parametri di input non possono essere modificati, i loro valori vengono passati dal client terminal per l'ulteriore utilizzo nel codice di questa funzione. Le variabili di input di OnCalculate sono descritte nella documentazione di MQL5. La funzione OnCalculate() restituisce i suoi valori per il client terminal utilizzando la funzione return(rates_total). Il client terminal riceve questo valore del tick corrente dopo l'esecuzione di OnCalculate() e passa il valore restituito a un altro parametro prev_calculated. Quindi, è sempre possibile determinare l'intervallo degli indici delle barre ed eseguire i calcoli contemporaneamente solo per i nuovi valori dell'indicatore che sono comparsi dopo il tick precedente.

È necessario notare che la disposizione delle barre nel client terminal MetaTrader viene eseguito da sinistra a destra, quindi la barra molto vecchia(quella a sinistra), presentata nel grafico ha indice 0, la successiva ha indice 1, ecc. Gli elementi del buffer ExtLineBuffer[] hanno lo stesso ordine.

La semplice struttura del codice all'interno della funzione OnCalculate del nostro indicatore è universale e tipica per molti indicatori di analisi tecnica. Quindi, analizziamolo più nel dettaglio. La logica della funzione OnCalcualte() è:

1. Verificare la presenza di barre, necessarie per i calcoli.
2. Dichiarazione di variabili locali.
3. Ottenere l'indice della barra di partenza per il calcolo.
4. Il ciclo principale del calcolo dell'indicatore
5. Restituire il valore di rates_total al client terminal utilizzando l'operatore return().

Penso che il primo termine sia chiaro. Ad esempio, se il periodo di media della Media Mobile è pari a 200, ma il client terminal ha solo 100 barre, non è necessario eseguire il calcolo perché non ci sono barre sufficienti per il calcolo. Quindi, dobbiamo restituire 0 al client terminal utilizzando l'operatore return.

//---- check for the presence of bars, sufficient for the calculation
   if(rates_total<MAPeriod-1+begin)
      return(0);

Il nostro indicatore può essere applicato ai dati di qualche altro indicatore che può anche avere un numero minimo di barre per il calcolo.  L'uso della costante begin è necessario e bisogna tenere in considerazione questo fatto. Per ulteriori dettagli, si veda l'articolo Applicazione di un Indicatore ad un Altro.

Le variabili locali, dichiarate in questo blocco, sono necessarie solo per i calcoli intermedi all'interno della funzione OnCalculate(). Queste variabili vengono rilasciate dalla RAM del computer dopo la chiamata della funzione.

//---- declaration of local variables 
   int first,bar,iii;
   double Sum,SMA;

E' necessario fare attenzione con l'indice di inizio del ciclo principale (variabile first). Alla prima chiamata della funzione (possiamo determinarla in base al valore del parametro prev_calculated), dobbiamo eseguire il calcolo dei valori dell'indicatore per tutte le barre. Per tutti gli ulteriori tick del client terminal dobbiamo eseguire il calcolo solo per le nuove barre comparse. È fatto da 3 righe di codice:

//---- calculation of starting index first of the main loop
   if(prev_calculated==0) // check for the first start of the indicator
      first=MAPeriod-1+begin; // start index for all the bars
   else first=prev_calculated-1; // start index for the new bars

L'intervallo delle modifiche delle variabili nell'operatore del ciclo principale del ricalcolo dell'indicatore è già stato discusso.

//---- main loop of the calculation
   for(bar=first;bar<rates_total;bar++)

L'elaborazione delle barre nel ciclo principale viene eseguita in ordine crescente (bar++), in altre parole, da sinistra a destra, in modo naturale e corretto. Nel nostro indicatore, potrebbe essere implementato in un altro modo (in ordine inverso). È meglio usare l'ordine crescente negli indicatori. La variabile del ciclo principale è denominata "bar", ma molti programmatori preferiscono usare il nome "i". Preferisco usare la "barra", perché rende il codice più chiaro e leggibile.

L'algoritmo di media, che è stato implementato nel ciclo principale, è semplice.

     {
      Sum=0.0;
       //---- summation loop for the current bar averaging
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // Sum = Sum + price[bar - iii]; // eqaual to 
      
      //---- calculate averaged value
      SMA=Sum/MAPeriod;

      //---- set the element of the indicator buffer with the value of SMA we have calculated
      ExtLineBuffer[bar]=SMA;
     }

Nel secondo ciclo, stiamo eseguendo la somma cumulativa dei prezzi dalle barre precedenti del periodo e dividendola per questo periodo medio. Di conseguenza, abbiamo il valore finale di SMA.

Al termine del ciclo principale, la funzione OnCalculate restituisce il numero di barre disponibili dalla variabile rates_total. Nella successiva chiamata della funzione OnCalculate(), questo valore verrà passato dal client terminal alla variabile prev_calculated. Questo valore, diminuito di 1, verrà utilizzato come indice di inizio per il ciclo principale.

Ecco il codice sorgente completo dell'indicatore con commenti dettagliati per ogni riga di codice:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//---- the indicator will be plotted in the main window
#property indicator_chart_window
//---- one buffer will be used for the calculations and plot of the indicator
#property indicator_buffers 1
//---- only one graphic plot is used 
#property indicator_plots   1
//---- the indicator should be plotted as a line
#property indicator_type1   DRAW_LINE
//---- the color of the indicator's line is red 
#property indicator_color1  Red 

//---- indicator input parameters
input int MAPeriod = 13; //Averaging period
input int MAShift = 0; //Horizontal shift (in bars)

//---- the declaration of the dynamic array
//that will be used further as an indicator's buffer
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- set plot shift along the horizontal axis by MAShift bars
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- set plot begin from the bar with number MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);  
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // number of available bars in history at the current tick
                const int prev_calculated,// number of bars, calculated at previous tick
                const int begin,          // index of the first bar
                const double &price[]     // price array for the calculation
                )
  {
//----+   
   //---- check for the presence of bars, sufficient for the calculation
   if (rates_total < MAPeriod - 1 + begin)
    return(0);
   
   //---- declaration of local variables 
   int first, bar, iii;
   double Sum, SMA;
   
   //---- calculation of starting index first of the main loop
   if(prev_calculated==0) // check for the first start of the indicator
      first=MAPeriod-1+begin; // start index for all the bars
   else first=prev_calculated-1; // start index for the new bars

   //---- main loop of the calculation
   for(bar = first; bar < rates_total; bar++)
    {    
      Sum=0.0;
      //---- summation loop for the current bar averaging
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // It's equal to: Sum = Sum + price[bar - iii];
         
      //---- calculate averaged value
      SMA=Sum/MAPeriod;

      //---- set the element of the indicator buffer with the value of SMA we have calculated
      ExtLineBuffer[bar]=SMA;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Questa forma di codice è molto più semplice da capire e da leggere.

Vorrei delineare un'altra funzionalità che può essere utilizzata per semplificare la comprensione del codice. Puoi usare spazi e righe vuote per renderlo chiaro.

Conclusione

Si tratta dell'interazione tra il codice dell'indicatore personalizzato e il client terminal MetaTrader. Ovviamente l'argomento è molto più vasto di quello che abbiamo considerato; lo scopo di questo articolo è aiutare i principianti a comprendere i fondamenti, quindi consulta la documentazione per i dettagli.

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

File allegati |
sma.mq5 (1.78 KB)
sma_.mq5 (3.32 KB)
Ultimi commenti | Vai alla discussione (1)
Marilena Oliva
Marilena Oliva | 9 dic 2021 a 12:37
Truffe all ordine del giorno. Bravi 
Elaborazione di eventi di trading nell'Expert Advisor utilizzando la funzione OnTrade() Elaborazione di eventi di trading nell'Expert Advisor utilizzando la funzione OnTrade()
MQL5 ha fornito una miriade di innovazioni, incluso il lavoro con eventi di vario tipo (eventi timer, eventi di trading, eventi personalizzati, ecc.). La capacità di gestire gli eventi ti consente di creare tipi completamente nuovi di programmi per il trading automatico e semi-automatico. In questo articolo, considereremo gli eventi di trading e scriveremo del codice per la funzione OnTrade(), che elaborerà l'evento Trade.
Usare i Puntatori di Oggetti in MQL5 Usare i Puntatori di Oggetti in MQL5
Di default, tutti gli oggetti in MQL5 vengono passati per riferimento, ma esiste la possibilità di utilizzare i puntatori agli oggetti. Tuttavia è necessario eseguire il controllo del puntatore, poiché l'oggetto potrebbe non essere inizializzato. In questo caso, il programma MQL5 verrà terminato con errore critico e scaricato. Gli oggetti, creati automaticamente, non causano tale errore, quindi in questo senso sono abbastanza sicuri. In questo articolo, cercheremo di comprendere la differenza tra il riferimento all'oggetto e il puntatore all'oggetto e considereremo come scrivere il codice sicuro, che utilizzi i puntatori.
Come chiamare gli indicatori in MQL5 Come chiamare gli indicatori in MQL5
Con la nuova versione del linguaggio di programmazione MQL disponibile non solo è cambiato l'approccio alla gestione degli indicatori, ma ci sono anche nuovi modi di creare gli indicatori. Inoltre, hai una maggiore flessibilità lavorando con i buffer dell'indicatore: ora puoi specificare la direzione di indicizzazione desiderata e ottenere esattamente tutti i valori dell'indicatore che desideri. Questo articolo spiega i metodi di base per chiamare gli indicatori e recuperare i dati dai buffer dell'indicatore.
Introduzione a MQL5: Come scrivere un semplice Expert Advisor e un Indicatore Personalizzato Introduzione a MQL5: Come scrivere un semplice Expert Advisor e un Indicatore Personalizzato
Il Linguaggio di Programmazione MetaQuotes 5 (MQL5), incluso nel Client Terminal MetaTrader 5, ha molte nuove possibilità e prestazioni più elevate rispetto a MQL4. Questo articolo ti aiuterà a familiarizzare con questo nuovo linguaggio di programmazione. I semplici esempi di come scrivere un Expert Advisor e un Indicatore Personalizzato vengono presentati in questo articolo. Considereremo anche alcuni dettagli del linguaggio MQL5, necessari per comprendere questi esempi.