Il Moving Average EA è incluso nel pacchetto standard del MetaTrader 5 e rappresenta un esempio di Expert Advisor che opera utilizzando l'indicatore Media Mobile.
Il file Moving Average.mq5 si trova nella cartella "terminal_data_folder\MQL5\Experts\Examples\Moving Average". Questo EA è un esempio di utilizzo degli indicatori tecnici, delle funzioni di storia delle operazioni e delle classi di trading della Standard Library. Inoltre, l'EA include un sistema di gestione del denaro basato sui risultati delle operazioni.
Vediamo quindi la struttura dell'Expert Advisor e come funziona.
1. Proprietà dell'EA
//+------------------------------------------------------------------+ //| Moving Averages.mq5 | //| Copyright 2009-2013, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2009-2013, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00"
Le prime 5 righe contengono un commento, le tre righe successive impostano le proprietà del programma MQL5 (copyright, link, versione) utilizzando le direttive preprocessor #property.
Quando esegui l'Expert Advisor, queste informazioni vengono visualizzate nella scheda "Comune":

Figura 1. Parametri comuni dell'EA Media Mobile
1.2. File Inclusi
Successivamente, la direttiva #include dice al compilatore di includere il file "Trade.mqh".
Questo file è parte della Standard Library, contiene la classe CTrade per un facile accesso alle funzioni di trading.
#include <Trade\Trade.mqh>Il nome del file incluso è mostrato tra parentesi "<>;", quindi il percorso è impostato rispetto alla directory: "terminal_data_folder\Include".
1.3 Input
Successivamente vengono specificati il tipo, il nome, i valori predefiniti e un commento. Il loro ruolo è mostrato nella fig. 2.
input double MaximumRisk = 0.02; // Rischio massimo in percentuale input double DecreaseFactor = 3; // Fattore di diminuzione input int MovingPeriod = 12; // Periodo della Media Mobile input int MovingShift = 6 // Spostamento della Media Mobile
I parametri MaximumRisk e DecreaseFactor saranno utilizzati per la gestione del denaro, MovingPeriod e MovingShift impostano il periodo e lo spostamento dell'indicatore Media Mobile che verrà utilizzato per verificare le condizioni di trading.
Il testo nel commento della riga del parametro di input, insieme ai valori predefiniti, viene visualizzato nella scheda "Opzioni" anziché il nome del parametro di input:

Fig. 2. Parametri di input dell'EA Media Mobile
1.4. Variabili Globali
Successivamente viene dichiarata la variabile globale ExtHandle. Sarà utilizzata per memorizzare il gestore dell'indicatore Media Mobile.
//--- int ExtHandle=0;
Segue un elenco di 6 funzioni. Lo scopo di ciascuna di esse è descritto nel commento prima del corpo della funzione:
- TradeSizeOptimized() - Calcola la dimensione ottimale del lotto;
- CheckForOpen() - Verifica le condizioni per l'apertura della posizione;
- CheckForClose() - Verifica le condizioni per la chiusura della posizione;
- OnInit() - Funzione di inizializzazione dell'Expert;
- OnTick() - Funzione tick dell'Expert;
- OnDeinit() - Funzione di de-inizializzazione dell'Expert;
Le ultime tre funzioni sono funzioni di gestione eventi; le prime tre funzioni di servizio vengono chiamate nel loro codice.
2. Funzioni di Gestione Eventi
2.1. Funzione di Inizializzazione OnInit()
La funzione OnInit() viene chiamata una sola volta durante il primo avvio dell'Expert Advisor. Di solito, nel gestore di eventi OnInit() l'EA viene preparato per l'operazione: i parametri di input vengono controllati, gli indicatori e i parametri vengono inizializzati, ecc. In caso di errori critici, quando il lavoro successivo è privo di significato, la funzione viene terminata con un codice di ritorno INIT_FAILED.
//+------------------------------------------------------------------+ //| Funzione di inizializzazione dell'Expert | //+------------------------------------------------------------------+ int OnInit(void) { //--- ExtHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE); if(ExtHandle==INVALID_HANDLE) { printf("Errore nella creazione dell'indicatore MA"); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); }
Poiché il trading dell'EA è basato sull'indicatore Media Mobile, chiamando iMA() viene creato l'indicatore Media Mobile e il suo gestore viene salvato nella variabile globale ExtHandle.
In caso di errore, OnInit() termina con un codice di ritorno INIT_FAILED - è un modo corretto per completare l'operazione dell'EA/indicatore in caso di inizializzazione non riuscita.
2.2. Funzione OnTick()
La funzione OnTick() viene chiamata ogni volta che viene ricevuta una nuova quotazione per il simbolo del grafico su cui l'EA viene eseguito.
//+------------------------------------------------------------------+ //| Funzione tick dell'Expert | //+------------------------------------------------------------------+ void OnTick(void) { //--- if(PositionSelect(_Symbol)) CheckForClose(); else CheckForOpen(); //--- }
La funzione PositionSelect() viene utilizzata per definire se c'è una posizione aperta per il simbolo corrente.
Se ci sono posizioni aperte, viene chiamata la funzione CheckForClose(), che analizza lo stato attuale del mercato e chiude la posizione aperta, altrimenti viene chiamata CheckForOpen(), che verifica le condizioni di ingresso nel mercato e apre una nuova posizione se tali condizioni si verificano.
2.3. Funzione OnDeInit()
OnDeInit() viene chiamata quando un EA viene rimosso dal grafico. Se un programma posiziona oggetti grafici durante il funzionamento, possono essere rimossi dal grafico.
//+------------------------------------------------------------------+ //| Funzione di de-inizializzazione dell'Expert | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+
In questo caso non vengono eseguite azioni durante la de-inizializzazione dell'Expert Advisor.
3. Funzioni di Servizio
3.1. Funzione TradeSizeOptimized()
Questa funzione calcola e restituisce il valore della dimensione ottimale del lotto per l'apertura della posizione con il livello di rischio specificato e i risultati del trading.
//+------------------------------------------------------------------+ //| Calcola la dimensione ottimale del lotto | //+------------------------------------------------------------------+ double TradeSizeOptimized(void) { double price=0.0; double margin=0.0; //--- Calcola la dimensione del lotto if(!SymbolInfoDouble(_Symbol,SYMBOL_ASK,price)) return(0.0); if(!OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,1.0,price,margin)) return(0.0); if(margin<=0.0) return(0.0); double lot=NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)*MaximumRisk/margin,2); //--- calcola la lunghezza della serie di trade perdenti consecutivi if(DecreaseFactor>0) { //--- richiede l'intera storia delle operazioni HistorySelect(0,TimeCurrent()); //-- int orders=HistoryDealsTotal(); // il numero totale di operazioni int losses=0 // il numero di operazioni in perdita nella serie for(int i=orders-1;i>=0;i--) { ulong ticket=HistoryDealGetTicket(i); if(ticket==0) { Print("HistoryDealGetTicket fallito, nessuna storia di trading"); break; } //--- verifica il simbolo dell'operazione if(HistoryDealGetString(ticket,DEAL_SYMBOL)!=_Symbol) continue; //--- verifica il profitto double profit=HistoryDealGetDouble(ticket,DEAL_PROFIT); if(profit>0.0) break; if(profit<0.0) losses++; } //--- if(losses>1) lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1); } //--- normalizza e verifica i valori consentiti del volume di trading double stepvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); lot=stepvol*NormalizeDouble(lot/stepvol,0); double minvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); if(lot<minvol) lot=minvol; double maxvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); if(lot>maxvol) lot=maxvol; //--- restituisce il valore del volume di trading return(lot); }
La funzione SymbolInfoDouble() viene utilizzata per controllare la disponibilità dei prezzi per il simbolo corrente, successivamente la funzione OrderCalcMargin() viene utilizzata per richiedere il margine necessario per piazzare un ordine (in questo caso un ordine di acquisto). La dimensione iniziale del lotto è determinata dal valore del margine necessario per piazzare un ordine, il margine libero dell'account (AccountInfoDouble(ACCOUNT_FREEMARGIN)) e il valore massimo di rischio consentito specificato nel parametro di input MaximumRisk.
Se il valore del parametro di input DecreaseFactor è positivo, le operazioni nella storia vengono analizzate e la dimensione del lotto viene regolata tenendo conto delle informazioni sulla massima serie di operazioni perdenti: la dimensione iniziale del lotto viene moltiplicata per il valore (1-losses/DecreaseFactor).
Successivamente, il volume di trading viene "arrotondato" al valore che è multiplo del passo minimo consentito del volume (stepvol) per il simbolo corrente. Vengono richiesti anche i valori minimi (minvol) e massimi (maxvol) del volume di trading e, se il valore del lotto esce dai limiti consentiti, viene regolato. Di conseguenza, la funzione restituisce il valore calcolato del volume di trading.
3.2. Funzione CheckForOpen()
CheckForOpen() viene utilizzata per verificare le condizioni di apertura della posizione e la apre quando si verificano le condizioni di trading (in questo caso quando il prezzo attraversa la media mobile).
//+------------------------------------------------------------------+ //| Verifica le condizioni per l'apertura della posizione | //+------------------------------------------------------------------+ void CheckForOpen(void) { MqlRates rt[2]; //--- copia i valori dei prezzi if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates di ",_Symbol," fallita, nessuna storia"); return; } //--- Esegui trading solo sul primo tick della nuova candela if(rt[1].tick_volume>1) return; //--- Ottieni il valore attuale dell'indicatore Media Mobile double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer da iMA fallita, nessun dato"); return; } //--- controlla i segnali ENUM_ORDER_TYPE signal=WRONG_VALUE; if(rt[0].open>ma[0] && rt[0].close<ma[0]) signal=ORDER_TYPE_SELL // condizione di vendita else { if(rt[0].open<ma[0] && rt[0].close>ma[0]) signal=ORDER_TYPE_BUY // condizione di acquisto } //--- controlli aggiuntivi if(signal!=WRONG_VALUE) if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) if(Bars(_Symbol,_Period)>100) { CTrade trade; trade.PositionOpen(_Symbol,signal,TradeSizeOptimized(), SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK), 0,0); } //--- }
Quando si opera utilizzando la media mobile, è necessario controllare se il prezzo attraversa la media mobile. Utilizzando la funzione CopyRates(), vengono copiati due valori dei prezzi attuali nell'array di strutture rt[], rt[1] corrisponde alla candela corrente, rt[0] - candela completata.
Una nuova candela viene avviata controllando il volume dei tick della candela corrente; se è uguale a 1, allora è iniziata una nuova candela. Va notato che questo metodo di rilevamento di una nuova candela potrebbe fallire in alcuni casi (quando le quotazioni arrivano a pacchetti), quindi il fatto di avvio della formazione di una nuova candela dovrebbe essere effettuato salvando e confrontando il tempo della quotazione corrente (vedi IsNewBar).
Il valore attuale dell'indicatore Media Mobile viene richiesto utilizzando la funzione CopyBuffer() e viene salvato nell'array ma[] che contiene solo un valore. Il programma quindi verifica se il prezzo ha attraversato la media mobile e fa controlli aggiuntivi (se il trading tramite l'EA è possibile e la presenza di barre nella storia). Se ha successo, una posizione appropriata per il simbolo viene aperta chiamando il metodo PositionOpen() dell'oggetto di trading (un'istanza di CTrade).
Il prezzo di apertura della posizione è impostato utilizzando la funzione SymbolInfoDouble() che restituisce il prezzo Bid o Ask a seconda del valore della variabile signal. Il volume della posizione è determinato chiamando TradeSizeOptimized() descritto sopra.
3.3. Funzione CheckForClose()
CheckForClose() controlla le condizioni per la chiusura della posizione e la chiude se si verificano le condizioni per chiuderla.
//+------------------------------------------------------------------+ //| Verifica le condizioni per chiudere la posizione | //+------------------------------------------------------------------+ void CheckForClose(void) { MqlRates rt[2]; //--- Copia i valori dei prezzi if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates di ",_Symbol," fallita, nessuna storia"); return; } //--- Esegui trading solo sul primo tick della nuova candela if(rt[1].tick_volume>1) return; //--- Ottieni il valore attuale dell'indicatore Media Mobile double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer da iMA fallita, nessun dato"); return; } //--- ottieni il tipo della posizione selezionata in precedenza utilizzando PositionSelect() bool signal=false; long type=PositionGetInteger(POSITION_TYPE); if(type==(long)POSITION_TYPE_BUY && rt[0].open>ma[0] && rt[0].close<ma[0]) signal=true; if(type==(long)POSITION_TYPE_SELL && rt[0].open<ma[0] && rt[0].close>ma[0]) signal=true; //--- controlli aggiuntivi if(signal) if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) if(Bars(_Symbol,_Period)>100) { CTrade trade; trade.PositionClose(_Symbol,3); } //--- }
L'algoritmo della funzione CheckForClose() è simile all'algoritmo di CheckForOpen(). A seconda della direzione delle posizioni aperte attualmente, vengono verificate le condizioni per la loro chiusura (attraversamento del prezzo della MA verso il basso per l'acquisto o verso l'alto per la vendita). Una posizione aperta viene chiusa chiamando il metodo PositionClose() dell'oggetto di trading (istanza di CTrade).
4. Backtesting
I migliori valori dei parametri possono essere trovati utilizzando il Strategy Tester del terminale MetaTrader 5.
Ad esempio, ottimizzando il parametro MovingPeriod nell'intervallo 2012.01.01-2013.08.01, i migliori risultati si ottengono con MovingPeriod=45:

Risultati del backtesting dell'Expert Advisor Media Mobile
Conclusioni:
L'Expert Advisor Media Mobile incluso nel pacchetto standard del MetaTrader 5 è un esempio di utilizzo di indicatori tecnici, delle funzioni di storia delle operazioni e delle classi di trading della Standard Library. Inoltre, l'EA include un sistema di gestione del denaro basato sui risultati delle operazioni.
Post correlati
- Rilevare il Venerdì della Prima Settimana del Mese: La Guida per il Giorno NFP
- MQL5 Wizard: Creare Trading Signals con Morning/Evening Stars e MFI per MetaTrader 5
- Utilizzare MQL5 Wizard per generare segnali di trading: Hammer e Hanging Man con CCI
- MQL5 Wizard: Strategia di Trading con Dark Cloud Cover/Piercing Line e RSI
- MQL5 Wizard: Creare Trade Signals con Hammer/Hanging Man e RSI