Download MetaTrader 5

A Quick Start Or a Short Guide for Beginners

25 September 2012, 09:36
Dmitriy Parfenovich
3
14 356

Introduction

Hello dear reader! In this article, I will try to explain and show you how you can easily and quickly get the hang of the principles of creating Expert Advisors, working with indicators, etc. It is beginner-oriented and will not feature any difficult or abstruse examples. So the article may probably not be so inspiring and informative for those of you who already know how to program Expert Advisors.

Expert Advisor and Its Structure

Expert Advisor is a program written in the MQL language that specifies conditions for doing the trade or keeping aside.

Basically, the structure of an EA can be made up of a great number of blocks but in order to make it easier to understand, I am going to give a very simple example generated by default in MetaEditor.

The entire EA can be visually divided into 4 parts each of which is responsible for a certain part of the work to be performed.

The main EA blocks 
Fig. 1. The main EA blocks

  1. Parameter Block contains information for the terminal allowing it to handle the EA in a proper way. The most common parameters are the EA version, name of manufacturing company and a brief description.

  2. OnInit() Block gains control once the EA is loaded into the terminal. It can contain various data related to the initialization of the EA - declaring variables and arrays, getting indicator handles, etc. That is, this block does not have any functions that would be directly associated with trading.

  3. OnDeinit() Block acts as the inverse of the OnInit() Block. It is called when the EA completes its operation (EA/Terminal shutdown or unsuccessful initialization of an EA). One of the main functions of this block is deallocation of memory space occupied by the EA when it is no longer needed. In other words, it describes processes of deleting variables, arrays and indicator handles, etc.

  4. OnTick() Block is called every time the new information on the symbol (currency pair) is received from the server. It specifies conditions for doing the trade and functions of the trade itself.

Example of a new document generated by default in MetaEditor
   Fig. 2. Example of a new document generated by default in MetaEditor

Let me explain it using the above example. We have a code of the "empty" Expert Advisor, a sort of an Expert Advisor template that will afterwards need to be filled.
What we can see here is as follows:

  • the first five lines (lines 1 through 5) represent comments containing the name of the EA (file name), the name of the manufacturing company and its website. You can write here anything you like. This text will not be seen anywhere and can even be skipped. The information it contains only targets the developer;

  • the next 3 lines (lines 6 through 8) represent the Parameter Block. This information can be observed when starting the EA in the terminal;

  • it is followed by the OnInit() function (lines 12 through 19). This is the OnInit() Block. This function does not get any parameters but returns (although it may as well not) the initialization code;

  • the OnDeinit(const int reason) function goes next (lines 22 through 26). This is the OnDeinit() Block. It has one parameter that specifies the EA shutdown reason.
    If the EA initialization is unsuccessful, this function receives a relevant code as a parameter;

  • the last function is OnTick() (lines 30 through 34). This is the earlier described OnTick() Block. This block can be said to be the "brains" of the EA as it comprises all functions in charge of trades.

As I said before, the structure can be much more complex and be made of a good deal of blocks unlike this easy to grasp example. When you feel that this is not enough, you can add your own blocks.
 

Indicators and How to Handle Them

Indicators are small programs written in MQL that are displayed in the price chart or in a separate window below the price chart and enable us to perform technical analysis of the market.

All indicators can be classified into two types: trend-following indicators and oscillators.

Trend-following indicators are, as a rule, drawn in the price chart and are used to identify the trend direction, while oscillators can normally be seen below the price chart and serve to identify entry points.

Most indicators have at least one buffer (indicator buffer) that contains its reading data at a given time. Like an EA, the indicator has its symbol and time frame on which it is calculated.

Indicator buffer can be considered as a queue the last element of which is a running value.

Example of the Moving Average Indicator
   Fig. 3. Example of the Moving Average Indicator

Indicator buffer is an array where the first element (with 0 index) carries data on the rightmost candlestick and the following element (with index 1) carries data on the second candlestick on the right, etc. Such arrangement of elements is called time series.

Take a look at the example as follows:
Assume the currency pair we have is EUR/USD, time frame is 1 hour.
First off, we need to add the indicator to the EA and get its handle.

Handle is a unique pointer to the indicator that enables us to address that indicator anywhere in the program.

int iMA_handle; 
iMA_handle=iMA("EURUSD",PERIOD_H1,10,0,MODE_SMA,PRICE_CLOSE);
Let us look at it closer.

The first line defines a variable that is going to store the indicator handle. The second line calls the indicator (here, the Moving Average indicator), specifies its parameters and saves the handle into the variable for future use.
After typing "iMA(" in MetaEditor, a tooltip will appear above that line showing comma separated indicator call parameters.

Example of the tooltip for the Moving Average indicator parameters
   Fig. 4. Example of the tooltip for the Moving Average indicator parameters

We can see the following parameters listed from left to right:

  1. symbol name (appears in bold letters in the tooltip) is a text parameter, currency pair (symbol);
  2. time frame;
  3. indicator period (here, the averaging period);
  4. chart shift by N bars forward/backward. A positive number denotes the chart shift by N bars forward, whereas a negative number denotes the chart shift by N bars backward;
  5. averaging method;
  6. price applied or a handle of a different indicator.

There is a unique set of variables and their types for every indicator. If you come across an unknown indicator, the information on it can always be found in the built-in context Help. For example, once you have typed iMA and pressed F1, a Help window will open providing information on that specific indicator and a detailed description of all its properties.

Example of calling the Help window for the description of the indicator by pressing F1
   Fig. 5. Example of calling the Help window for the description of the indicator by pressing F1

After writing the code and starting the EA in the terminal, we will see (once the EA appears in the top right corner of the price chart) that the indicator is missing from the chart. This is not an error - it was so intended. In order for it appear, we need to add another line:

ChartIndicatorAdd(ChartID(),0,iMA_handle);

Let us now see what it does. Hover the cursor over the ChartIndicatorAdd command and press F1 to read the Help information on the purpose of the command. It says that this command:

Adds an indicator with the specified handle into a specified chart window.

The second parameter which is equal to zero is the subwindow number. Subwindows usually contain oscillators, below the price chart. Remember? There can be a lot of them. To display the indicator in the subwindow, you only need to specify the subwindow number so that it is greater than the already existing number by 1, i.e. the number following the last existing one.

Having changed the code line as follows:

ChartIndicatorAdd(ChartID(),1,iMA_handle);

our indicator will appear in the subwindow below the price chart.

Now it is time to try to get some data from the indicator. For this purpose, we declare a dynamic array, arrange array indexing as time series for the sake of convenience and copy indicator values into this array.

double iMA_buf[];
ArraySetAsSeries(iMA_buf,true);
CopyBuffer(iMA_handle,0,0,3,iMA_buf);

The above example shows that we have declared the dynamic array iMA_buf[] of double type as the Moving Average indicator is based on prices and prices have fractions.

The next line sets the indexing for the array so that the elements with smaller indices store older values, while the elements with greater indices store more recent values. This is used for convenience to avoid confusion as indicator buffers in all indicators are indexed as time series.

The last line serves to copy the indicator values into the iMA_buf[] array. These data are now ready to be used. 

 

Orders, Trades and Positions

Let us start with orders.

  • Orders are trade requests accepted by the trade server. If the request is invalid, it will be rejected.
    To avoid the difficulty of filling in the trade request, I will later show you how this can be done using the standard libraries making it all much easier.
    There are 2 types of orders: market (for immediate execution) and pending.
Market orders represent instructions to sell or buy a certain amount of a specified financial instrument at the current market price.
Pending orders represent instructions to execute the trade subject to certain conditions. Pending orders have a certain expiration time upon which they are deleted.
  • Trades represent the results of the execution of orders (instructions to execute a trade). Every trade is based on a certain single order, whereas a single order can result in multiple trades. For example, an order to buy 10 lots can be executed by a partial execution of a number of consecutive trades. Trades are always stored in the trade history and cannot be modified. The terminal displays trades in the "History" tab.
  • Positions represent the outcome of orders in action. Only one position, either Long or Short, can be opened for each single symbol.

To make it clearer, let me illustrate it with an example: we open a long position of 1 lot, i.e. we place an order at the current market price (for instance) and the size of 1 lot. If the request is valid, it will be sent to the server for processing. As soon as the processing is complete, a position with the order parameters will appear in the "Trade" tab of the terminal. Assume, we then decide to open another long position, also sized at 1 lot. Following the processing of the order, we will not see two orders in the "Trade" tab but rather one position sized at 2 lots. I.e. the position is the outcome of execution of a number of orders.

Let us now proceed to practice. The following structure fields need to be filled in, in order to make a request:

struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; // Type of action
ulong magic; // Expert Advisor ID (magic number)
ulong order; // Order ticket
string symbol; // Trade instrument
double volume; // Requested trade size 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; // Maximum allowed deviation from the requested price
ENUM_ORDER_TYPE type; // Order type
ENUM_ORDER_TYPE_FILLING type_filling; // Order type by execution
ENUM_ORDER_TYPE_TIME type_time; // Order type by duration
datetime expiration; // Order expiration time (for orders of the ORDER_TIME_SPECIFIED type)
string comment; // Comment to the order
};

Since there are various orders, each order type has its own set of mandatory parameters. I will not address these fields at length. The website offers plenty of information on this matter. If even one of the mandatory parameters for a certain order type is not specified or specified incorrectly, the request will fail.

The above structure is laid out here only to better demonstrate the difficulty arising when filling it in.

Stop Loss and Take Profit

Stop Loss and Take Profit are special orders placed as a "fallback measure". I.e. in case of mistakes or a position opened by the Expert Advisor that is showing a loss, a Stop Loss order can limit losses at a certain predefined level.

Take Profit acts in a similar way, only limiting profit in this case. It may become necessary to stop worrying about closing a position. It will get closed upon reaching a certain price level. In other words, these orders represent our "insurance plan", should the market turn against us or should we want to take profit.

This type of orders cannot be placed separately on its own - it can only modify already existing positions.

Using Standard Libraries

So we have finally made it to the Standard Library. This library comes together with the terminal, hence its name - Standard Library. It comprises functions that facilitate programming EAs and partially undertake complex processes, e.g. trade request generation.

Trade libraries (see also trade classes) are located in the following path: Include\Trade\ and can be added using the #include directive.
Example:

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>

The above classes can be considered as the basic ones as the majority of Expert Advisors can be programmed using only these two classes (libraries). I call them libraries:

  • The first one is designed to placed and modify orders.
  • The second one serves to obtain information on already existing positions.

At times, another library can prove useful:
#include <Trade\OrderInfo.mqh>
It contains functions for working with orders, if, say, our strategy requires the use of pending orders.

Remember the trade request structure full of various parameters that require knowledge to be used properly?
Now, I will give you an example of a trade request made using the library:
CTrade m_Trade;
m_Trade.Sell(lot,symbol_name,price,sl,tp,comment);

There is a total of 6 parameters here, only one of which is mandatory (the order size - that is the first parameter).
I will now specify each one of them:

  • lot is the size of the order to be placed;
  • symbol_name is the symbol (currency pair) the order applies to (if none is specified, the current symbol of the Expert Advisor is used);
  • price is the opening price (since this is the function for opening an active order, its price might be unspecified in which case it will automatically be obtained directly from the price chart);
  • sl is the price at which the order will close, should the price not be to our advantage (it may be left out if the strategy does not imply the use of stop loss);
  • tp is the price at which the order will close, should the price take the required direction, i.e. it takes profit (it may be left out if the strategy does not imply the use of take profit);
  • comment is the comment to the order, e.g. specifying the reason for placing the order.

There is a number of ways to close a position:

  1. to close the whole position
    CPositionInfo m_Position;
    m_Position.Select(symbol_name);
    m_Trade.PositionClose(symbol_name);
  2. to close the position by placing a reverse order of the same size
    CTrade m_Trade;
    m_Trade.Buy(lot,symbol_name,price,sl,tp,comment);
  3. by using a more complicated method whereby all open positions are first searched through to single out the one that meets the required parameters (symbol, type, magic number, position identifier, etc.) to be further closed.
    I am not going to give any example of the above due to its difficulty for beginners.

Putting It All Together

It is high time now to put the newly-acquired knowledge into a single Expert Advisor.

//+------------------------------------------------------------------+
//|                                           fast-start-example.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>                                         //include the library for execution of trades
#include <Trade\PositionInfo.mqh>                                  //include the library for obtaining information on positions

int               iMA_handle;                              //variable for storing the indicator handle
double            iMA_buf[];                               //dynamic array for storing indicator values
double            Close_buf[];                             //dynamic array for storing the closing price of each bar

string            my_symbol;                               //variable for storing the symbol
ENUM_TIMEFRAMES   my_timeframe;                             //variable for storing the time frame

CTrade            m_Trade;                                 //structure for execution of trades
CPositionInfo     m_Position;                              //structure for obtaining information of positions
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   my_symbol=Symbol();                                      //save the current chart symbol for further operation of the EA on this very symbol
   my_timeframe=PERIOD_CURRENT;                              //save the current time frame of the chart for further operation of the EA on this very time frame
   iMA_handle=iMA(my_symbol,my_timeframe,40,0,MODE_SMA,PRICE_CLOSE);  //apply the indicator and get its handle
   if(iMA_handle==INVALID_HANDLE)                            //check the availability of the indicator handle
   {
      Print("Failed to get the indicator handle");              //if the handle is not obtained, print the relevant error message into the log file
      return(-1);                                           //complete handling the error
   }
   ChartIndicatorAdd(ChartID(),0,iMA_handle);                  //add the indicator to the price chart
   ArraySetAsSeries(iMA_buf,true);                            //set iMA_buf array indexing as time series
   ArraySetAsSeries(Close_buf,true);                          //set Close_buf array indexing as time series
   return(0);                                               //return 0, initialization complete
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(iMA_handle);                             //deletes the indicator handle and deallocates the memory space it occupies
   ArrayFree(iMA_buf);                                      //free the dynamic array iMA_buf of data
   ArrayFree(Close_buf);                                    //free the dynamic array Close_buf of data
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   int err1=0;                                             //variable for storing the results of working with the indicator buffer
   int err2=0;                                             //variable for storing the results of working with the price chart
   
   err1=CopyBuffer(iMA_handle,0,1,2,iMA_buf);               //copy data from the indicator array into the dynamic array iMA_buf for further work with them
   err2=CopyClose(my_symbol,my_timeframe,1,2,Close_buf);    //copy the price chart data into the dynamic array Close_buf for further work with them
   if(err1<0 || err2<0)                                    //in case of errors
   {
      Print("Failed to copy data from the indicator buffer or price chart buffer");  //then print the relevant error message into the log file
      return;                                                               //and exit the function
   }

   if(iMA_buf[1]>Close_buf[1] && iMA_buf[0]<Close_buf[0])   //if the indicator values were greater than the closing price and became smaller
     {
      if(m_Position.Select(my_symbol))                     //if the position for this symbol already exists
        {
         if(m_Position.PositionType()==POSITION_TYPE_SELL) m_Trade.PositionClose(my_symbol);  //and this is a Sell position, then close it
         if(m_Position.PositionType()==POSITION_TYPE_BUY) return;                              //or else, if this is a Buy position, then exit
        }
      m_Trade.Buy(0.1,my_symbol);                          //if we got here, it means there is no position; then we open it
     }
   if(iMA_buf[1]<Close_buf[1] && iMA_buf[0]>Close_buf[0])  //if the indicator values were less than the closing price and became greater
     {
      if(m_Position.Select(my_symbol))                     //if the position for this symbol already exists
        {
         if(m_Position.PositionType()==POSITION_TYPE_BUY) m_Trade.PositionClose(my_symbol);   //and this is a Buy position, then close it
         if(m_Position.PositionType()==POSITION_TYPE_SELL) return;                             //or else, if this is a Sell position, then exit
        }
      m_Trade.Sell(0.1,my_symbol);                         //if we got here, it means there is no position; then we open it
     }
  }
//+------------------------------------------------------------------+

Let us test our Expert Advisor with the parameters as follows:

  • symbol - EURUSD;
  • time frame - H1;
  • trade mode "Opening prices only". 

Since we use the indicator values and closing prices starting with the first bar (zero bar is a current, active bar), the chart will not get redrawn. It means that we can use the "Opening prices only" trade mode. It will not affect the testing quality but will make it run faster.

And here are quick testing results using historical data. 

Our Expert Advisor testing results
   Fig. 6. Our Expert Advisor testing results

The drawdowns certainly cannot go unnoticed. However this article did not aim at programming a "super Expert Advisor" that would have great profit potential with minimal drawdown but rather at demonstrating how easily one can make an EA when armed with basic knowledge.
We have got the Expert Advisor consisting of less than hundred lines of code.
 

Conclusion

This article has covered the main principles to consider when programming an EA. We have learned how to use the built-in context Help in MetaEditor 5 to obtain information on various functions, got the general idea of orders and positions and embraced the use of the standard libraries.


Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/496

Attached files |
Last comments | Go to discussion (3)
okwh
okwh | 25 Sep 2012 at 11:51

some small questions:

1  what happen or rehappen  when time-period of chart changed during runing?

2  ima_handle defined at OnInit(),  iMA_handle=iMA("EURUSD",PERIOD_H1,10,0,MODE_SMA,PRICE_CLOSE);

 is it possible to change this handle's parameters dynamic by programself like change PERIOD_H1,10 to PERIOD_M15, 39 during runing?

3 what is deal? 

Icarus
Icarus | 27 Sep 2012 at 04:29

Congratulations!

Extremelly simple, well-written code... I think by doing reference to the library, you did in few lines what I did in almost 100 (only to open long and close long positions).

I refer specially to these lines:

 if(m_Position.Select(my_symbol))                     //if the position for this symbol already exists
        {
         if(m_Position.PositionType()==POSITION_TYPE_SELL) m_Trade.PositionClose(my_symbol);  //and this is a Sell position, then close it
         if(m_Position.PositionType()==POSITION_TYPE_BUY) return;                              //or else, if this is a Buy position, then exit
        }
      m_Trade.Buy(0.1,my_symbol);                          //if we got here, it means there is no position; then we open it
     }
   if(iMA_buf[1]<Close_buf[1] && iMA_buf[0]>Close_buf[0])  //if the indicator values were less than the closing price and became greater
     {
      if(m_Position.Select(my_symbol))                     //if the position for this symbol already exists
        {
         if(m_Position.PositionType()==POSITION_TYPE_BUY) m_Trade.PositionClose(my_symbol);   //and this is a Buy position, then close it
         if(m_Position.PositionType()==POSITION_TYPE_SELL) return;                             //or else, if this is a Sell position, then exit
        }
      m_Trade.Sell(0.1,my_symbol);                         //if we got here, it means there is no position; then we open it
     }
 

The only thing I missed may be a command to trade only when a new bar appears. There was a discussion in: www.mql5.com/en/forum/5762 

I use the following code, posted there by mogplus8:

static int   LastBarCount=0;

   if(Bars(_Symbol,_Period)>LastBarCount)
      LastBarCount=Bars(_Symbol,_Period);
   else
      return; 

tao zemin.
tao zemin. | 22 Mar 2014 at 09:29

I am somewhat puzzled about your statement about Trades.

The trades presentted in the article is very close to "seals" in artiel http://mqlmagazine.com/leading-article/orders-positions-and-deals-part-i/. Are they the same thing ? what are their differences?

By the way, in the linked page, the author do states: Each order placed in the market is a trade itself, with its own result, independent of the others.  

I paste the words here just to clarify the possible misconceptions to those who paid attention to the comments.

thanks you.

Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

False trigger protection for Trading Robot False trigger protection for Trading Robot

Profitability of trading systems is defined not only by logic and precision of analyzing the financial instrument dynamics, but also by the quality of the performance algorithm of this logic. False trigger is typical for low quality performance of the main logic of a trading robot. Ways of solving the specified problem are considered in this article.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.