Quick Start: Short Guide for Beginners

Dmitriy Parfenovich | 25 September, 2012

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:

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.

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.

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:


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:

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. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://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:

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.