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

MetaTrader 5Esempi | 8 dicembre 2021, 17:14
1 731 0
Denis Zyatkevich
Denis Zyatkevich

Introduzione

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.

I dettagli dell'articolo e la descrizione completa di MQL5 possono essere trovati in MQL5 Reference, incluso in MetaTrader 5. Le informazioni contenute nella guida integrata di MQL5 sono sufficienti per studiare il linguaggio. Questo articolo può essere utile per coloro che hanno familiarità con MQL4 e anche per i principianti che si stanno cimentando nella programmazione di sistemi e indicatori di trading.


Iniziare con MQL5

La piattaforma di trading MetaTrader 5 consente di effettuare analisi tecniche di strumenti finanziari e di fare trading, sia manualmente che in modalità automatica. MetaTrader 5 differisce dal suo predecessore - MetaTrader 4. In particolare, sono stati affinati i concetti di deal, posizione e ordine.

  • Posizione - è un impegno di mercato, il numero di contratti di strumenti finanziari acquistati o venduti.
  • Ordine - è un ordine per acquistare o vendere una certa quantità di strumento finanziario a determinate condizioni.
  • Deal - è un fatto di esecuzione di un ordine da parte del broker, che porta all'apertura, alla modifica o alla chiusura di una posizione.

Il Client Terminal ha il linguaggio di programmazioneMQL5 integrato che consente di scrivere diversi tipi di programmi con scopi diversi:

  • Un Expert Advisor - è un programma che opera secondo un algoritmo specificato. Un Expert Advisor ti consente di implementare il sistema di trading per il trading automatizzato (le operazioni di trading possono essere eseguite senza un trader). Un Expert Advisor può eseguire operazioni di trading, aprire e chiudere posizioni e gestire ordini in sospeso.
  • Indicatore - è un programma che permette di presentare i dati in forma grafica, aspetto conveniente per l'analisi.
  • Script - è un programma che consente di eseguire alcune sequenze di operazioni contemporaneamente.

Expert Advisor, Indicatori e Script possono chiamare funzioni della libreria standard MQL5 e funzioni DLL, incluse le librerie del sistema operativo. Le parti di codice che si trovano negli altri file possono essere incluse nel testo del programma scritto in MQL5.

Per scrivere un programma (Expert Advisor, Indicatore o Script), puoi avviare il Client Terminal MetaTrader 5 e scegliere MetaQuotes Language Editor dal menu Tools o semplicemente premere il tasto F4.

Figura 1. Lancio di MetaEditor.

Nella finestra MetaEditor 5 scegli Nuovo dal menu File o premi Ctrl+N.

Figura 2. Creazione di un Nuovo Programma.

Nella finestra MQL5 Wizard scegli il tipo di programma che vuoi creare: 

Figura 3. MQL5 Wizard.

Nel passaggio successivo, è possibile specificare il nome del programma, le informazioni sull'autore e i parametri che verranno richiesti all'utente dopo l'avvio del programma.

Figura 4. Proprietà generali dell’Expert Advisor.

Successivamente, verrà creato il template del programma (Expert Advisor, Indicator o Script) che puoi modificare e compilare con il tuo codice:

Figura 5. Template di un nuovo programma.

Quando il programma è pronto, è necessario compilarlo. Per compilare il programma scegli Compile dal menu File o premi il tasto F7:

Figura 6. Compilazione del programma.

Se non sono presenti errori nel codice del programma, verrà creato il file con estensione .ex5. Dopodiché puoi allegare questo nuovo Expert Advisor, Indicatore o Script al grafico di MetaTrader 5 Client Terminal per l'esecuzione.

Un programma MQL5 è una sequenza di operatori. Ogni operatore termina con un simbolo di punto e virgola ";". Per tua comodità puoi aggiungere commenti al tuo codice che sono posti all'interno dei simboli "/*" e "*/" o dopo "//" alla fine della riga. MQL5 è un linguaggio di programmazione "orientato agli eventi". Ciò significa che al verificarsi di determinati eventi (avvio o chiusura del programma, arrivo di nuova quotazione, ecc.), il Client Terminal avvia la relativa funzione (sottoprogramma), scritta dall'utente, che esegue determinate operazioni. Il Client Terminal ha i seguenti eventi predefiniti:

  • L'evento Start si verifica quando lo Script è in esecuzione (utilizzato solo negli Script). Porta all'esecuzione della funzione OnStart. Equivalente a MQL4: funzione startnegli Script.
  • L'evento Init si verifica quando viene avviato l’Expert Advisor o l’Indicatore. Porta all'esecuzione della funzione OnInit. Equivalente a MQL4 - funzione init.
  • L'evento Deinit si verifica quando l’Expert Advisor o l’Indicatore viene terminato (ad esempio, dopo lo scollegamento dal grafico, la chiusura del Client Terminal, ecc.). Porta all'esecuzione della funzione OnDeinit. Equivalente MQL4 - funzione deinit.
  • L'evento NewTick si verifica quando arriva una nuova quotazione per lo strumento finanziario corrente (utilizzato solo nell’Expert Advisor). Porta all'esecuzione della funzione OnTick. Equivalente MQL4: funzione start negli Expert Advisor.
  • L'evento Calculate si verifica all'avvio dell'indicatore (dopo l'esecuzione della funzione OnInit) e all'arrivo di una nuova quotazione per lo strumento finanziario corrente (utilizzato solo negli indicatori). Porta all'esecuzione della funzione OnCalculate. Equivalente MQL4 - funzione start negli Indicatori.
  • L'evento Trade si verifica quando l'ordine viene eseguito, modificato o cancellato, quando la posizione viene aperta, modificata o chiusa (utilizzata solo negli Expert Advisor). Porta all'esecuzione della funzione OnTrade. In MQL4 t non esiste un equivalente di questo evento e di questa funzione.
  • L'evento BookEvent si verifica quando viene modificata la Profondità del Mercato (utilizzata solo negli Expert Advisor). Porta all'esecuzione della funzione OnBookEvent. In MQL4 t non esiste un equivalente di questo evento e funzione, così come la Profondità del Mercato.
  • L'evento ChartEvent si verifica quando l'utente lavora con il grafico: fa click con il mouse e preme i tasti, quando la finestra del grafico è attiva. Ciò avviene anche durante la creazione, lo spostamento o la cancellazione degli oggetti grafici, ecc. (usati negli Expert Advisor e Indicatori). Porta all'esecuzione della funzione OnChartEvent. Non esiste un equivalente di questo evento e di questa funzione in MQL4.
  • L'evento Timer si verifica periodicamente quando il timer viene attivato, se è stato attivato utilizzando la funzione EventSetTimer. Porta all'esecuzione della funzione OnTimer . In MQL4 t non esiste un equivalente di questo evento e di questa funzione, così come un timer.

Prima di utilizzare le variabili, è necessario specificare il tipo di dati di ciascuna di esse. MQL5 supporta più tipi di dati rispetto a MQL4:

  • bool ha lo scopo di memorizzare valori logici (true o false). Richiede 1 byte di memoria.
  • char ha lo scopo di memorizzare valori interi da -128 a 127. Richiede 1 byte di memoria.
  • uchar ha lo scopo di memorizzare valori interi senza segno da 0 a 255. Richiede 1 byte di memoria.
  • short ha lo scopo di memorizzare valori interi da -32 768 a 32 767. Richiede 2 byte di memoria.
  • ushort ha lo scopo di memorizzare valori interi senza segno da 0 a 65 535. Richiede 2 byte di memoria.
  • int ha lo scopo di memorizzare valori interi da -2 147 483 648 a 2 147 483 647. Richiede 4 byte di memoria.
  • uint ha lo scopo di memorizzare valori interi senza segno da 0 a 4 294 967 295. Richiede 4 byte di memoria.
  • long ha lo scopo di memorizzare valori interi da -9 223 372 036 854 775 808 a 9 223 372 036 854 775 807. Richiede 8 byte di memoria.
  • ulong ha lo scopo di memorizzare valori interi senza segno da 0 a 18 446 744 073 709 551 615. Richiede 8 byte di memoria.
  • float ha lo scopo di memorizzare i valori in virgola mobile. Richiede 4 byte di memoria.
  • double ha lo scopo di memorizzare i valori in virgola mobile. Di solito, viene utilizzato per memorizzare i dati sui prezzi. Richiede 8 byte di memoria.
  • datetime ha lo scopo di memorizzare i valori di data e ora, è un numero di secondi trascorsi da 01.01.1970 00:00:00. Richiede 8 byte di memoria.
  • color ha lo scopo di memorizzare le informazioni sul colore, contiene le caratteristiche di tre componenti di colore: rosso, verde e blu. Richiede 4 byte di memoria.
  • enum sta per enumerazione. Consente di specificare un tipo di un certo insieme limitato di dati. Richiede 4 byte di memoria.
  • string ha lo scopo di memorizzare stringhe di testo. La sua rappresentazione interna è una struttura a 8 byte che contiene la dimensione del buffer con la stringa e il puntatore a quel buffer.

È necessario scegliere il tipo di dati adatto per le prestazioni ottimali e l'uso razionale della memoria. In MQL5 c'è un nuovo concetto chiamato struttura. La struttura combina i dati correlati logicamente.

Sistema di trading

Il sistema di trading, che viene utilizzato in questo articolo come esempio, si basa sul presupposto che gli istituti finanziarie europei siano aperti al mattino e, successivamente, gli eventi economici siano pubblicati negli Stati Uniti, il che porta al trend di EURUSD. Il periodo del grafico non è importante, ma consiglio di usare le barre dei minuti, perché l'intera giornata (o una sua parte) è visibile in una volta, quindi è molto comodo per l'osservazione.

Figura 7. Sistema di trading.

Alle 7:00 (ora del server) gli ordini in sospeso Buy Stop e Sell Stop vengono piazzati a una distanza di un punto oltre il range di prezzo del giorno corrente. Per gli ordini Buy Stop in sospeso viene preso in considerazione lo spread. I livelli di StopLoss sono posizionati sui lati opposti dell'intervallo. Dopo l'esecuzione, l'ordine StopLoss viene spostato alla media mobile semplice, ma solo se è redditizio.

Il vantaggio di questo tipo di trailing rispetto al classico Stop Trailing è il seguente: permette di evitare la chiusura anticipata della posizione in caso di picchi di prezzo con correzioni. D’altro canto, porta alla chiusura della posizione quando il trend finisce e inizia il movimento flat. La media mobile semplice viene calcolata utilizzando i dati del grafico a minuti e ha un periodo di media pari a 240.

Il livello di profitto dipende dall'attuale volatilità del mercato. Per determinare la volatilità del mercato viene utilizzato l'indicatore Average True Range (ATR) (con periodo pari a 5 applicato al grafico giornaliero). Quindi, mostra l'intervallo giornaliero medio dell'ultima settimana. Per determinare il valore del livello Take Profit per la posizione lunga, aggiungeremo il valore dell'indicatore ATR al prezzo minimo del giorno corrente. Lo stesso, per le posizioni short: sottrarremo il valore dell'indicatore ATR dal prezzo massimo del giorno corrente. L'ordine non viene effettuato se il valore del prezzo dell'ordine supera i livelli StopLoss e TakeProfit. Dopo le 19:00 (ora del server) tutti gli ordini in sospeso vengono cancellati e non vengono inseriti in questo giorno (le posizioni aperte sono ancora tracciate fino alla chiusura).


Creare un indicatore

Scriviamo un indicatore che mostri i livelli di profitto del sistema di trading descritto sopra .

Se il primo simbolo in una riga è "#", significa che questa stringa è una direttiva del preprocessore. Le direttive vengono utilizzate per specificare proprietà aggiuntive del programma, per dichiarare costanti, per includere file di intestazione e funzioni importate. Nota che dopo le direttive del preprocessore non ci sono simboli di punto e virgola (;).

#property copyright   "2010, MetaQuotes Software Corp."
#property link        "http://www.mql5.com"
#property description "This indicator calculates TakeProfit levels"
#property description "using the average market volatility. It uses the values"
#property description "of Average True Range (ATR) indicator, calculated"
#property description "on daily price data. Indicator values are calculated"
#property description "using maximal and minimal price values per day."
#property version     "1.00"

Le informazioni sull'autore e sulla sua pagina web possono essere specificate nelle proprietà copyright e link, la proprietà description permette di aggiungere la breve descrizione, la proprietà version permette di specificare la versione del programma. Quando l'indicatore è in esecuzione, queste informazioni hanno il seguente aspetto:

Figura 8. Informazioni sull'indicatore.

È necessario specificare la posizione degli indicatori: su un grafico o in una finestra separata. Può essere fatto specificando una delle proprietà: indicator_chart_window o indicator_separate_window:

#property indicator_chart_window

Inoltre, è necessario specificare il numero di buffer dell'indicatore che verranno utilizzati e il numero di serie di grafici. Nel nostro caso, ci sono due linee, ognuna con il proprio buffer: un array con i dati che verranno tracciati.

#property indicator_buffers 2
#property indicator_plots   2

Per ogni riga dell'indicatore specifichiamo le seguenti proprietà: tipo (indicator_typeproperty), colore (indicator_color property), stile di disegno (indicator_style property) ed etichetta di testo (indicator_label property):

#property indicator_type1   DRAW_LINE
#property indicator_color1  C'127,191,127'
#property indicator_style1  STYLE_SOLID
#property indicator_label1  "Buy TP"
#property indicator_type2   DRAW_LINE
#property indicator_color2  C'191,127,127'
#property indicator_style2  STYLE_SOLID
#property indicator_label2  "Sell TP"

I tipi di linea di base sono: DRAW_LINE - per le linee, DRAW_SECTION - per le sezioni, DRAW_HISTORAM per gli istogrammi. Ci sono molti altri stili di disegno. Puoi definire il colore specificando la luminosità dei suoi tre componenti RGB o utilizzando i colori predefiniti, ad esempio Rosso, Verde, Blu, Bianco, ecc. Gli stili di linea sono: STYLE_SOLID - linea continua, STYLE_DASH - linea tratteggiata, STYLE_DOT - linea punteggiata, STYLE_DASHDOT - linea tratto-punto, STYLE_DASHDOTDOT - trattino-due punti.

Figura 9. Descrizione delle linee dell’indicatore.

Usando il modificatore di input, specifichiamo le variabili esterne (è possibile specificare i loro valori dopo aver lanciato l'indicatore), il loro tipo e i valori predefiniti:

input int             ATRper       = 5;         //ATR Period
input ENUM_TIMEFRAMES ATRtimeframe = PERIOD_D1; //Indicator timeframe

I nomi dei parametri possono essere specificati nei commenti - saranno visibili al posto dei nomi delle variabili:

Figura 10. Parametri di input dell'indicatore.

A livello globale (che è visibile a tutte le funzioni), specificheremo le variabili (e i loro tipi) che verranno utilizzate da diverse funzioni del nostro indicatore.

double bu[],bd[];
int hATR;

Gli array bu[] e bd[] verranno utilizzati per la riga superiore e per quella inferiore dell'indicatore. Useremo array dinamici (cioè gli array senza un numero specificato di elementi), perché non sappiamo esattamente il numero di elementi che verranno utilizzati (la loro dimensione sarà assegnata automaticamente). L'handle dell'indicatore tecnico integrato verrà memorizzato nella variabile hATR. L’handle dell'indicatore è necessario per utilizzare l'indicatore.

La funzione OnInit viene chiamata dopo l'esecuzione dell'indicatore (dopo il suo collegamento al grafico).

void OnInit()
  {
   SetIndexBuffer(0,bu,INDICATOR_DATA);
   SetIndexBuffer(1,bd,INDICATOR_DATA);
   hATR=iATR(NULL,ATRtimeframe,ATRper);
  }

La funzione SetIndexBuffer è necessaria per specificare il fatto che gli array bu[] e bd[] sono i buffer dell'indicatore, che verranno utilizzati per memorizzare i valori degli indicatori, che vengono tracciati come linee di indicatore. Il primo parametro definisce l'indice del buffer dell'indicatore, l'ordine parte da 0. Il secondo parametro specifica un array, assegnato al buffer dell'indicatore. Il terzo parametro specifica il tipo di dati, memorizzati nel buffer dell'indicatore: INDICATOR_DATA - dati per la stampa, INDICATOR_COLOR_INDEX - colore del disegno, INDICATOR_CALCULATIONS - buffer ausiliari per calcoli intermedi.

L'handle dell'indicatore, restituito dalla funzione iATR, è memorizzato nella variabile hATR. Il primo parametro della funzione iATR è il simbolo di trading, NULL- è il simbolo del grafico corrente. Il secondo parametro specifica il periodo del grafico, utilizzato per il calcolo dell'indicatore. Il terzo parametro è il periodo medio dell'indicatore ATR.

La funzione OnCalculate viene chiamata subito dopo la fine dell'esecuzione della funzione OnInit e ogni volta dopo l'arrivo della nuova quotazione per il simbolo corrente. Ci sono due modi per chiamare questa funzione. Uno di questi, quello utilizzato nel nostro indicatore, ha il seguente aspetto:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])

Dopo aver chiamato la funzione OnCalculate, il client terminal passa i seguenti parametri: rates_total - numero di barre sul grafico corrente, prev_calculated  - numero di barre, già calcolato dall'indicatore, time[], open[], high[], low[], close[], tick_volume[], volume[], spread[]- array, contenenti rispettivamente i valori di tempo, apertura, massimo, minimo, chiusura, tick_volume, volume e spread per ciascuna barra. Per ridurre i tempi di calcolo, non è necessario ricalcolare i valori degli indicatori che sono già stati calcolati e non sono stati modificati. Dopo aver chiamato la funzione OnCalculate restituisce il numero di barre che sono già state calcolate.

Il codice della funzione OnCalculate è racchiuso tra parentesi. Inizia con le variabili locali che vengono utilizzate nella funzione - i loro tipi e nomi.

  {
   int i,day_n,day_t;
   double atr[],h_day,l_day;

La variabile i viene utilizzata come contatore di cicli, le variabili day_n e day_t vengono utilizzate per memorizzare il numero di giorni e per memorizzare temporaneamente il numero di giorni, quando si calcolano i valori di prezzo massimo e minimo durante il giorno. L'array atr[] viene utilizzato per memorizzare i valori dell'indicatore ATR e le variabili h_day e l_day vengono utilizzate per memorizzare i valori di prezzo massimo e minimo durante il giorno.

Innanzitutto, dobbiamo copiare i valori dell'indicatore ATR nell'array atr[], utilizzando la funzione CopyBuffer. Useremo l'handle dell'indicatore ATR come primo parametro di questa funzione. Il secondo parametro è il numero dei buffer dell'indicatore (la numerazione parte da 0), l'indicatore ATR ha un solo buffer. Il terzo parametro specifica il numero del primo elemento da cui partire, l'indicizzazione viene eseguita dal presente al passato, l'elemento zero corrisponde alla barra corrente (non completata). Il quarto parametro specifica il numero di elementi che devono essere copiati.

Copiamo due elementi, perché ci interessa solo il penultimo elemento, che corrisponde all'ultima barra (completata). L'ultimo parametro è l'array di destinazione per copiare i dati.

   CopyBuffer(hATR,0,0,2,atr);

La direzione dell'indicizzazione dell'array dipende dal flag AS_SERIES. Se è impostato (cioè uguale a true), l'array viene considerato come serie temporali, l'indicizzazione degli elementi viene eseguita dai dati più recenti a quelli più vecchi. Se non è impostato (cioè uguale a false), gli elementi più vecchi hanno un indice inferiore, gli elementi più recenti hanno un indice maggiore.

   ArraySetAsSeries(atr,true);

Per l'array atr[] impostiamo il flag AS_SERIES su true utilizzando la funzione ArraySetAsSeries (il primo parametro di questa funzione è l'array, per il quale il flag deve essere modificato, il secondo parametro è il nuovo valore del flag). Ora, l'indice della barra corrente (non completata) è uguale a 0, l'indice della penultima barra (completata) è uguale a 1.

L'operatore for permette di creare un loop.

   for(i=prev_calculated;i<rates_total;i++)
     {
      day_t=time[i]/PeriodSeconds(ATRtimeframe);
      if(day_n<day_t)
        {
         day_n=day_t;
         h_day=high[i];
         l_day=low[i];
        }
        else
        {
         if(high[i]>h_day) h_day=high[i];
         if(low[i]<l_day) l_day=low[i];
        }
      bu[i]=l_day+atr[1];
      bd[i]=h_day-atr[1];
     }

Dopo l'operatore for, il primo operatore tra parentesi è un'istruzione: i=prev_calculated. Successivamente, c'è un'espressione, nel nostro caso è: i<rates_total. È una condizione del ciclo: il ciclo viene eseguito finché è vero. La terza è l'istruzione eseguita dopo ogni esecuzione del ciclo. Nel nostro caso, è i++ (è uguale a i=i+1 e significa aumentare la variabile i di 1).

Nel nostro ciclo la variabile i varia dal valore prev_calculated al valore, pari a rates_total-1 con il passaggio 1. Gli array di dati storici (time[], high[]e low[]) non sono le serie temporali per impostazione predefinita, l'indice zero corrisponde alla barra più vecchia della storia, l'ultima corrisponde alla barra non completata corrente. Nel ciclo vengono elaborate tutte le barre dalla prima non calcolata (prev_calculated) all'ultima barra (rates_total-1). Per ognuna di queste barre, calcoliamo i valori del nostro indicatore.

I valori dell'ora nell'array time[] vengono memorizzati come numero di secondi trascorsi da 01.01.1970 00:00:00. Se lo dividiamo per il numero di secondi nel giorno (o in qualche altro periodo), la parte intera del risultato sarà il numero del giorno che inizia dal 01.01.1970 (o qualche altro periodo di tempo). La funzione PeriodSeconds restituisce il numero di secondi nel periodo di tempo, definito come parametro. La variabile day_t è il numero del giorno, corrispondente alla barra con l'indice i. La variabile day_n è il numero del giorno per il quale vengono calcolati i valori di prezzo più alto e più basso.

Consideriamo l'operatore if. Se l'espressione della parentesi di questo operatore è vera, viene eseguito l'operatore che segue la parola chiave if. Se è falso, viene eseguito l'operatore, che segue la parola chiave else. Ogni operatore può essere composto, cioè può essere composto da più operatori, nel nostro caso sono racchiusi tra parentesi.

I valori di prezzo massimo e minimo del giorno elaborato vengono memorizzati rispettivamente nelle variabili h_day e l_day. Nel nostro caso, stiamo verificando la seguente condizione: se la barra analizzata corrisponde al nuovo giorno, ricominciamo a calcolare i valori di prezzo massimo e minimo, altrimenti continuiamo. Stiamo calcolando i valori per ogni linea dell'indicatore: per la linea superiore - stiamo usando il prezzo giornaliero minimo, per la linea inferiore - stiamo usando i valori massimi del prezzo.

Alla fine della funzione OnCalculate l'operatore return restituisce il numero di barre calcolate.

   return(rates_total);
  }

All'interno della funzione DeInit (funziona quando l'indicatore viene rimosso dal grafico o quando il Client Terminal viene chiuso) la memoria, riservata dall'indicatore ATR, viene rilasciata tramite la funzione IndicatorRelease. Questa funzione ha un solo parametro: l'handle dell'indicatore.

void OnDeinit(const int reason)
  {
   IndicatorRelease(hATR);
  }

Ora il nostro indicatore è completo. Per compilarlo, in MetaEditor scegli Compile dal menu File o premi il tasto F7. Se non ci sono errori nel codice, la compilazione andrà a buon fine. I risultati della compilazione vengono stampati nel tab Errors della finestra Toolbox. Nel tuo caso, il compilatore può stampare l'avviso "Conversione possibile perdita di dati" per la seguente stringa:

      day_t=time[i]/PeriodSeconds(ATRtimeframe);

In questa riga stiamo intenzionalmente scartando la parte frazionaria, quindi questa perdita di dati non è un errore.

Quando un indicatore è completo e compilato, può essere allegato ai grafici nel Client Terminal MetaTrader 5 o può essere utilizzato in altri Indicatori, Expert Advisor o Script. Il codice sorgente di questo indicatore è disponibile come allegato in questo articolo.


Scrivere un Expert Advisor

Ora è il momento di scrivere un Expert Advisor che implementi il sistema di trading sopra descritto. Supponiamo che scambierà un solo strumento finanziario. Affinché più Expert Advisor possano operare su uno strumento, è necessario analizzare attentamente il contributo di ciascuno di essi nella posizione complessiva, e questo va oltre lo scopo di questo articolo.

#property copyright   "2010, MetaQuotes Software Corp."
#property version     "1.00"
#property description "This Expert Advisor places the pending orders during the"
#property description "time from StartHour till EndHour on the price levels, that"
#property description "are 1 point below/lower the current trade range."
#property description "The StopLoss levels are placed at the opposite side"
#property description "of the price range. After order execution, the TakeProfit value"
#property description "is set at the 'indicator_TP' level. The StopLoss level is moved"
#property description "to the SMA values only for the profitable orders."

Lo scopo di queste direttive per il preprocessore è stato già considerato nella sezione “Scrivere un Indicatore”. Funzionano allo stesso modo per un Expert Advisor.

Specifichiamo i valori dei parametri di input (che possono essere definiti dall'utente dopo aver avviato un Expert Advisor), i loro tipi e valori predefiniti.

input int    StartHour = 7;
input int    EndHour   = 19;
input int    MAper     = 240;
input double Lots      = 0.1;

I parametri StartHour e EndHour definiscono il periodo di tempo (ora di inizio e di fine) per gli ordini in sospeso. Il parametro MAper definisce il periodo di media della media mobile semplice che viene utilizzato per il livello di StopLoss della posizione aperta durante il suo trailing. Il parametro Lots definisce il volume dello strumento finanziario, utilizzato nel trading.

Specifichiamo le variabili globali che verranno utilizzate nelle diverse funzioni di trading:

int hMA,hCI;

La variabile hMA verrà utilizzata per memorizzare l'handle dell'indicatore MA e la variabile hCI verrà utilizzata per memorizzare l'handle dell'indicatore personalizzato (è un indicatore che è stato scritto sopra).

La funzione OnInit viene eseguita all'avvio dell’Expert Advisor.

void OnInit()
  {
   hMA=iMA(NULL,0,MAper,0,MODE_SMA,PRICE_CLOSE);
   hCI=iCustom(NULL,0,"indicator_TP");
  }

In questa funzione otteniamo le maniglie dell'indicatore MA e il nostro indicatore personalizzato. La funzione iMA e i suoi parametri vengono utilizzati come nella funzione iATR, descritta sopra.

Il primo parametro della funzione iCustom è il nome simbolico dello strumento NULL - significa strumento per il grafico corrente. Il secondo parametro - è l'intervallo del grafico, i cui dati vengono utilizzati per calcolare l'indicatore, 0 - indica il periodo per il grafico corrente. Il terzo parametro è il nome del file dell'indicatore (senza estensione). Il percorso del file è relativo alla cartella MQL5\Indicators\.

Creiamo la funzione OnTick che viene eseguita dopo ogni arrivo di una nuova quotazione:

void OnTick()
  {

Il codice della funzione è racchiuso tra parentesi.

Specifichiamo le strutture di dati predefinite, cosa verrà utilizzato nell’Expert Advisor:

   MqlTradeRequest request;
   MqlTradeResult result;
   MqlDateTime dt;
La struttura predefinita MqlTradeRequest ha parametri di ordini e posizioni che vengono passati alla funzione OrderSend nelle operazioni di trading. Lo scopo della struttura MqlTradeResult è memorizzare le informazioni sui risultati dell'operazione di trading, restituite dalla funzione OrderSend. Lo scopo della struttura predefinita MqlDateTime è memorizzare le informazioni su data e ora.

Specifichiamo le variabili locali (e i loro tipi) che verranno utilizzate nella funzione OnTick:

   bool bord=false, sord=false;
   int i;
   ulong ticket;
   datetime t[];
   double h[], l[], ma[], atr_h[], atr_l[],
          lev_h, lev_l, StopLoss,
          StopLevel=_Point*SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL),
          Spread   =NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK) - SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits);
Le variabili booleane bord e sord vengono utilizzate come flag che mostrano la presenza di ordini Buy Stop e Sell Stop in sospeso. Se è presente, la variabile corrispondente ha valore, uguale a vero, altrimenti è falso. La variabile i viene utilizzata come contatore negli operatori di ciclo e per memorizzare i dati intermedi. Il ticket dell'ordine in sospeso viene memorizzato nella variabile ticket.

Gli array t[], h[] e l[] vengono utilizzati per memorizzare l'ora, i valori di prezzo massimo e minimo per ciascuna barra nei dati della cronologia. L'array ma[] viene utilizzato per memorizzare i valori dell'indicatore MA, gli array atr_h[] e atr_l[] vengono utilizzati per memorizzare i valori delle righe superiore e inferiore dell'indicatore personalizzato indicator_TP che abbiamo creato.

Le variabili lev_h e lev_l vengono utilizzate per memorizzare i valori di prezzo massimo e minimo del giorno corrente e i prezzi di apertura degli ordini in sospeso. La variabile StopLoss viene utilizzata per memorizzare temporaneamente il prezzo Stop Loss della posizione aperta.

La variabile StopLevel viene utilizzata per memorizzare il valore di STOP_LEVEL - la distanza minima tra il prezzo corrente e il prezzo dell'ordine in sospeso (in unità di prezzo). Calcoliamo questo valore come prodotto del prezzo in punti (la variabile predefinita _Point) per il valore della variabile STOP_LEVEL, definita in punti. Il valore di STOP_LEVEL viene restituito dalla funzione SymbolInfoInterger. Il primo parametro di questa funzione è il nome del simbolo, il secondo è l'identificatore della proprietà richiesta. Il simbolo (nome dello strumento finanziario)può essere ottenuto tramite la funzione Symbol (non ha parametri).

Il valore Spread viene utilizzato per memorizzare il valore dello spread (in unità di prezzo). Il suo valore viene calcolato come differenza tra i valori Ask e Bid correnti, normalizzati mediante la funzione NormalizeDouble. Il primo parametro di questa funzione è il valore di tipo double da normalizzare, il secondo è il numero di cifre dopo il punto che abbiamo ottenuto dalla variabile predefinita _Digits. Gli attuali valori Ask e Bid possono essere ottenuti utilizzando la funzione SymbolInfoDouble. Il primo parametro di questa funzione è il nome del simbolo, il secondo è l'identificatore della proprietà.

Riempiamo la richiesta della struttura con i valori, che saranno comuni per la maggior parte delle chiamate alla funzione OrderSend:

   request.symbol      =Symbol();
   request.volume      =Lots;
   request.tp          =0;
   request.deviation   =0;
   request.type_filling=ORDER_FILLING_FOK;
L'elemento request.symbol contiene il nome simbolico dello strumento, che fa trading, l'elemento request.volume - il volume (dimensione del contratto) dello strumento finanziario, request.tp - il valore numerico di TakeProfit (in alcuni casi non lo utilizzeremo e riempire con 0), request.deviation - deviazione consentita del prezzo durante l'esecuzione dell'operazione di trading, request.type_filling - il tipo di ordine, può essere uno dei seguenti:
  • ORDER_FILLING_FOK - il deal può essere eseguito solo se il volume è uguale o migliore di quanto specificato nell'ordine. Se non c'è un volume sufficiente, l'ordine non verrà eseguito.
  • ORDER_FILLING_IOC - non c'è volume sufficiente, l'ordine verrà eseguito al massimo volume di mercato disponibile. 
  • ORDER_FILLING_RETURN - lo stesso dell’ ORDER_FILING_IOC, ma in questo caso un ordine aggiuntivo per volume mancante sarà posizionato.

Otteniamo l'ora corrente del server (ora dell'ultima quotazione) utilizzando la funzione TimeCurrent. L'unico parametro di questa funzione è il puntatore alla struttura con risultato.

   TimeCurrent(dt);

Per tutti i nostri calcoli, abbiamo bisogno dei dati storici sui prezzi solo per il giorno corrente. Il numero di barre necessarie (e con qualche riserva) può essere calcolato utilizzando questa formula: i = (dt.hour + 1)*60, dove dt.hour - è l'elemento della struttura contenente l'ora corrente. I valori di tempo, prezzo massimo e prezzo minimo vengono copiati negli array t[], h[] e l[] utilizzando rispettivamente le funzioni CopyTime, CopyHigh, CopyLow:

   i=(dt.hour+1)*60;
   if(CopyTime(Symbol(),0,0,i,t)<i || CopyHigh(Symbol(),0,0,i,h)<i || CopyLow(Symbol(),0,0,i,l)<i)
     {
      Print("Can't copy timeseries!");
      return;
     }

Il primo parametro nelle Funzioni CopyTime, CopyHigh e CopyLow - è il nome del simbolo, la seconda - è l'intervallo del grafico, la terza - è l'elemento iniziale da copiare, la quarta - è il numero di elementi da copiare, la quinta - è l'array di destinazione per i dati. Ognuna di queste funzioni restituisce il numero di elementi copiati o il valore negativo uguale a -1 in caso di errore.

L'operatore if viene utilizzato per controllare il numero di elementi copiati per tutti e tre gli array. Se il numero di elementi copiati è inferiore a quello necessario per il calcolo (anche per uno degli array), o in caso di errore, stampa il messaggio "Impossibile copiare le serie temporali!" messaggio nel log degli Expert e termina l'esecuzione della funzione OnTick utilizzando l'operatore return. Il messaggio viene stampato dalla funzione Print. Può stampare qualsiasi tipo di dati che sono separati da una virgola.

Quando i dati del prezzo sono stati copiati negli array t[], h[] e l[], impostiamo il flag AS_SERIES su true utilizzando la funzione ArraySetAsSeries che è stata considerata sopra. È necessario impostare l'indicizzazione dell'array come serie temporali (dai prezzi correnti ai prezzi precedenti):

   ArraySetAsSeries(t,true);
   ArraySetAsSeries(h,true);
   ArraySetAsSeries(l,true);

I valori di prezzo massimo e minimo del giorno corrente sono inseriti nelle variabili lev_h e lev_l:

   lev_h=h[0];
   lev_l=l[0];
   for(i=1;i<ArraySize(t) && MathFloor(t[i]/86400)==MathFloor(t[0]/86400);i++)
     {
      if(h[i]>lev_h) lev_h=h[i];
      if(l[i]<lev_l) lev_l=l[i];
     }

Il ciclo viene eseguito solo se la condizione MathFloor(t[i]/86400) == MathFloor(t[0]/86400) è vera per restringere la ricerca alle barre appartenenti al giorno corrente. Sul lato sinistro di questa espressione c'è un numero del giorno corrente della barra, a destra - il numero del giorno corrente (86400 è il numero di secondi del giorno). La funzione MathFloor arrotonda il valore numerico, cioè utilizza una sola parte intera per i valori positivi. L'unico parametro di questa funzione è l'espressione da arrotondare. In MQL5, così come in MQL4, l'uguaglianza è definita con i simboli "==" (vedi Operazioni delle relazioni).

I prezzi dell'ordine sono calcolati come segue: per l'ordine in sospeso di tipo Buy Stop aggiungiamo un punto (la variabile predefinita _Point è uguale alla dimensione del punto in unità di prezzo) e Spread alla variabile lev_h (lev_h+=Spread+_Point o lev_h =lev_h+Spread+_Point). Per gli ordini pendenti di tipo Sell Stop sottraiamo un punto dal valore della variabile lev_l (lev_l-=_Point o lev_l=lev_l-_Point).

   lev_h+=Spread+_Point;
   lev_l-=_Point;

Successivamente, stiamo copiando i valori dai buffer dell'indicatore agli array utilizzando la funzione CopyBuffer. I valori MA vengono copiati nell'array ma[], i valori della riga superiore del nostro indicatore personalizzato vengono copiati nell'array atr_h[], i valori della riga inferiore dell'indicatore vengono copiati nell'array atr_l[]. La funzione CopyBuffer è stata descritta sopra, quando abbiamo considerato i dettagli dell'indicatore.

   if(CopyBuffer(hMA,0,0,2,ma)<2 || CopyBuffer(hCI,0,0,1,atr_h)<1 || CopyBuffer(hCI,1,0,1,atr_l)<1)
     {
      Print("Can't copy indicator buffer!");
      return;
     }

Abbiamo bisogno del valore dell'indicatore MA che corrisponde alla penultima barra (l’ultima completata) e del valore del nostro indicatore che corrisponde all'ultima barra, quindi stiamo copiando questi due elementi nell'array ma[] e un elemento negli arrayatr_h[] e atr_l[]. In caso di errore, durante la copia o se il numero di valori copiati è inferiore al necessario (per uno qualsiasi di questi array), il messaggio viene stampato nel log degli Expert e la funzione OnTick viene terminata utilizzando l'operatore return.

Per l'array ma[] stiamo impostando il flag AS_SERIES che indica l'indicizzazione delle serie temporali dell'array.

   ArraySetAsSeries(ma,true);

Gli array atr_[] e atr_l[] hanno un solo elemento, quindi l'indicizzazione delle serie temporali non è importante. Poiché il valore atr_l[0] verrà utilizzato ulteriormente per determinare il livello TakeProfit, le posizioni corte vengono chiuse al prezzo Ask, ma stiamo aggiungendo lo spread al valore di atr_l[0], perché i prezzi Bid sono utilizzati nei grafici dei prezzi .

   atr_l[0]+=Spread;

La funzione PositionsTotal restituisce il numero di posizioni aperte (non ha parametri). Gli indici delle posizioni partono da 0. Creiamo un ciclo che cercherà tutte le posizioni aperte:

// in this loop we're checking all opened positions
   for(i=0;i<PositionsTotal();i++)
     {
      // processing orders with "our" symbols only
      if(Symbol()==PositionGetSymbol(i))
        {
         // we will change the values of StopLoss and TakeProfit
         request.action=TRADE_ACTION_SLTP;
         // long positions processing
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
           {
            // let's determine StopLoss
            if(ma[1]>PositionGetDouble(POSITION_PRICE_OPEN)) StopLoss=ma[1]; else StopLoss=lev_l;
            // if StopLoss is not defined or lower than needed            
            if((PositionGetDouble(POSITION_SL)==0 || NormalizeDouble(StopLoss-PositionGetDouble(POSITION_SL),_Digits)>0
               // if TakeProfit is not defined or higer than needed
               || PositionGetDouble(POSITION_TP)==0 || NormalizeDouble(PositionGetDouble(POSITION_TP)-atr_h[0],_Digits)>0)
               // is new StopLoss close to the current price?
               && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)-StopLoss-StopLevel,_Digits)>0
               // is new TakeProfit close to the current price?
               && NormalizeDouble(atr_h[0]-SymbolInfoDouble(Symbol(),SYMBOL_BID)-StopLevel,_Digits)>0)
              {
               // putting new value of StopLoss to the structure
               request.sl=NormalizeDouble(StopLoss,_Digits);
               // putting new value of TakeProfit to the structure
               request.tp=NormalizeDouble(atr_h[0],_Digits);
               // sending request to trade server
               OrderSend(request,result);
              }
           }
         // short positions processing
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
           {
            // let's determine the value of StopLoss
            if(ma[1]+Spread<PositionGetDouble(POSITION_PRICE_OPEN)) StopLoss=ma[1]+Spread; else StopLoss=lev_h;
            // if StopLoss is not defined or higher than needed
            if((PositionGetDouble(POSITION_SL)==0 || NormalizeDouble(PositionGetDouble(POSITION_SL)-StopLoss,_Digits)>0
               // if TakeProfit is not defined or lower than needed
               || PositionGetDouble(POSITION_TP)==0 || NormalizeDouble(atr_l[0]-PositionGetDouble(POSITION_TP),_Digits)>0)
               // is new StopLoss close to the current price?
               && NormalizeDouble(StopLoss-SymbolInfoDouble(Symbol(),SYMBOL_ASK)-StopLevel,_Digits)>0
               // is new TakeProfit close to the current price?
               && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK)-atr_l[0]-StopLevel,_Digits)>0)
              {
               // putting new value of StopLoss to the structure
               request.sl=NormalizeDouble(StopLoss,_Digits);
               // putting new value of TakeProfit to the structure
               request.tp=NormalizeDouble(atr_l[0],_Digits);
               // sending request to trade server
               OrderSend(request,result);
              }
           }
         // if there is an opened position, return from here...
         return;
        }
     }
Usando l'operatore if all'interno del loop, scegliamo le posizioni che sono state aperte per il simbolo del grafico corrente. La funzione PositionGetSymbol restituisce il simbolo dello strumento, inoltre, seleziona automaticamente la posizione con cui lavorare. La funzione ha un solo parametro: l'indice della posizione nell'elenco delle posizioni aperte. È molto probabile che sarà necessario modificare i valori StopLoss e TakeProfit delle posizioni aperte, quindi mettiamo il valore TRADE_ACTION_SLTP nell'elemento request.action. Successivamente, a seconda della sua direzione, le posizioni sono divise in lunghe e corte.

Per le posizioni lunghe il livello di StopLoss viene determinato come segue: Se il valore dell'indicatore MA è superiore al prezzo di apertura della posizione, si assume che il valore di StopLoss sia uguale al valore dell'indicatore MA, altrimenti si assume che il valore di StopLoss sia uguale al valore della variabile lev_l. Il valore corrente di StopLoss per la posizione aperta viene determinato utilizzando la funzione PositionGetDouble, che ha un solo parametro: l'identificatore della proprietà della posizione. Se il valore di StopLoss non è definito per la posizione aperta (uguale a 0) o è maggiore di quanto dovrebbe essere, modificheremo i valori di StopLoss e TakeProfit per questa posizione. Se il valore TakeProfit non è definito (uguale a 0) o è superiore a quello che dovrebbe essere (superiore alla linea superiore del nostro indicatore), modificheremo i valori di StopLoss e TakeProfit per questa posizione.

Dobbiamo verificare la possibilità di cambiare i valori StopLoss e TakeProfit. Il nuovo valore di StopLoss deve essere inferiore al prezzo Bid attuale (almeno del valore STOP_LEVEL), il nuovo valore di TakeProfit deve essere maggiore del prezzo Bid attuale almeno del valore STOP_LEVEL. Abbiamo utilizzato la differenza normalizzata per il confronto, poiché i valori confrontati possono differire nelle ultime cifre a causa dell'imprecisione, causata dalla conversione di numeri binari in virgola mobile di tipo double in numeri decimali in virgola mobile.

Se è necessario modificare i livelli di StopLoss o TakeProfit per la posizione aperta e i nuovi valori sono validi secondo le regole di trading, mettiamo i nuovi valori di StopLoss e TakeProfit negli elementi corrispondenti della struttura e chiamiamo la funzione OrderSend per inviare i dati al server trading.

La modifica dei valori StopLoss e TakeProfit per le posizioni corte è la stessa. Le posizioni corte, rispetto alle lunghe, sono chiuse dai prezzi Ask, quindi i valori Ask verranno utilizzati per il confronto. Se c'è una posizione aperta per il grafico corrente, terminiamo l'esecuzione della funzione OnTick utilizzando l'operatore return.

La funzione OrdersTotal (non ha parametri) restituisce il numero di ordini in sospeso. Gli indici iniziano da 0. Creiamo un ciclo che verrà utilizzato per elaborare tutti gli ordini in sospeso:

// in this loop we're checking all pending orders
   for(i=0;i<OrdersTotal();i++)
     {
      // choosing each order and getting its ticket
      ticket=OrderGetTicket(i);
      // processing orders with "our" symbols only
      if(OrderGetString(ORDER_SYMBOL)==Symbol())
        {
         // processing Buy Stop orders
         if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP)
           {
            // check if there is trading time and price movement is possible
            if(dt.hour>=StartHour && dt.hour<EndHour && lev_h<atr_h[0])
              {
               // if the opening price is lower than needed
               if((NormalizeDouble(lev_h-OrderGetDouble(ORDER_PRICE_OPEN),_Digits)>0
                  // if StopLoss is not defined or higher than needed
                  || OrderGetDouble(ORDER_SL)==0 || NormalizeDouble(OrderGetDouble(ORDER_SL)-lev_l,_Digits)!=0)
                  // is opening price close to the current price?
                  && NormalizeDouble(lev_h-SymbolInfoDouble(Symbol(),SYMBOL_ASK)-StopLevel,_Digits)>0)
                 {
                  // pending order parameters will be changed
                  request.action=TRADE_ACTION_MODIFY;
                  // putting the ticket number to the structure
                  request.order=ticket;
                  // putting the new value of opening price to the structure
                  request.price=NormalizeDouble(lev_h,_Digits);
                  // putting new value of StopLoss to the structure
                  request.sl=NormalizeDouble(lev_l,_Digits);
                  // sending request to trade server
                  OrderSend(request,result);
                  // exiting from the OnTick() function
                  return;
                 }
              }
            // if there is no trading time or the average trade range has been passed
            else
              {
               // we will delete this pending order
               request.action=TRADE_ACTION_REMOVE;
               // putting the ticket number to the structure
               request.order=ticket;
               // sending request to trade server
               OrderSend(request,result);
               // exiting from the OnTick() function
               return;
              }
            // setting the flag, that indicates the presence of Buy Stop order
            bord=true;
           }
         // processing Sell Stop orders
         if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
           {
            // check if there is trading time and price movement is possible
            if(dt.hour>=StartHour && dt.hour<EndHour && lev_l>atr_l[0])
              {
               // if the opening price is higher than needed
               if((NormalizeDouble(OrderGetDouble(ORDER_PRICE_OPEN)-lev_l,_Digits)>0
                  // if StopLoss is not defined or lower than need
                  || OrderGetDouble(ORDER_SL)==0 || NormalizeDouble(lev_h-OrderGetDouble(ORDER_SL),_Digits)>0)
                  // is opening price close to the current price?
                  && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)-lev_l-StopLevel,_Digits)>0)
                 {
                  // pending order parameters will be changed
                  request.action=TRADE_ACTION_MODIFY;
                  // putting ticket of modified order to the structure
                  request.order=ticket;
                  // putting new value of the opening price to the structure
                  request.price=NormalizeDouble(lev_l,_Digits);
                  // putting new value of StopLoss to the structure
                  request.sl=NormalizeDouble(lev_h,_Digits);
                  // sending request to trade server
                  OrderSend(request,result);
                  // exiting from the OnTick() function
                  return;
                 }
              }
            // if there is no trading time or the average trade range has been passedе
            else
              {
               // we will delete this pending order
               request.action=TRADE_ACTION_REMOVE;
               // putting the ticket number to the structure
               request.order=ticket;
               // sending request to trade server
               OrderSend(request,result);
               // exiting from the OnTick() function
               return;
              }
            // setting the flag, that indicates the presence of Sell Stop order
            sord=true;
           }
        }
     }
Utilizzando la funzione OrderGetTicket selezioniamo l'ordine per ulteriori lavori con esso e stiamo salvando il ticket dell'ordine nella variabile ticket. Questa funzione ha un solo parametro - indice dell'ordine nella lista degli ordini aperti. La funzione OrderGetString viene utilizzata per ottenere il nome del simbolo. Ha un solo parametro: l'identificatore della proprietà dell'ordine. Stiamo confrontando il nome del simbolo con il nome del grafico corrente, consente di selezionare gli ordini solo per strumento, su cui sta lavorando l’Expert Advisor. Il tipo di ordine è determinato dalla funzione OrderGetInteger con l'identificatore del tipo di ordine corrispondente. Elaboreremo separatamente gli ordini Buy Stop e Sell Stop.

Se l'ora corrente è nell'intervallo da StartHour a EndHour e il prezzo di apertura dell'ordine Buy Stop non supera la linea superiore dell'indicatore, modificheremo il prezzo di apertura e il valore del livello StopLoss (se necessario), altrimenti eliminiamo l'ordine.

Successivamente stiamo determinando, è necessario modificare il prezzo di apertura o il livello di StopLoss per l'ordine in sospeso. Se il prezzo di apertura dell'ordine Buy Stop è inferiore a quello che dovrebbe essere o se StopLoss non è definito o superiore, stiamo inserendo il valore TRADE_ACTION_MODIFY nell'elemento request.action - significa che i parametri dell'ordine in sospeso dovrebbero essere modificati. Inoltre, inseriamo il ticket dell'ordine nell'elemento request.ticket e inviamo la richiesta di scambio al server di trading utilizzando la funzione OrderSend. In confronto, determiniamo il caso in cui il prezzo di apertura è inferiore a quello che dovrebbe essere, perché il valore dello spread può essere variato, ma non modifichiamo l'ordine dopo ogni variazione dello spread, sarà impostato al livello più alto che corrisponde a il valore massimo dello spread.

Anche in confronto stiamo determinando solo il caso, quando il valore di StopLoss è più alto di quanto dovrebbe essere perché il range di prezzo potrebbe espandersi durante il giorno ed è necessario abbassare il valore dell'ordine StopLoss dopo i nuovi minimi del prezzo. Dopo aver inviato la richiesta al server di trading, l'esecuzione della funzione OnTick viene terminata utilizzando l'operatore return. Se sono presenti ordini Buy Stop, il valore della variabile bord è impostato su true.

Gli ordini Sell Stop vengono elaborati allo stesso modo degli ordini Buy Stop.

Ora, posizioniamo gli ordini Buy Stop e Sell Stop in sospeso in caso di loro assenza. Stiamo inserendo il valore TRADE_ACTION_PENDING nell'elemento request.action (significa che l'ordine in sospeso è stato inserito).

   request.action=TRADE_ACTION_PENDING;

Se il valore dell'ora corrente rientra nell'orario di inserimento dell'ordine, stiamo effettuando gli ordini:

   if(dt.hour>=StartHour && dt.hour<EndHour)
     {
      if(bord==false && lev_h<atr_h[0])
        {
         request.price=NormalizeDouble(lev_h,_Digits);
         request.sl=NormalizeDouble(lev_l,_Digits);
         request.type=ORDER_TYPE_BUY_STOP;
         OrderSend(request,result);
        }
      if(sord==false && lev_l>atr_l[0])
        {
         request.price=NormalizeDouble(lev_l,_Digits);
         request.sl=NormalizeDouble(lev_h,_Digits);
         request.type=ORDER_TYPE_SELL_STOP;
         OrderSend(request,result);
        }
     }
  }
Durante l'immissione di ordini Buy Stop e Sell Stop, controlliamo la presenza degli stessi ordini analizzando i valori delle variabili bord e sord. Inoltre, stiamo verificando la seguente condizione: il prezzo dell'ordine dovrebbe essere all'interno dei valori del nostro indicatore. Il prezzo normalizzato dell'ordine viene inserito nell'elemento request.price, il valore normalizzato di StopLoss viene inserito nella variabile request.sl, il tipo di ordine (ORDER_BUY_STOP o ORDER_SELL_STOP) viene inserito nella variabile request.type. Dopodiché, stiamo inviando la richiesta al server di trading. Il codice della funzione OnTick termina con un punto e virgola.

Le risorse, allocate per indicatori, vengono rilasciate all'interno della funzione OnDeinit tramite la funzione IndicatorRelease considerata sopra.

void OnDeinit(const int reason)
  {
   IndicatorRelease(hCI);
   IndicatorRelease(hMA);
  }

L’Expert Advisor è completo, la compilazione dovrebbe avere successo se non ci sono errori. Ora possiamo eseguirlo collegandoci al grafico. Il codice sorgente può essere scaricato negli allegati a questo articolo.


Avvio e Debug

Quando gli Expert Advisor e l’Indicatore sono pronti, consideriamo come avviarli e come eseguirne il debug utilizzando il debugger MetaEditor integrato.

Per avviare un Expert Advisor, è necessario trovarlo nel gruppo Expert Advisor della finestra del Navigatore. Dopodiché scegli Allega al grafico dal menu di scelta rapida, che apparirà quando fai click con il pulsante destro del mouse:

Figura 11. Avvio dell’Expert Advisor.

Apparirà la finestra con i parametri di input dell’Expert Advisor, è possibile modificare questi parametri se necessario. Dopo aver premuto OK, l'icona apparirà nell'angolo in alto a destra del grafico, questa icona indica che l’Expert Advisor sta funzionando. Per mostrare o chiudere la finestra del Navigatore puoi scegliere Navigatore dal menu View o premere Ctrl+N. Il secondo modo per avviare l'Expert Advisor è selezionarlo nel sottomenu Experts del menu Insert

Affinché l’Expert Advisor sia in grado di fare trading, AutoTrading dovrebbe essere consentito nelle opzioni del Client Terminal: Menu Tools -> finestra Options -> tab Expert Advisors -> l’opzione Consenti AutoTrading dovrebbe essere abilitata. Affinché l’Expert Advisor sia in grado di chiamare funzioni dalle DLL, anche l'opzione Consenti importazione DLL deve essere abilitata.

Figura 12. Opzioni del terminale - consentendo AutoTrading.

Inoltre, è possibile impostare l'autorizzazione o il divieto di trading e importazione delle librerie DLL esterne per ciascun Expert Advisor singolarmente selezionando le opzioni corrispondenti.

Nonostante il fatto che il nostro Expert Advisor utilizzi indicatori, le linee degli indicatori non vengono tracciate sul grafico. Se necessario, puoi collegare manualmente gli indicatori.

L'avvio degli indicatori è lo stesso degli Expert Advisor: se desideri avviare gli indicatori integrati, espandere l'albero degli Indicatori nella finestra del Navigatore (per gli indicatori personalizzati è necessario espandere l'albero degli Indicatori Personalizzati), fare click con il pulsante destro del mouse per far comparire il menu a comparsa, quindi scegli Allega al Grafico dal menu di scelta rapida. Il secondo modo è scegliere Indicatori dal menu Insert scegliere il gruppo (o scegliere Personalizzato per indicatori personalizzati) e l'indicatore stesso.

Gli script vengono lanciati allo stesso modo degli Expert Advisor e degli Indicatori.

Le informazioni sugli eventi del Client Terminal (connessione/disconnessione al/dal server di trading, aggiornamento automatico, modifiche di posizioni e ordini, Expert Advisor e script in esecuzione, messaggi di errore) possono essere trovati nel tab Journal della finestra Toolbox. I messaggi stampati dall’Expert Advisor, Indicatori e Script si trovano nel tab Experts.

Il MetaEditor ha un debugger integrato. Ti consente di eseguire il debug dei programmi: l'esecuzione step-by-step dell’Expert Advisor, Indicatori e Script. Il debug aiuta a trovare errori nel codice del programma e ad osservare i processi durante l'esecuzione dell’Expert Advisor, Indicatore o Script. Per eseguire un programma in modalità debug, è necessario scegliere Start dal menu Debug o premere il tasto F5. Il programma verrà compilato e verrà eseguito in modalità debug nel grafico separato, il suo periodo e il suo simbolo possono essere specificati nel tab Debugging della finestra Opzioni di MetaEditor.

Figura 13. Opzioni dell'editor - Debug.

È possibile impostare i breakpoint premendo il tasto F9o facendo doppio click sul lato sinistro della linea o scegliendo il Toggle Breakpoint dalla finestra di Debug. In modalità debug, l'esecuzione del programma si fermerà prima dell'operatore con breakpoint. Dopo l'interruzione, il tabDebug comparirà nella finestra Toolbox (Figura 14.). Sul lato sinistro, c'è un pannello dello stack di chiamate - lì vengono visualizzati file, funzione e numero di una riga. Sul lato destro, c'è il pannello di controllo - i valori delle variabili controllate sono visualizzati lì. Per aggiungere la variabile all'elenco di controllo, fare click con il pulsante destro del mouse sul pannello e selezionare Aggiungi o premere il tasto Inserisci.

Figure 14. Debug del Programma.

L'esecuzione del programma step-by-step può essere eseguita premendo i tasti F11, F10 o Shift+F11 . Dopo aver premuto il tasto F11 o aver scelto Step Into dal menu Debug, passerà un passaggio dell'esecuzione del programma inserendo tutte le funzioni chiamate. Dopo aver premuto il tasto F10 o aver scelto Step Over dal menu Debug, passerà un passaggio dell'esecuzione del programma senza inserire Funzioni chiamate. Dopo aver premuto i tasti Shift+F11 o aver scelto Step Out dal menu Debug, eseguirà l'esecuzione di un passaggio del programma di un livello superiore. La freccia verde a sinistra del codice indica la riga del codice che verrà eseguita.


Conclusione

Nell'articolo, viene presentato un esempio di scrittura di un semplice Expert Advisor e Indicatore e vengono descritte le basi del linguaggio di programmazione MQL5. Il sistema di trading qui fornito è scelto come esempio, quindi l'autore non è responsabile per il suo utilizzo in un trading reale.


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

File allegati |
indicator_tp.mq5 (2.17 KB)
expert.mq5 (11.3 KB)
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.
MQL5 per Principianti Guida all'Utilizzo degli Indicatori Tecnici negli Expert Advisor MQL5 per Principianti Guida all'Utilizzo degli Indicatori Tecnici negli Expert Advisor
Per ottenere i valori di un indicatore integrato o personalizzato in un Expert Advisor, innanzitutto il suo handle deve essere creato utilizzando la funzione corrispondente. Gli esempi nell'articolo mostrano come utilizzare questo o quell'indicatore tecnico durante la creazione dei propri programmi. L'articolo descrive gli indicatori creati nel linguaggio MQL5. È destinato a coloro che non hanno molta esperienza nello sviluppo di strategie di trading e offre modi semplici e chiari di lavorare con gli indicatori utilizzando la libreria di funzioni offerta.
Indicatori Personalizzati in MQL5 per Principianti Indicatori Personalizzati in MQL5 per Principianti
Qualsiasi nuovo argomento sembra complicato e difficile da imparare per un principiante. Gli argomenti che conosciamo ci sembrano molto semplici e chiari. Ma semplicemente non ricordiamo che tutti devono studiare qualcosa da zero, persino la nostra lingua madre. Lo stesso avviene con il linguaggio di programmazione MQL5 che offre ampie possibilità di sviluppare le proprie strategie di trading: puoi iniziare a impararlo dalle nozioni di base e dagli esempi più semplici. L'interazione di un indicatore tecnico con il client terminal MetaTrader 5 viene considerata in questo articolo sull'esempio del semplice indicatore personalizzato SMA.
L'Ordine di Creazione e Distruzione dell'oggetto in MQL5 L'Ordine di Creazione e Distruzione dell'oggetto in MQL5
Ogni oggetto, sia esso un oggetto personalizzato, un array dinamico o un array di oggetti, viene creato ed eliminato nel programma MQL5 nel suo modo particolare. Spesso, alcuni oggetti fanno parte di altri oggetti e l'ordine di eliminazione dell'oggetto durante la reinizializzazione diventa particolarmente importante. Questo articolo fornisce alcuni esempi che illustrano i meccanismi di utilizzo degli oggetti.