
Guida Step-By-Step alla scrittura di un Expert Advisor in MQL5 per Principianti
Introduzione
Questo articolo è rivolto ai principianti che desiderano imparare a scrivere semplici Expert Advisor nel nuovo linguaggio MQL5. In primo luogo, inizieremo prima stabilendo cosa vogliamo che faccia il nostro EA (Expert advisor), per poi passare a come vogliamo che l'EA lo faccia.
1. Strategia di Trading
Cosa farà il nostro EA:
- Monitorerà un particolare indicatore e, quando una certa condizione viene soddisfatta (o determinate condizioni sono soddisfatte), piazzerà uno scambio (Short/Sell Long/Buy), a seconda della condizione attuale che è stata soddisfatta.
Ciò che abbiamo descritto sopra viene chiamato strategia di trading. Prima di poter scrivere un EA, è necessario innanzitutto sviluppare la strategia che desideri da automatizzare nell'EA. Quindi, in questo caso, modifichiamo l'affermazione di cui sopra in modo che rifletta la strategia che vogliamo sviluppare in un EA.
-
Useremo un indicatore chiamato Media Mobile con un periodo di 8 (puoi scegliere qualsiasi periodo, ma ai fini della nostra strategia, useremo 8)
- Vogliamo che il nostro EA effettui un trade Long (Buy) quando la Media Mobile-8 (ai fini della nostra discussione, mi riferirò ad essa come MA-8) sta aumentando verso l'alto e il prezzo è poco al di sopra di questa e posizionerà uno Short (Sell) quando MA-8 sta diminuendo verso il basso e il prezzo è poco al di sotto di essa.
- Useremo anche un altro indicatore chiamato Movimento direzionale medio (ADX) con il periodo 8 anche per aiutarci a determinare se il mercato è crescita oppure no. Stiamo facendo questo perché vogliamo aprire una posizione solo quando il mercato segue un trend e rilassarci quando il mercato si muove all’interno di un range definito (cioè, non in crescita). Per raggiungere questo obiettivo, piazzeremo il nostro trade (Buy o Sell) solo quando le condizioni di cui sopra sono soddisfatte e il valore ADX è maggiore di 22. Se ADX è maggiore di 22 ma decrescente, o ADX è inferiore a 22, non effettueremo operazioni di trading, anche se la condizione B è stata soddisfatta.
- Inoltre, vogliamo proteggerci impostando uno Stop loss di 30 pip e per il nostro obiettivo di Profitto; punteremo a un profitto di 100 pip.
- Vogliamo anche che il nostro EA cerchi opportunità di Acquisto/Vendita solo quando è stata formata una nuova barra e ci assicureremo anche di aprire una posizione di Acquisto se le condizioni di Acquisto sono soddisfatte e non ne abbiamo già una, nonché aprire una posizione di Vendita quando le condizioni di Vendita sono soddisfatte e non ne abbiamo già una aperta.
Ora, abbiamo sviluppato la nostra strategia; adesso è il momento di iniziare a scrivere il nostro codice.
2. Scrivere un Expert Advisor
2.1 MQL5 Wizard
Inizia avviando MetaQuotes Language Editor 5. Quindi premere Ctrl+No cliccare sul pulsante Nuovo nella barra del Menu
Figura 1. Avvio di un nuovo documento MQL5
Nella finestra di MQL5 Wizard, selezionare Expert Advisor e cliccare su "Avanti" come mostrato in Fig. 2:
Figura 2. Selezione del tipo di programma
Nella finestra successiva, digita il Nome che vuoi dare al tuo EA nella casella Nome. In questo caso, ho digitato My_First_EA. Quindi, puoi digitare il tuo nome nella casella Autore e anche l'indirizzo del sito Web o indirizzo e-mail nella casella Link (se ne possiedi uno).
Figura 3. Proprietà generali dell'Expert Advisor
Dal momento che vogliamo essere in grado di cambiare alcuni dei parametri per il nostro EA per vedere quale valore può darci il miglior risultato, li aggiungeremo cliccando sul pulsante "Aggiungi".
Figura 4. Impostazione dei parametri di Input dell’EA
Nel nostro EA, vogliamo essere in grado di sperimentare con i nostri Stop Loss, Take Profit, ADX Period e Impostazioni del Periodo della Media Mobile, quindi le definiremo a questo punto.
Fare doppio click sotto la sezione Nome e digitare il nome del parametro, quindi fare doppio click su Tipo per Selezionare il tipo di dati per il parametro, fare doppio click nella sezione Valore iniziale e digitare il valore iniziale per il parametro.
Una volta fatto, dovresti vedere qualcosa di simile:
Figura 5. Tipi di dati dell’ EA parametri di Input dell’EA
Come puoi vedere sopra, ho selezionato il tipo di dati integer (int) per tutti i parametri. Parliamo un po' di tipi di dati.
- char: Il Il tipochar occupa 1 byte di memoria (8 bit) e consente di esprimere nella notazione binaria 2^8=256 valori. Il tipo di carattere può contenere entrambi i valori positivi e negativi. L'intervallo di valori va da -128 a 127.
- uchar: L’uchar il tipo integer occupa anche 1 byte di memoria, così come il tipo char, ma a differenza sua uchar è inteso solo per valori positivi. Il valore minimo è zero, il valore massimo è 255. La prima lettera u nel nome del tipouchar è l'abbreviazione diunsigned.
- short: La dimensione del tiposhort è di 2 byte (16 bit) e, di conseguenza, consente di esprimere l'intervallo di valori pari a 2 alla potenza 16: 2^16 = 65 536. Poiché il tipo short è un segno uno, e contiene sia valori positivi che negativi, l'intervallo di valori è compreso tra -32 768 e 32 767.
- ushort: Il tipo unsigned short è il tipo ushortche ha anche una dimensione di 2 byte. Il valore minimo è 0, il valore massimo è 65 535.
- int: La dimensione del tipoint è di 4 byte (32 bit). Il valore minimo è di -2 147 483 648, quello massimo è 2 147 483 647.
- uint: Il tipo unsigned integer è uint. Richiede 4 byte di memoria e consente esprimere numeri interi da 0 a 4 294 967 295.
- long: La dimensione del tipo long è di 8 byte (64 bit). Il valore minimo è -9 223 372 036 854 775 808, il valore massimo è 9 223 372 036 854 775 807.
- ulong: Il tipoulong occupa anche 8 byte e può memorizzare valori da 0 a 18 446 744 073 709 551 615.
Dalla descrizione precedente dei vari tipi di dati, i tipi integer senza segno non sono progettati per memorizzare valori negativi, qualsiasi tentativo di impostare un valore negativo può portare a conseguenze inaspettate. Ad esempio, se si desidera memorizzare valori negativi, non è possibile memorizzarli all'interno dei tipi unsigned (ad es. uchar, uint, ushort, ulong).
Torniamo al nostro EA. Esaminando i tipi di dati, sarai d'accordo con me sul fatto che supponiamo di utilizzare i tipi di dati char o uchar poiché i dati che intendiamo archiviare in questi parametri sono rispettivamente inferiori a 127 o 255. Per una buona gestione della memoria, questa è la cosa migliore da fare. Tuttavia, ai fini della nostra discussione, ci atterremo ancora d attenerci al tipo int.
Una volta che hai finito di configurare tutti i parametri necessari, cliccare sul pulsante Finito e l'Editor MetaQuotes creerà per te lo scheletro del codice come mostrato nella figura successiva.
Spezziamo il codice nelle varie sezioni per una migliore comprensione.
La parte superiore (Intestazione) del codice è dove viene definita la proprietà dell'EA. Puoi vedere che qui ci sono i valori compilati in MQL5 Wizard nella figura 3.
In questa sezione del codice, puoi definire parametri aggiuntivi come ladescrizione(breve descrizione testuale dell'EA), dichiarare costanti, includere file aggiuntivi o funzioni di importazione.
Quando una dichiarazione inizia con un simbolo #, si chiama direttiva del preprocessore e non termina con un punto e virgola ';' un altro esempio di direttive preprocessore include:
#define :
La direttiva#define viene utilizzata per una dichiarazione delle costanti. È scritto nella forma
#define identificatore token_string
Ciò che questo fa è sostituire ogni occorrenza di identificatore nel codice con il valore token_string.
Esempio:
#define ABC 100
#define COMPANY_NAME "MetaQuotes Software Corp."
Sostituirà ogni occorrenza di COMPANY_NAME con la stringa "MetaQuotes Software Corp." o sostituirà ogni occorrenza di ABC con il carattere (o intero) 100 nel codice.
Puoi leggere di più sulle direttive del preprocessore nel Manuale MQL5. Continuiamo ora con discussione.
La seconda parte dell'intestazione del nostro codice è la sezione dei parametri di input:
Specifichiamo tutti i parametri, che verranno utilizzati nel nostro EA in questa sezione. Questi includono tutte le variabili che verranno utilizzate da tutte le funzioni che scriveremo nel nostro EA.
Le variabili dichiarate a questo livello sono chiamate Variabili Globalipoiché sono accessibili da tutte le funzioni del nostro EA che potrebbero averne bisogno. I parametri di Input sono parametri che possono essere modificati solo al di fuori del nostro EA. Possiamo anche dichiarare altre variabili che gestiremo nel corso del nostro EA ma non saranno disponibili al di fuori del nostro EA in questa sezione.
La prossima è la funzione di inizializzazione dell’EA. Questa è la prima funzione che viene chiamata quando l'EA viene avviato o associato a un grafico e viene chiamato solo una volta.
Questa sezione è il luogo migliore per eseguire alcuni controlli importanti per assicurarsi che il nostro EA funzioni alla perfezione.
Possiamo decidere di sapere se il grafico ha abbastanza barre per far funzionare il nostro EA, ecc.
È anche il posto migliore per ottenere gli handle che utilizzeremo per i nostri indicatori (ADX e Indicatori della Media Mobile).
Lafunzione OnDeinitviene chiamata quando l’ EA viene rimosso dal grafico.
Per il nostro EA, rilasceremo gli handle creati per i nostri Indicatori durante l'inizializzazione in questa sezione.
Questa funzione elabora l'evento NewTick, generato quando si riceve una nuova offerta per un simbolo.
Si noti che l’Expert Advisor non può eseguire operazioni di trading l'uso dell’Expert Advisor nel client terminal non è consentito (Pulsante "Auto Trading").
Figura 6. L’Autotrading è abilitato
La maggior parte dei nostri codici che implementeranno la nostra la strategia di trading, sviluppata in precedenza, sarà scritta all'interno di questa sezione.
Ora che abbiamo esaminato le varie sezioni del codice per il nostro EA, iniziamo ad aggiungere “carne” allo scheletro.
2.2 SEZIONE PARAMETRI DI INPUT
//--- input parameters input int StopLoss=30; // Stop Loss input int TakeProfit=100; // Take Profit input int ADX_Period=8; // ADX Period input int MA_Period=8; // Moving Average Period input int EA_Magic=12345; // EA Magic Number input double Adx_Min=22.0; // Minimum ADX Value input double Lot=0.1; // Lots to Trade //--- Other parameters int adxHandle; // handle for our ADX indicator int maHandle; // handle for our Moving Average indicator double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars double maVal[]; // Dynamic array to hold the values of Moving Average for each bars double p_close; // Variable to store the close value of a bar int STP, TKP; // To be used for Stop Loss & Take Profit values
Come puoi vedere, abbiamo aggiunto più parametri. Prima di continuare a parlare dei nuovi parametri, trattiamo qualcosa che puoi vedere ora. I due forward slash '//' ci permettono di inserire commenti nei nostri codici. Con i commenti, siamo in grado di sapere cosa rappresentano le nostre variabili o cosa stiamo facendo in quel momento nel nostro codice. Esso offre anche una migliore comprensione del nostro codice. Esistono due modi fondamentali per scrivere i commenti:
// Altri parametri ...
Questo è un commento di una sola riga
/*
Questo è un commento su più righe
*/
Questo è un commento su più righe. I commenti multi-riga iniziano con la coppia di simboli /* e termina con quella */ .
Il compilatore ignora tutti i commenti mentre compila il tuo codice.
Utilizzo di commenti a riga singola per i parametri di input è un buon modo per far capire ai nostri utenti EA cosa rappresentano questi parametri. Nelle proprietà di Input EA, i nostri utenti non vedranno il parametro stesso, bensì i commenti come mostrato di seguito:
Advisor
Figura 7. Parametri di input di Expert Advisor
Ora, torniamo al nostro codice...
Abbiamo deciso di aggiungere ulteriori parametri per il nostro EA. L’ EA_Magic è il numero magico per tutti gli ordini del nostro EA. Il valore ADX minimo (Adx_Min) è dichiarato come undoppio tipo di dati. Un tipodoppio viene utilizzato per memorizzare i floating point costanti che contengono una parte intera, un punto decimale e una parte frazione.
Esempio:
doppio mysum = 123,5678;
doppio b7 = 0,09876;
Il Lotto da per fare trading (Lotto) rappresenta il volume dello strumento finanziario con cui vogliamo operare. Quindi abbiamo dichiarato altri parametri che utilizzeremo:
L’ adxHandle deve essere utilizzato per memorizzare l'handle dell'indicatore ADX, mentre maHandle memorizzerà l'handle per il Indicatore della Media Mobile. I plsDI[], minDI[], adxVal[] sono array dinamici che conterranno i valori di +DI, -DI e ADX principale (dell'Indicatore ADX ) per ogni barra del grafico. Il maVal[] è un array dinamico che contiene i valori dell’Indicatore della Media mobile per ogni barra del grafico.
A proposito, cosa sono gli array dinamici? Un array dinamico è una array dichiarata senza una dimensione. In altre parole, nessun valore è specificato nella coppia di parentesi quadre. Un array statico statica, invece, ha le sue dimensioni definite nel punto di dichiarazione.
Esempio:
doppio allbars[20]; Ci vorranno 20 elementi
p_close è una variabile che useremo per memorizzare il Prezzo di chiusura per la barra che monitoreremo per il controllo dei nostri trade Buy/Sell.
STPe TKP verranno utilizzati per memorizzare i valori Stop Loss e Take Profit nel nostro EA.
2.3. SEZIONE INIZIALIZZAZIONE EA
int OnInit() { //--- Get handle for ADX indicator adxHandle=iADX(NULL,0,ADX_Period); //--- Get the handle for Moving Average indicator maHandle=iMA(_Symbol,_Period,MA_Period,0,MODE_EMA,PRICE_CLOSE); //--- What if handle returns Invalid Handle if(adxHandle<0 || maHandle<0) { Alert("Error Creating Handles for indicators - error: ",GetLastError(),"!!"); }
Qui otteniamo gli handle del nostro indicatore utilizzando le rispettive funzioni dell’ indicatore.
L’ handle dell’indicatore ADX viene ottenuto utilizzando la funzione iADX. Prende Il simbolo del grafico (NULL indica anche il simbolo corrente sul grafico corrente), il grafico periodo/timeframe (0 indica anche il timeframe corrente sul grafico corrente), il periodo di media ADX per calcolare l’indice (che abbiamo definito in precedenza con nella sezione parametri di input) come parametri o argomenti.
int iADX(
string symbol, // symbol name
ENUM_TIMEFRAMES period, // period
int adx_period // averaging period
);
L'handle dell'indicatore della Media Mobile viene ottenuto utilizzando la funzione iMA. Ha i seguenti argomenti:
- il simbolo del grafico (che può essere ottenuto utilizzando _symbol, symbol() o NULL per il simbolo corrente sul grafico corrente),
- ilperiod/timeframe del grafico (che può essere ottenuto utilizzando _period, period(), o 0 per l’attuale timeframe del grafico corrente),
- ilperiodo di media mobile (che abbiamo definito in precedenza nella sezione parametri di input),
- lo spostamento dell'indicatore relativo al grafico dei prezzi (lo spostamento qui è 0),
- il tipo di adattamento della media mobile (potrebbe essere uno dei seguenti tipi di media: MODE_SMA media semplice, media esponenziale MODE_EMA, MODE_SMMA media adattiva o media ponderata lineare-MODE_LWMA), e
- il prezzo utilizzato per la media (qui usiamo il prezzo di chiusura).
int iMA( |
Ti invitiamo a leggere il manuale MQL5 per ottenere ulteriori dettagli su queste funzioni dell’indicatore. Ciò ti offrirà una migliore comprensione sull’utilizzo di ciascun indicatore.
Proviamo nuovamente a controllare eventuali errori; nel caso in cui la funzione non abbia restituito l’handle con successo, otterremo un errore INVALID_HANDLE. Usiamo la funzione di avviso per visualizzare l'errore utilizzando la funzione GetlastError.
//--- Let us handle currency pairs with 5 or 3 digit prices instead of 4 STP = StopLoss; TKP = TakeProfit; if(_Digits==5 || _Digits==3) { STP = STP*10; TKP = TKP*10; }
Decidiamo di memorizzare i valori Stop Loss e i Profit nelle variabili STP e TKP che abbiamo dichiarato in precedenza. Perché stiamo facendo questo?
Perché i valori memorizzati nei parametri INPUT sono di sola lettura, non possono essere modificati. Quindi, qui vogliamo assicurarci che il nostro EA funzioni bene con tutti i broker. Digits o Digits() restituisce il numero di cifre decimali determinando l'accuratezza del prezzo del simbolo del grafico corrente. Per un grafico dei prezzi a 5 o 3 cifre, moltiplichiamo sia lo Stop Loss che il Take Profit per 10.
2.4. SEZIONE REINIZIALIZZAZIONE EA
Poiché questa funzione viene chiamata ogni volta che l'EA viene disabilitato o rimosso da un grafico, rilasceremo qui tutti gli indicatori handle creati durante il processo di inizializzazione. Abbiamo creato due handle, uno per l'indicatore ADX e un altro handle per l’indicatore della Media Mobile.
Useremo la funzione IndicatorRelease() per eseguire questa operazione. Ci vuole un solo argomento (l’handle dell'indicatore)
bool IndicatorRelease(
int indicator_handle, // indicator handle
);
La funzione rimuove un handle dell’indicatore e rilascia il blocco di calcolo dell'indicatore, se non è stato utilizzato.
2.5 LA SEZIONE EA ONTICK
La prima cosa che dobbiamo fare qui è verificare se abbiamo abbastanza barre sul grafico corrente. Possiamo ottenere le barre totali nella cronologia di qualsiasi grafico che utilizza la funzione Barre. Ci vogliono due parametri, il simbolo (può essere ottenuto utilizzando _Symbol o Symbol(). Questi due restituiscono il simbolo corrente per il grafico corrente a cui è associato il nostro EA) e il periodo o timeframe del grafico presente (può essere ottenuto utilizzando Period o Period(). Questi due restituiranno il timeframe del grafico corrente a cui è associato l'EA).
Se il totale delle barre disponibili è inferiore a 60, vogliamo che il nostro EA si “rilassi” fino a quando non avremo abbastanza barre disponibili sul grafico. La funzione Alert mostra un messaggio in una finestra separata. Prende qualsiasi valore separato da virgole come parametri/argomenti. In questo caso, abbiamo un solo valore della stringa. Il ritorno esce dall'inizializzazione del nostro EA.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Do we have enough bars to work with if(Bars(_Symbol,_Period)<60) // if total bars is less than 60 bars { Alert("We have less than 60 bars, EA will now exit!!"); return; } // We will use the static Old_Time variable to serve the bar time. // At each OnTick execution we will check the current bar time with the saved one. // If the bar time isn't equal to the saved time, it indicates that we have a new tick. static datetime Old_Time; datetime New_Time[1]; bool IsNewBar=false; // copying the last bar time to the element New_Time[0] int copied=CopyTime(_Symbol,_Period,0,1,New_Time); if(copied>0) // ok, the data has been copied successfully { if(Old_Time!=New_Time[0]) // if old time isn't equal to new bar time { IsNewBar=true; // if it isn't a first call, the new bar has appeared if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time); Old_Time=New_Time[0]; // saving bar time } } else { Alert("Error in copying historical times data, error =",GetLastError()); ResetLastError(); return; } //--- EA should only check for new trade if we have a new bar if(IsNewBar==false) { return; } //--- Do we have enough bars to work with int Mybars=Bars(_Symbol,_Period); if(Mybars<60) // if total bars is less than 60 bars { Alert("We have less than 60 bars, EA will now exit!!"); return; } //--- Define some MQL5 Structures we will use for our trade MqlTick latest_price; // To be used for getting recent/latest price quotes MqlTradeRequest mrequest; // To be used for sending our trade requests MqlTradeResult mresult; // To be used to get our trade results MqlRates mrate[]; // To be used to store the prices, volumes and spread of each bar ZeroMemory(mrequest); // Initialization of mrequest structure
L'Expert Advisor eseguirà operazioni di trading all'inizio di una nuova barra, quindi è necessario risolvere il problema con la nuova identificazione della barra. In altre parole, vogliamo essere sicuri che il nostro EA non controlli le impostazioni Long/Short su ogni tick, vogliamo solo che il nostro EA controlli le posizioni Long/Short quando c'è una nuova barra.
Iniziamo dichiarando una variabile datetime staticaOld_Time, che memorizzerà il Bar time. La abbiamo definita statica poiché vogliamo che il valore venga mantenuto in memoria fino alla chiamata successiva della funzione OnTick. Quindi saremo in grado di confrontare il suo valore con la variabile New_Time (anche del tipo di dati datetime), che è un array di un elemento che contiene il nuovo (corrente) bar time. Abbiamo anche dichiarato una variabile di dati di tipo bool IsNewBar e ne abbiamo impostato il valore su false. Ciò, perché vogliamo che il suo valore sia VERO solo quando abbiamo una nuova barra.
Usiamo la funzione CopyTime per ottenere l'ora della barra corrente. Copia il bar time nell’array New_Time con un elemento; se ha esito positivo, confrontiamo l'ora di una nuova barra con l'ora della barra precedente. Se i tempi non coincidono, significa che abbiamo una nuova barra e impostiamo la variabile IsNewBar su TRUE e salviamo il valore della bar time corrente sulla variabile Old_Time.
La variabile IsNewBar indica che abbiamo una nuova barra. Se è FALSO, terminiamo l'esecuzione della funzione OnTick.
Dai un'occhiata al codice
if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);
esso controlla l'esecuzione della modalità di debug, stamperà il messaggio sul bar time nella modalità di debug, lo considereremo in seguito.
La prossima cosa che vogliamo fare qui è verificare se abbiamo abbastanza barre con cui lavorare. Perché ripeterlo? Vogliamo solo essere sicuri che il nostro EA funzioni correttamente. Va notato che mentre la funzione OnInit viene chiamata solo una volta quando l'EA è associato ad un grafico, la funzione OnTick viene chiamata ogni volta che c'è un nuovo tick (quotazione del prezzo).
Sicuramente, hai notato che lo abbiamo fatto di nuovo in modo diverso qui. Decidiamo di memorizzare i bar totali nella cronologia che abbiamo ottenuto dall'espressione
int Mybars=Bars(_Symbol,_Period);
in una nuova variabile, Mybars, dichiarata all'interno della funzione OnTick. Questo tipo di variabile è una variabile locale, a differenza della variabile che abbiamo dichiarato nella sezione PARAMETRI DI INPUT del nostro codice. Mentre le variabili, dichiarate nella sezione Parametri di input del nostro codice, sono disponibili per tutte le funzioni, all'interno del nostro codice che potrebbe averne bisogno, le variabili dichiarate all'interno di un la singola funzione sono limitate e disponibili solo per quella funzione. Non può essere utilizzato al di fuori di questa funzione.
Successivamente, abbiamo dichiarato alcune variabili tipi struttura MQL5 che verranno utilizzati in questa sezione del nostro EA. MQL5 ha un certo numero di Strutture integrate che rende le cose abbastanza facili per gli sviluppatori EA. Prendiamo le Strutture una dopo l'altra.
Questa è una struttura utilizzata per memorizzare gli ultimi prezzi dei simboli.
struct MqlTick
{
datetime time ; //Time of the last prices update
double bid; //Current Bid price
double ask; //Current Ask price
double last;) // Price of the last deal (Last)
ulong volume; // Volume for the current Last price
};
Qualsiasi variabile dichiarata di tipo MqlTick può essere facilmente utilizzata per ottenere i valori correnti di Ask, Bid, Last e Volume una volta chiamata la funzione SymbolInfoTick().
Così abbiamo dichiarato latest_price come tipo MqlTick in modo da poterlo utilizzare per ottenere i prezzi Ask e Bid
Questa struttura viene utilizzata per eseguire tutte le richieste di trading per un'operazione di trading. Contiene, nella sua struttura, tutti i campi necessari per l'esecuzione di una transazione di trading.
struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; // Trade operation type
ulong magic; // Expert Advisor ID (magic number)
ulong order; // Order ticket
string symbol; // Trade symbol
double volume; // Requested volume for a deal in lots
double price; // Price
double stoplimit; // StopLimit level of the order
double sl; // Stop Loss level of the order
double tp; // Take Profit level of the order
ulong deviation; // Maximal possible deviation from the requested price
ENUM_ORDER_TYPE type; // Order type
ENUM_ORDER_TYPE_FILLING type_filling; // Order execution type
ENUM_ORDER_TYPE_TIME type_time; // Order execution time
datetime expiration; // Order expiration time (for the orders of ORDER_TIME_SPECIFIED type)
string comment; // Order comment
};
Qualsiasi variabile dichiarata di tipo MqlTradeRequest può essere utilizzata per inviare ordini per le nostre operazioni di trading. Qui abbiamo dichiarato mrequest come un tipo MqlTradeRequest.
Il risultato di qualsiasi operazione di trading viene restituito come una speciale struttura predefinita di tipo MqlTradeResult. Qualsiasi variabile dichiarata di tipo MqlTradeResult sarà in grado di accedere ai risultati della richiesta di trading.
struct MqlTradeResult
{
uint retcode; // Operation return code
ulong deal; // Deal ticket, if it is performed
ulong order; // Order ticket, if it is placed
double volume; // Deal volume, confirmed by broker
double price; // Deal price, confirmed by broker
double bid; // Current Bid price
double ask; // Current Ask price
string comment; // Broker comment to operation (by default it is filled by the operation description)
};
Qui abbiamo dichiarato mresult come un tipo MqlTradeResult.
Il Prezzo (di Apertura, di Chiusura, Alto, Basso), il Tempo, i Volumi di ogni barra e lo spread per un simbolo viene memorizzato in questa struttura. Qualsiasi array dichiarato di tipo MqlRates può essere utilizzato per memorizzare la cronologia del prezzo, dei volumi dello spread per un simbolo.
struct MqlRates
{
datetime time; // Period start time
double open; // Open price
double high; // The highest price of the period
double low; // The lowest price of the period
double close; // Close price
long tick_volume; // Tick volume
int spread; // Spread
long real_volume; // Trade volume
};
Qui abbiamo dichiarato un array mrate[] che verrà utilizzato per memorizzare queste informazioni.
/* Let's make sure our arrays values for the Rates, ADX Values and MA values is store serially similar to the timeseries array */ // the rates arrays ArraySetAsSeries(mrate,true); // the ADX DI+values array ArraySetAsSeries(plsDI,true); // the ADX DI-values array ArraySetAsSeries(minDI,true); // the ADX values arrays ArraySetAsSeries(adxVal,true); // the MA-8 values arrays ArraySetAsSeries(maVal,true);
Successivamente, decidiamo di impostare tutti gli array che utilizzeremo per memorizzare i dettagli delle Barre come serie. Questo è per garantire che i valori che verranno copiati negli array vengano indicizzati come la timeserie, cioè 0, 1, 2, 3, (per corrispondere all'indice delle barre. Quindi, noi utilizziamo la funzione ArraySetAsSeries().
bool ArraySetAsSeries(
void array[], // array by reference
bool set // true denotes reverse order of indexing
);
Si dovrebbe notare che ciò può essere fatto anche una volta nella sezione inizializzazione del nostro codice. Tuttavia, ho deciso di mostrarlo a questo punto ai fini della nostra spiegazione.
//--- Get the last price quote using the MQL5 MqlTick Structure if(!SymbolInfoTick(_Symbol,latest_price)) { Alert("Error getting the latest price quote - error:",GetLastError(),"!!"); return; }
Ora utilizziamo la funzione SymbolInfoTick per ottenere l'ultima quotazione del prezzo. Questa la funzione accetta due argomenti: il simbolodel grafico e lavariabile di struttura MqlTick (latest_price). Ancora una volta, se c'è un errore, lo segnaliamo.
//--- Get the details of the latest 3 bars if(CopyRates(_Symbol,_Period,0,3,mrate)<0) { Alert("Error copying rates/history data - error:",GetLastError(),"!!"); return; }
Successivamente, abbiamo copiato le informazioni sulle ultime tre barre nel nostro array di tipi Mqlrates utilizzando la funzione CopyRates. La funzione CopyRates viene utilizzata per ottenere la cronologia dati della struttura MqlRates di un Simbolo-Periodo specificato in quantità specificata in un array di tipo MqlRates.
int CopyRates(
string symbol_name, // symbol name
ENUM_TIMEFRAMES timeframe, // period
int start_pos, // start position
int count, // data count to copy
MqlRates rates_array[] // target array to copy
);
Il nome del simbolo si ottiene utilizzando ‘_symbol’, the current period/timeframe is obtained by using ‘_period’. Per la posizione di partenza, partiremo dalla barra corrente, Barra 0 e conteremo solo tre Barre, Barre 0, 1 e 2. Il risultato sarà memorizzato nel nostro array, mrate[].
L’array mrate[] ora contiene tutte le informazioni su prezzo, tempo, volumi e spread per le barre 0, 1 e 2. Pertanto, per ottenere i dettagli di qualsiasi barra, utilizzeremo quanto segue:
mrate[bar_number].bar_property
Ad esempio, possiamo avere le seguenti informazioni su ogni barra:
mrate[1].time // Bar 1 Start time
mrate[1].open // Bar 1 Open price
mrate[0].high // Bar 0 (current bar) high price, etc
Successivamente, abbiamo copiato tutti i valori dell'indicatore negli array dinamici che abbiamo dichiarato utilizzando la funzione CopyBuffer.
int CopyBuffer(
int indicator_handle, // indicator handle
int buffer_num, // indicator buffer number
int start_pos, // start position
int count, // amount to copy
double buffer[] // target array to copy
);
L’handle dell’indicatore è l’handle che abbiamo creato nella sezione OnInit. Riguardo ai numeri di buffer, l'indicatore ADX ha tre (3) buffer:
- 0 - MAIN_LINE,
- 1 - PLUSDI_LINE,
- 2 - MINUSDI_LINE.
L’ indicatore della Media Mobile ha un solo (1) buffer:
- 0 – MAIN_LINE.
Copiamo dalla barra presente (0) alle ultime due barre. Quindi, la quantità di record da copiare è 3 (barre 0, 1 e 2). Il buffer[] è l'array dinamico di destinazione che avevamo precedentemente dichiarato – adxVal, plsDI, minDI and maVal.
Come puoi vedere, anche in questo caso, proviamo a rilevare qualsiasi errore che possa verificarsi nel processo di copiatura. Se c'è un errore, non c'è bisogno di andare oltre.
È importante notare che la funzione CopyBuffer() e la funzione CopyRates() restituiscono il numero totale di record copiati in caso di esito positivo mentre restituiscono -1 in caso di errore. Ecco perché stiamo controllando qui un valore inferiore a 0 (zero) nelle funzioni di controllo dell’errore.
//--- Copy the new values of our indicators to buffers (arrays) using the handle if(CopyBuffer(adxHandle,0,0,3,adxVal)<0 || CopyBuffer(adxHandle,1,0,3,plsDI)<0 || CopyBuffer(adxHandle,2,0,3,minDI)<0) { Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!"); return; } if(CopyBuffer(maHandle,0,0,3,maVal)<0) { Alert("Error copying Moving Average indicator buffer - error:",GetLastError()); return; }
A questo punto vogliamo verificare se abbiamo già una posizione Buy or Sell aperta, in altre parole, vogliamo assicurarci di avere solo UN trade Sell o Buy aperto alla volta. Non vogliamo aprire un nuovo Buy se ne abbiamo già uno e non vogliamo aprire un nuovo Sell se ne abbiamo già uno aperto.
Per raggiungere questo obiettivo dichiareremo prima di tutto due variabili di tipo di dati bool (Buy_opened e Sell_opened) che conterranno un un valore TRUE se abbiamo già una posizione aperta per Buy o Sell.
//--- we have no errors, so continue //--- Do we have positions opened already? bool Buy_opened=false; // variable to hold the result of Buy opened position bool Sell_opened=false; // variable to hold the result of Sell opened position if (PositionSelect(_Symbol) ==true) // we have an opened position { if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { Buy_opened = true; //It is a Buy } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { Sell_opened = true; // It is a Sell } }
Usiamo la funzione di trading PositionSelect per sapere se abbiamo una posizione aperta. Questa funzione restituisce TRUE se abbiamo già una posizione aperta e FALSE se non ne abbiamo nessuna.
bool PositionSelect( string symbol // Symbol name );
Prende, come argomento/parametro principale, il simbolo (coppia di valute) che vogliamo controllare. Qui, usiamo _symbol perché stiamo controllando il simbolo corrente (coppia di valute).
Se questa espressione restituisce TRUE, allora vogliamo verificare se la posizione aperta è un Buy o un Sell. Usiamo la funzione PositionGetInteger proprio a tale scopo. Essa ci dà il tipo di posizione aperta quando la usiamo con il modificatore POSITION_TYPE. Restituisce l'identificatore del tipo di Posizione che può essere POSITION_TYPE_BUY o POSITION_TYPE_SELL
long PositionGetInteger( ENUM_POSITION_PROPERTY property_id // Property identifier );
Nel nostro caso, l'abbiamo utilizzato per determinare quale delle posizioni abbiamo già aperto. Se si tratta di un Sell, memorizziamo un valore TRUE in Sell_opened e se si tratta di un Buy, memorizziamo un valore TRUE in Buy_opened. Saremo in grado di utilizzare queste due variabili in un secondo momento, quando controlleremo le condizioni per Sell o Buy più avanti nel nostro codice.
Ora è il momento di memorizzare il prezzo di chiusura per la barra che utilizzeremo per la nostra configurazione Buy/Sell. Ricorda che abbiamo dichiarato una variabile per quello precedente
// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1 p_close=mrate[1].close; // bar 1 close price
Fatto questo, procederemo ora alla fase successiva.
/* 1. Check for a long/Buy Setup : MA-8 increasing upwards, previous price close above it, ADX > 22, +DI > -DI */ //--- Declare bool type variables to hold our Buy Conditions bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]); // MA-8 Increasing upwards bool Buy_Condition_2 = (p_close > maVal[1]); // previuos price closed above MA-8 bool Buy_Condition_3 = (adxVal[0]>Adx_Min); // Current ADX value greater than minimum value (22) bool Buy_Condition_4 = (plsDI[0]>minDI[0]); // +DI greater than -DI //--- Putting all together if(Buy_Condition_1 && Buy_Condition_2) { if(Buy_Condition_3 && Buy_Condition_4) { // any opened Buy position? if (Buy_opened) { Alert("We already have a Buy Position!!!"); return; // Don't open a new Buy Position } mrequest.action = TRADE_ACTION_DEAL; // immediate order execution mrequest.price = NormalizeDouble(latest_price.ask,_Digits); // latest ask price mrequest.sl = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // Stop Loss mrequest.tp = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // Take Profit mrequest.symbol = _Symbol; // currency pair mrequest.volume = Lot; // number of lots to trade mrequest.magic = EA_Magic; // Order Magic Number mrequest.type = ORDER_TYPE_BUY; // Buy Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult);
Ora è il momento di iniziare a controllare un’ opportunità di Acquisto.
Analizziamo l'espressione sopra in quanto rappresenta la strategia che abbiamo progettato in precedenza. Stiamo dichiarando una variabile di tipo bool per ciascuna delle nostre condizioni che deve essere soddisfatta prima che un ordine possa essere effettuato. Una variabile di tipo bool può contenere solo TRUE o FALSE. Quindi, la nostra strategia di Acquisto è stata suddivisa in quattro condizioni. Se una qualsiasi delle condizioni viene rispettata o soddisfatta, un valore TRUE viene memorizzato nella nostra variabile di tipo bool, altrimenti verrà memorizzato un valore FALSE. Analizziamoli uno per uno.
bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]);
Qui, stiamo considerando i valori MA-8 sulle Barre 0, 1 e 2. Se il valore di MA-8 sulla barra corrente è maggiore del suo valore sulla Barra 1 precedente e anche il valore MA-8 sulla Barra 1 è maggiore del suo valore sulla Barra 2, significa che MA-8 sta aumentando verso l'alto. Ciò soddisfa una delle nostre condizioni per una configurazione Buy.
bool Buy_Condition_2 = (p_close > maVal[1]);
Questa espressione sta verificando se il prezzo di Chiusura Barra 1 è superiore al valore di MA-8 nello stesso periodo (periodo Barra 1). Se il prezzo è più alto, allora anche la nostra seconda condizione è stata soddisfatta; quindi possiamo controllare altre condizioni. Tuttavia, se le due condizioni che abbiamo appena analizzato non fossero state soddisfatte, allora non sarebbe stato necessario verificare altre condizioni. Ecco perché decidiamo di includere la prossima espressione all'interno di queste due condizioni iniziali (espressioni).
bool Buy_Condition_3 = (adxVal[0]>Adx_Min);
Ora vogliamo verificare se il valore corrente di ADX (valore ADX sulla Barra 0) è maggiore del valore ADX Minimo dichiarato nei Parametri di input Se questa espressione è vera, cioè, il valore corrente di ADX è maggiore del valore Minimo richiesto; vogliamo anche essere sicuri che il valore plusDI sia è maggiore del valore minusDI . Questo è ciò che abbiamo ottenuto nella successiva espressione
bool Buy_Condition_4 = (plsDI[0]>minDI[0]);
Se tutte queste condizioni sono soddisfatte, cioè se risultano vere, allora vogliamo essere sicuri di non aprire una nuova posizione Buy se ne abbiamo già una. Ora è il momento di controllare il valore della variabile Buy_opened che abbiamo dichiarato in precedenza nel nostro codice.
// any opened Buy position? if (Buy_opened) { Alert("We already have a Buy Position!!!"); return; // Don't open a new Buy Position }
Se Buy_opened risulta vera, non vogliamo aprire un'altra posizione Buy.Quindi, mostriamo un avviso per informarci e, dunque, in modo che ora il nostro EA attenda il prossimo Tick. Tuttavia, se Buy_opened è FALSE, prepariamo i nostri record utilizzando la variabile di tipo MqlTradeRequest (mrequest) che abbiamo dichiarato in precedenza per inviare il nostro ordine.
- L’ azione qui, che è il tipo di operazione di trading, è TRADE_ACTION_DEAL perché stiamo effettuando un ordine di trading per la sua esecuzione immediata. Se stiamo modificando un ordine, allora utilizzeremo TRADE_ACTION_MODIFY. Per eliminare un ordine useremo TRADE_ACTION_REMOVE. Abbiamo usato il nostro tipo MqlTick latest_price per ottenere l’ ultimo prezzoAsk . Il prezzo di Stop loss dell’ordine si ottiene sottraendo il nostro StopLoss in punti dal prezzo Ask mentre il prezzo take profit dell'ordine si ottiene aggiungendo il nostro TakeProfit in punti al prezzo Ask. Noterai anche che abbiamo utilizzato la funzione NormalizeDouble per il prezzo Ask, i valori StopLoss e TakeProfit; è buona norma normalizzare sempre questi prezzi al numero di cifre della coppia di valute prima di inviarlo al server di trading.
- Il simbolo è il simbolo corrente (_Symbol o Symbol()). Il tipo di ordine è il tipo di ordine che stiamo effettuando; qui stiamo effettuando un ordine di acquisto ORDER_TYPE_BUY. Per un ordine Sell, ci sarà ORDER_TYPE_SELL.
- Iltype_filling dell'ordine è il tipo di esecuzione dell'ordine; ORDER_FILLING_FOK significa che la transazione può essere effettuate esclusivamente con un volume specificato a parità di prezzo o a un prezzo migliore del prezzo specificato dall'ordine. Se non c'è abbastanza volume di offerte sul simbolo dell'ordine, l'ordine non verrà eseguito.
La funzione OrderSend() accetta due argomenti, il tipo MqlTradeRequest e la variabile di tipo MqlTradeResult.
bool OrderSend( MqlTradeRequest& request // query structure MqlTradeResult& result // structure of the answer );
Come puoi vedere, abbiamo usato la nostra variabile di tipo MqlTradeRequest e la variabile di tipo MqlTradeResult nell'effettuare il nostro ordine utilizzando OrderSend.
// get the result code if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Alert("A Buy order has been successfully placed with Ticket#:",mresult.order,"!!"); } else { Alert("The Buy order request could not be completed -error:",GetLastError()); ResetLastError(); return; }
Dopo aver inviato il nostro ordine, ora useremo la variabile di tipo MqlTradeResult per controllare il risultato del nostro ordine. Se il nostro ordine viene effettuato con successo, vogliamo essere informati e se non lo è, vogliamo saperlo ugualmente. Con la variabile di tipo MqlTradeResult 'mresult' possiamo accedere al codice di ritorno dell'Operazione e anche il numero di ticket dell’ordine se l'ordine viene effettuato.
Il codice di ritorno 10009 mostra che la richiesta OrderSend è stata completata con successo, mentre 10008 mostra che il nostro ordine è stato effettuato. Questo è il motivo per cui abbiamo controllato per uno qualsiasi dei questi due codici restituiti. Se ne abbiamo uno, siamo sicuri che il nostro ordine è stato completato o è stato effettuato.
Per controllare un'Opportunità di Vendita, controlliamo l'opposto di quello che abbiamo fatto per l'Opportunità Acquisto, ad eccezione del nostro ADX che deve essere maggiore del valore Minimo specificato.
/* 2. Check for a Short/Sell Setup : MA-8 decreasing downwards, previous price close below it, ADX > 22, -DI > +DI */ //--- Declare bool type variables to hold our Sell Conditions bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]); // MA-8 decreasing downwards bool Sell_Condition_2 = (p_close <maVal[1]); // Previous price closed below MA-8 bool Sell_Condition_3 = (adxVal[0]>Adx_Min); // Current ADX value greater than minimum (22) bool Sell_Condition_4 = (plsDI[0]<minDI[0]); // -DI greater than +DI //--- Putting all together if(Sell_Condition_1 && Sell_Condition_2) { if(Sell_Condition_3 && Sell_Condition_4) { // any opened Sell position? if (Sell_opened) { Alert("We already have a Sell position!!!"); return; // Don't open a new Sell Position } mrequest.action = TRADE_ACTION_DEAL; // immediate order execution mrequest.price = NormalizeDouble(latest_price.bid,_Digits); // latest Bid price mrequest.sl = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // Stop Loss mrequest.tp = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // Take Profit mrequest.symbol = _Symbol; // currency pair mrequest.volume = Lot; // number of lots to trade mrequest.magic = EA_Magic; // Order Magic Number mrequest.type= ORDER_TYPE_SELL; // Sell Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult);
Proprio come abbiamo fatto nella sezione di acquisto, stiamo dichiarando una variabile di tipo bool per ciascuna delle nostre condizioni che deve essere soddisfatta prima che un ordine possa essere effettuato. Una variabile di tipo bool può contenere solo TRUE o FALSE. Quindi, la nostra strategia di Vendita è stata suddivisa in quattro condizioni. Se una qualsiasi delle condizioni viene rispettata o soddisfatta, un valore TRUE viene memorizzato nella nostra variabile di tipo bool, altrimenti verrà memorizzato un valore FALSE. Analizziamoli uno per uno come abbiamo nella sezione Buy
bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);
Qui, stiamo considerando i valori MA-8 sulle Barre 0, 1 e 2. Se il valore di MA-8 sulla barra corrente è inferiore al valore sulla Barra 1 precedente e anche il valore MA-8 sulla Barra 1 è inferiore al suo valore sulla Barra 2, significa che MA-8 sta diminuendo verso il basso. Ciò soddisfa una delle nostre condizioni per una configurazione di Sell.
bool Sell_Condition_2 = (p_close <maVal[1]);
Questa espressione sta controllando se la Barra 1 Il prezzo di chiusura è inferiore al valore di MA-8 nello stesso periodo (periodo Barra 1). Se il prezzo è più basso, allora anche la nostra seconda condizione è stata soddisfatta; quindi, possiamo controllare altre condizioni. Tuttavia, se le due condizioni che abbiamo appena considerato non sono state soddisfatte, allora non sarà necessario controllare altre condizioni. Ecco perché decidiamo di includere le prossime espressioni all'interno di queste due condizioni iniziali (espressioni).
bool Sell_Condition_3 = (adxVal[0]>Adx_Min);
Ora vogliamo verificare se il valore corrente di ADX (valore ADX sulla Barra 0) è maggiore del valore ADX Minimo dichiarato nei Parametri di input Se questa espressione è vera, cioè, il valore corrente di ADX è maggiore del valore Minimo richiesto; vogliamo anche essere sicuri che il valoreMinusDI sia maggiore del valoreplusDI . Questo è ciò che abbiamo ottenuto nella successiva espressione
bool Sell_Condition_4 = (plsDI[0]<minDI[0]);
Se queste condizioni sono soddisfatte,cioè se tornano vere, allora vogliamo essere sicuri di non aprire una nuova posizione di acquisto se ne abbiamo già una. Ora è il momento di controllare il valore della variabile Buy_opened che abbiamo dichiarato in precedenza nel nostro codice.
// any opened Sell position? if (Sell_opened) { Alert("We already have a Sell position!!!"); return; // Don't open a new Sell Position }
Se Sell_opened è vero, non vogliamo aprire un'altra posizione di vendita, quindi mostriamo un avviso per informarci e poi tornare in modo che il nostro EA attenda ora il prossimo Tick. Tuttavia, se Sell_opened è FALSO, allora impostiamo la nostra richiesta di Sell trade come abbiamo fatto per l'ordine di Acquisto.
La principale differenza qui è il modo in cui noi calcoliamo il nostro prezzo stop loss e il prezzo take profit. Anche quando stiamo vendendo, vendiamo al prezzo Bid; ecco perché abbiamo usato la nostra variabile di tipo MqlTick latest_price per ottenere l'ultimo prezzo bid. L'altro tipo qui, come spiegato in precedenza, è ORDER_TYPE_SELL.
Anche qui,abbiamo utilizzato la funzione NormalizeDouble per il prezzo Bid, i valori StopLoss e TakeProfit; è buona norma normalizzare sempre questi prezzi al numero di cifre della coppia di valute prima di inviarlo al server di trading.
Proprio come abbiamo fatto per il nostro ordine di Acquisto, dobbiamo controllare anche se il nostro ordine di Vendita è andato a buon fine o no. Quindi abbiamo usato la stessa espressione come nel nostro ordine di Acquisto.
if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Alert("A Sell order has been successfully placed with Ticket#:",mresult.order,"!!"); } else { Alert("The Sell order request could not be completed -error:",GetLastError()); ResetLastError(); return; } }
3. Debug e test del nostro Expert Advisor
A questo punto, dobbiamo testare il nostro EA per sapere se la nostra strategia funziona o no. Inoltre, è possibile che ci siano uno o due errori nel nostro codice EA. Questo lo scopriremo nel passaggio successivo.
3.1 IL DEBUG
Il debug del nostro codice ci aiuta a capire come il nostro codice esegue riga per riga (se impostiamo dei breakpoint) e quindi poi possiamo notare qualsiasi errore o bug nel nostro codice ed effettuare rapidamente le correzioni necessarie prima di utilizzare il nostro codice nel real trade.
Qui, passeremo attraverso il processo graduale step by step di debug del nostro Expert Advisor impostando, in primo luogo, con breakpoint e, in secondo luogo, senza breakpoint. Per fare questo, assicurati di non aver chiuso l'Editor. Prima di tutto, selezioniamo il grafico che vogliamo utilizzare per testare il nostro EA. Nella barra dei Menu dell'Editor, clicca su Strumenti e clicca su Opzioni come mostrato di seguito:
Figura 8. Configurazione delle opzioni di Debug
Una volta comparsa la finestra Opzioni, selezionare la coppia di valute e il periodo/intervallo di tempo da utilizzare e cliccare sul pulsante OK:
Prima di avviare il debugger, impostiamo i breakpoint. I breakpoint ci permettono di monitorare il comportamento/le prestazioni del nostro codice in determinate posizioni o linee selezionate. Piuttosto che eseguire tutto il codice in una sola volta, il debugger si fermerà ogni volta che vede un breakpoint, in attesa della tua azione netta. Con questo, saremo in grado di analizzare il nostro codice e monitorare il suo comportamento quando raggiunge ogni punto di rottura configurato. Saremo anche in grado di valutare i valori di alcune delle nostre variabili per vedere se le cose vanno effettivamente come abbiamo previsto.
Per inserire unbreakpoint, passare alla riga nel tuo codice quando vuoi impostare il breakpoint. Sul lato sinistro, nel campo grigio vicino al bordo della riga del codice, fare doppio click e verrà visualizzato un piccolo bottone blu rotondo con un quadrato bianco al suo interno. O in alternativa, posizionare il cursore del tuo mouse in un punto qualsiasi della riga del codice in cui si desidera che il breakpoint per visualizzare e premere F9. Per rimuovere il breakpoint, premere di nuovo F9 o fare doppio click su di esso.
Figura10. Impostazione di un breakpoint
Per il nostro codice, stabiliremo un breakpoint su cinque righe diverse.
Li etichetterò anche da 1 a 5 ai fini della spiegazione.
Per continuare, impostare un breakpoint nelle sette righe di codice, come illustrato nella figura seguente. Il Breakpoint1 è quello che abbiamo creato sopra.
Figura 11. Configurazione di breakpoint aggiuntivi
Una volta che abbiamo finito di configurare i nostri breakpoint, ora siamo pronti per iniziare il debug del nostro codice.
Per avviare il debugger, premere F5 o cliccare sul pulsante verde sulla Barra degli Strumenti del MetaEditor:
Figura 12. Avvio del Debugger
La prima cosa che l'editor fa è compilare il codice; se c'è qualche errore in un determinato punto, lo visualizzerà e se non c’è nessun errore, ti comunicherà che il codice è stato compilato correttamente.
Figura 13. Report di Compilazione
Si prega di notare che il fatto che il codice sia compilato correttamente non significa che esso non contenga errori. A seconda di come viene scritto il codice, potrebbero esserci errori di runtime. Ad esempio, se una qualsiasi delle nostre espressioni non viene valutata correttamente a causa di una piccola svista, il codice verrà compilato correttamente ma potrebbe non essere eseguito correttamente. Troppe chiacchiere, vediamolo in azione...
Una volta che il debugger ha terminato la compilazione del codice, ti porta al terminale di trading e associa l'EA al grafico che hai specificato nelle impostazioni delle Opzioni di MetaEditor. Allo stesso tempo, mostra la sezione parametri di input dell'EA. Dal momento che non stiamo sistemando ancora nulla, basta fare clic sul pulsante OK.
Figura 14. Parametri di Input dell’Expert Advisor per il Debug
Ora vedrai chiaramente l'EA nell’ angolo in alto a destra del grafico.
Una volta avviato l’ OnTick(), si fermerà non appena arriva al nostro breakpoint 1.
Figura 15. Il Debugger si ferma al primo breakpoint
Noterai una freccia verde in quella riga di codice. Ciò indica che la riga di codice precedente era stata eseguita; ora siamo pronti per eseguire la riga corrente.
Permettetemi di spiegare alcune cose prima di procedere. Se guardi la Barra degli Strumenti dell'Editor, noterai che i tre pulsanti con frecce curve che in precedenza erano disattivate, ora sono attivate. Questo perché ora stiamo eseguendo il debugger. Questi pulsanti/comandi vengono utilizzato per esaminare il nostro codice (Step into, Step over o Step out)
Figura 16. Comando Step into
Lo Step Into viene utilizzato per andare da un passaggio dell'esecuzione del programma al passaggio successivo, entrando in ogni funzione chiamata all'interno di quella riga di codice. Cliccare sul pulsante o premere F11 per richiamare il comando. (Useremo questo comando nel nostro debug Step by Step del nostro codice.)
Figura 17. Comando Step over
LoStep over invece non entra in qualsiasi funzione chiamata all'interno di quella riga di codice. Cliccare sul pulsante o premere F10 per richiamare il comando
Figura 18. Comando Step out
Per eseguire un passaggio del programma che è a un livello superiore, cliccare su questo pulsante o premere Shift+F11.
Inoltre, nella parte inferiore dell'Editor, vedrai la finestra Toolbox. Il tab Debug in questa finestra ha le seguenti voci:
- File: mostra il nome del file chiamato
- Funzione. Questo mostra la funzione corrente dal file chiamato
- Riga: Mostra il numero della riga di codice nel file da cui viene chiamata la funzione.
- Espressione: Qui è dove puoi digitare il nome di qualsiasi espressione/variabile che sei interessato a monitorare dal nostro codice.
- Valore Verrà visualizzato il valore dell’ espressione/variabile digitata nell'area dell’Espressione.
- Tipo: Mostra il tipo di dati dell’ espressione/variabile monitorata.
Torniamo al nostro processo di debug...
La prossima cosa che vogliamo fare ora è digitare le variabili/espressioni del nostro codice che ci interessa monitorare. Assicurati di monitorare solo le variabili/ espressioni che contano davvero nel tuo codice. Per il nostro esempio, monitoreremo quanto segue:
- Old_Time (bar time vecchia)
- New_Time[0] (bar time corrente)
- IsNewBar (flag che indica la nuova barra)
- Mybars (Barre totali nella Cronologia) – Il nostro EA dipende da questo
Puoi aggiungere altri come i valori ADX, i valori MA-8, ecc.
Per aggiungere l'espressione/variabile, fare doppio click nell'area delle Espressioni oppure cliccare con il pulsante destro del mouse nell’area delle Espressioni e selezionare Aggiungi come mostratonella figura qui sopra.
Digitare l'espressione/variabile da monitorare o da osservare.
Figura 19. La finestra di osservazione delle espressioni
Digita tutte le variabili/espressioni necessarie...
Figura 20. Aggiunta di espressioni o variabili da osservare
Se la variabile non è stata ancora dichiarata, il suo tipo è "Identificatore sconosciuto" (ad eccezione delle variabili statiche).
Figura 21. Comando step into in azione
Cliccare il pulsanteStep into o premere F11 e osserva cosa succede. Continua, premendo questo pulsante o F11 fino ad arrivare al breakpoint n. 2, continuare fino ad arrivare al breakpoint n. 4 come mostrato di seguito e si osservare la finestra di osservazione delle espressioni.
Figura 22. Osservare le espressioni o le variabili
Figura 23. Osservare le espressioni o le variabili
variabili
Figura 24. Osservare le espressioni o le variabili
Una volta che c'è un nuovo tick, tornerà alla prima riga di codice della funzioneOnTick(). Tutti i valori delle nostre variabili/espressioni saranno resettati poiché si tratta di un nuovo tick, eccetto il caso in cui uno di essi viene dichiarato una variabile statica. Nel nostro caso, abbiamo una variabile statica Old_Time.
Figura 25. Valori delle variabili sull'evento NewTick
Per ripetere il processo, continua premendo il tasto F11e continua a monitorare le variabili nella finestra di osservazione delle espressioni. È possibile arrestare il debugger e quindi rimuovere tutti i breakpoints.
Come possiamo vedere, in modalità Debug stampa il messaggio "Abbiamo una nuova barra qui ...".
Figura 26. L’ Expert Advisor stampa il messaggio in modalità Debug
Avviare nuovamente il processo di debug; ma questa volta senza breakpoint. Continua a guardare ogni tick e se una qualsiasi delle nostre condizioni di Acquisto/Vendita è soddisfatta, effettuerà un trade. Dal momento che abbiamo scritto il nostro codice, per comunicarci se un ordine è andato a buon fine o meno, visualizzare un avviso.
Figura 27. L’Expert Advisor esegue un trade durante il debug
Penso che tu possa lasciare l'EA a lavorare per qualche minuto in più mentre prendi un caffè. Una volta che sei tornato e hai guadagnato un po 'di soldi (sto scherzando),clicca sul pulsante (rosso) STOP sul MetaEditor per interrompere il debug.
Figura 28. Interruzione del debugger
Quello che abbiamo effettivamente fatto qui è stato controllare che il nostro EA controlli solo un trade all'apertura di una nuova Barra e che il nostro EA funzioni correttamente. C'è ancora molto margine per le modifiche al nostro codice EA.
Permettetemi di chiarire, a questo punto, che il terminale di Trading deve essere connesso a Internet, altrimenti il debug non funzionerà perché il terminale non sarà in grado di fare trading.
3.2 TESTARE LA NOSTRA STRATEGIA EA
A questo punto, ora vogliamo testare il nostro EA utilizzando lo Strategy Tester integrato nel Terminale di Trading. Per avviare Strategy Tester, premere CTRL+R oppure cliccare sul menu Visualizza sulla Barra dei Menu del Terminale e fare click su Strategy Tester come mostrato di seguito
Figura 26. Avvio dello Strategy Testing
Il Tester (Strategy Tester) è mostrato nella la parte inferiore del terminale. Per poter vedere tutte le impostazioni del Tester, c’è bisogno di espanderlo / ridimensionarlo. Per fare questo, sposta il puntatore del mouse sul punto indicato dalla freccia rossa (come mostrato di seguito)
Figura 27. La finestra dello Strategy Tester.
Il puntatore del mouse si trasforma in una freccia a doppia estremità, tieni premuto il mouse e trascina la linea verso l'alto. Fermati quando scopri che puoi vedere tutto nel tab delle impostazioni.
Figura 28. Tab delle Impostazioni dello Strategy Tester
- Seleziona l'EA che vuoi testare
- Seleziona la coppia di Valute da utilizzare per il test
- Selezionare il Periodo/Intervallo di Tempo da utilizzare per il test
- Seleziona Periodo Personalizzato e imposta le date su 5
- Impostare le date per il periodo personalizzato da utilizzare per il test
- L'Esecuzione è Normale
- Seleziona l'importo del deposito in dollari USA da utilizzare per il test
- Imposta l'Ottimizzazione su Disabilita (ora non stiamo ottimizzando, vogliamo solo testare)
- Clicca su questo pulsante quando sei pronto per iniziare il test.
Prima di cliccare sul pulsante Start, analizziamo gli altri tab sul Tester
Tab Agents
Il processore utilizzato dal Tester per effettuare il Test. Dipende dal tipo di processore del tuo Computer. Il mio è solo un (1) processore core.
Figura 29. Tab Agents dello Strategy Tester
Una volta che l'agente, vedrai qualcosa di simile alla figura seguente
Figura 30. Tab Agents dello Strategy Testert durante un test
Tab Journal
Qui vengono visualizzati tutti gli eventi in corso durante il periodo di prova
Figura 31. Tab Journal dello Strategy Tester che mostra le attività di trading
Tab Inputs
Qui è possibile specificare i parametri di input per l'EA.
Figura 32. Tab Inputs dello Strategy Tester
Se stiamo ottimizzando il nostro EA, allora dovremo impostare i valori nell'area cerchiata.
- LoStart sono i valori con cui vuoi che il Tester cominci.
- Lo Step è il tasso di incremento per il valore selezionato e
- LoStop è il valore al quale il Tester smetterà di incrementare il valore per quel parametro.
Tuttavia, nel nostro caso non stiamo ottimizzando il nostro EA, quindi non avremo bisogno di toccarlo per ora.
Una volta che tutto è impostato, torniamo ora al tabSettings e cliccare sul pulsante Start. Quindi, il Tester inizia il suo lavoro. Tutto ciò che devi fare ora è andare a bere un’altra tazza di caffè se vuoi o, se sei come me, potresti voler monitorare ogni evento, quindi consulti il tab Journal .
Tab Graph
Una volta che inizi a visualizzare i messaggi sugli ordini inviati nel Tab Journal, potresti quindi voler passare a una NUOVO tab chiamato Graph che è stato appena creato. Una volta passato al tab Graph, vedrai il grafico continuare ad aumentare o diminuire in base ai casi a seconda dell'esito delle tue operazioni.
Figura 33. Il risultato del grafico per il Test dell’Expert Advisor
Tab Results
Una volta completato il test, vedrai un altro tab chiamatoResults. Passare al tab Results e vedrai il riepilogo del test che abbiamo appena effettuato.
Figura 34. II tab Strategy Tester Results mostra il riepilogo dei risultati dei test
Puoi vedere il Profitto Lordo totale, l'Utile Netto, il totale delle transazioni, il totale delle perdite delle transazioni e molto altro ancora. È davvero interessante vedere che abbiamo circa 1.450 dollari USA all’interno del periodo che abbiamo selezionato per il nostro test. Almeno abbiamo qualche profitto.
A questo punto, permettetemi di dirvi una cosa molto chiara. Scoprirai che le impostazioni per i parametri EA che vedi nello Strategy tester sono diverse dalle impostazioni iniziali dei parametri di input dell'EA. Ti ho appena dimostrato che puoi modificare uno qualsiasi di questi parametri di input per ottenere il meglio dal tuo EA. Invece di usare un periodo di 8 ciascuno per la Media Mobile e l’ADX, l'ho modificato in 10 per la Media Mobile e 14 per l’ADX. Cambio anche lo Stop Loss, passando da 30 a 35. Ultima cosa ma non meno importante, ho deciso di utilizzare un intervallo di tempo di 2 Ore. Ricorda, questo è lo Strategy Tester.
Se vuoi visualizzare un report completo del test, allora clicca con il pulsante destro del mouse su qualsiasi punto del tab Results, vedrai un menu. Da questo menu, seleziona ‘Salva con Report’.
Figura 35. Salvataggio del risultato del test
Apparirà la finestra di dialogo salva; digita un nome per il tuo report (se vuoi, altrimenti puoi lasciare il nome predefinito) e cliccare sul pulsante Salva. L'intero report verrà salvato per te in formato HTML.
Per visualizzare il grafico per il test che è stato eseguito, clicca su Open Chart e vedrai il grafico mostrato
Figura 36. Il grafico mostra il test
Questo è tutto.Abbiamo scritto e testato con successo il nostro EA e ora abbiamo un risultato con cui lavorare. Ora puoi tornare al tab delle Impostazioni dello Strategy Tester ed effettuare il test per un altro Intervalli di Tempo/Periodo.
Assegnazione
Vorrei che tu eseguissi il test usando diverse coppie di valute, diversi intervalli di tempo, diversi Stop Loss, diversi Take profit e vedere come si comporta l'EA. Puoi anche provare nuovi valori di Media Mobile e ADX. Come ho detto prima, questa è l'essenza dello Strategy Tester. Mi piacerebbe anche che tu condividessi i tuoi risultati con me.
Conclusione
In questa guida step by step, siamo stati in grado di esaminare i passaggi fondamentali richiesti per scrivere un semplice Expert Advisor basato su una strategia di trading sviluppata. Abbiamo anche analizzato il modo in cui controlliamo il nostro EA per gli errori utilizzando il debugger. Abbiamo, inoltre, parlato di come testare le prestazioni del nostro EA utilizzando lo Strategy Tester. Con questo, siamo stati in grado di vedere la potenza e la solidità del nuovo linguaggio MQL5. Il nostro EA non è ancora perfetto o completo in quanto molte altre modifiche devono ancora essere effettuate per poterlo utilizzare per il trading reale.
C'è ancora molto da imparare: voglio che tu legga di nuovo l'articolo insieme al manuale MQL5 e provi tutto ciò che hai imparato in questo articolo. Posso assicurarti che sarai un grande sviluppatore EA in un futuro non molto lontano.
Buona codifica!
Tradotto dall’inglese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/en/articles/100





- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso