How to call indicators in MQL5

KlimMalgin | 12 March, 2010

Introduction

In MQL5 there are several ways to call indicators, and they are mostly carried out using IndicatorCreate() and iCustom() functions. Moreover, these functions only return indicator handle, and further work on indicators is done through it. So what is a handle? How to deal with IndicatorCreate() and iCustom() functions? And how your expert will get indicator data? All these questions are covered in this article.

Creating a source file

To begin with our expert, let's create it's source file. We'll do this pretty much as in the MetaEditor4 by calling the MQL5 Wizard from File -> New menu. On the first step select the Expert Advisor and press Next.


On the second step we are offered to enter the name, the author's contact details and the input parameters for the Expert Adviser. Enter just the Name of advisor (required field) and the Author. You can add input parameters later directly in the program code, if necessary.


After you click Finish the Wizard will create the following source code:

//+------------------------------------------------------------------+
//|                                                   SampleMQL5.mq5 |
//|                                             Copyright KlimMalgin |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "KlimMalgin"
#property link      ""
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Source file structure

Let's briefly examine the source file structure of an expert and go further.

Program code begins with a header. It's a comment, containing file name and author. After the title come Program Properties (#property) of an expert. Here the program version is automatically indicated, as well as the author's name and the link to your page, if you have entered them. After describing the properties, the input parameters are specified (we haven't specified them yet), and then the global variables are declared.

I want to note, that properties should be set in the main mql5-file. All properties from included files will be ignored!

Next come three functions that process events:

Connecting indicators

In MQL5 methods of working with indicators have changed! In MQL4 to get the indicator's value at particular bar, the index of that bar passed to iCustom() function as parameter. Now we don't get the indicator's buffer value, but we get it's handle.  And using this handle, we can obtain the indicator's values on a given interval using special function called CopyBuffer(). So, what is a handle?

Handle is a unique identifier, created to use with certain indicator. It is represented as an integer and is stored in the int type of variable. There are 2 ways of getting indicator handle in MQL5:\

Both methods are equally valid, and it's up to you to decide which one of them to use. Let's consider both ways in details.

Getting indicator's handle using IndicatorCreate() function

So, as a result of successful execution of IndicatorCreate() function we will get indicator's handle, and in case of failure the function will return INVALID_HANDLE value equal to -1. If the called indicator has parameters, then before calling the function we must fill an array of MqlParam type parameters. Each element of this array is represented as a structure of MqlParam type, containing 4 fields. The first field of this structure indicates which of the remaining three fields are used as a parameter for the indicator.

Let's write the code that will create handles of Moving Average and ParabolicSAR indicators. We'll begin with declaration of global variables, that will store the handles, and declare an array of MqlParam type to set parameters for the indicators:

int MA1 = 0,            // Declaring variable to store fast MA handle
    MA2 = 0,            // Declaring variable to store slow MA handle
    SAR = 0;            // Declaring variable to store SAR handle
    
MqlParam params[];      // Array for storing indicators parameters

This code must be placed immediately after defining expert properties.

Now, when the variables are declared, you can fill the parameters array and call IndicatorCreate() function. It's better to do this in the OnInit() function, as the indicator will be created once in the beginning of program, and not on each tick!

Let's fill the parameters array and call the first indicator:

int OnInit()
{
//---
   // Setting the params size equal to number of called indicator parameters
   ArrayResize(params,4);

   // Setting the period of fast MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Offset
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), // Symbol used to calculate the indicator
                         0,        // Timeframe. 0 means current
                         IND_MA,   // Indicator type from the ENUM_INDICATOR enumeration
                         4,        // Number of parameters passed in the params array
                         params    // Parameters array
                         );   
//---
return(0);
}

As the params array was declared as a dynamic array, i.e. the size of array was not set in brackets, then before using it we must set it's size. Using ArrayResize() function we'll define the size equal to the number of parameters, which we will transfer in the array. For the moving average four parameters are needed.

The first parameter is MA period. It is set as an integer. Therefore, to the type field of the first parameter we will set the TYPE_INT value, thereby our IndicatorCreate() function will "understand", that value should be taken from the integer_value field. Since all parameters for the moving average have int type, they will be set accordingly.

Once the array is filled, you can call the IndicatorCreate() function. Its output will be stored in the MA1 variable. There should be no difficulty calling the function, and just five parameters must be transfered into it:

That's it! We've got the fast MA handle and have to get the slow MA and SAR indicators handles. As a result, the OnInit() function will look like:

int OnInit()
  {
//---

   // Setting the params size equal to number of called indicator's parameters
   ArrayResize(params,4);

//*    Calling indicators   *
//***************************
   // Setting the period of fast MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Offset
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   // Setting the period of slow MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=21;
   // Offset   
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Calculation method: simple averaging
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Price type for calculation: the Close prices
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA2 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   
   // Changing array size to store the SAR indicator parameters
   ArrayResize(params,2);
   // Step
   params[0].type         =TYPE_DOUBLE;
   params[0].double_value = 0.02;
   // Maximum
   params[1].type         =TYPE_DOUBLE;
   params[1].double_value = 0.2;
   
   SAR = IndicatorCreate(Symbol(), 0, IND_SAR, 2, params);
   
//---
   return(0);
  }

To get the slow MA handle you can only change the period from 5 to 21. And when the ParabolicSAR is called, the floating point numbers (double) are transferred as it's parameters. When filling the array you must take this into account, and put the TYPE_DOUBLE in the type field, and the parameter itself to the double_value field, respectively. Also, the SAR requires only two parameters, so the array size is changing to 2 using ArrayResize().

For now the IndicatorCreate() function ends. In my opinion, this method of calling indicators is most convenient in object-oriented approach, when the library of classes is created to work with indicators. This library will make large-scale projects easier to process. And as for writing and testing quick experts it is best to use the following method.

Getting handle using technical indicators functions

The second method, that allows you to get indicator handle, is calling one of the of technical indicators functions. Each of these functions is intended to get the handle of one of the standard indicators, shipped with MetaTrader 5. They all have a certain set of parameters, depending on the indicator for which they are intended. For example, the for CCI indicator the iCCI() function is called, and it looks as follows:

   CCI = iCCI(
              Symbol(),            // symbol name
              PERIOD_CURRENT,      // period
              20,                  // averaging period
              PRICE_CLOSE          // price type or handle
              );

Other indicator's handle can be submitted as the last parameter in technical indicators function instead of price type. Then the indicator, called in this way, will be calculated using not the price data, but data of other indicator.

The iCustom() function causes particular interest among other technical indicators functions, because it can call any indicators, including custom ones. Let's write the code similar to the above, but using the iCustom() function:

int OnInit()
  {
//---

   MA1 = iCustom(NULL,0,"Custom Moving Average",
                          5,          // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   MA2 = iCustom(NULL,0,"Custom Moving Average",
                          21,         // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   SAR = iCustom(NULL,0,"ParabolicSAR",
                          0.02,        // Step
                          0.2          // Maximum
                 );

//---
   return(0);
  }

Bear in mind, that it is best to get handle once during expert start, so let's make call in the OnInit() function.

iCustom() function contains the following parameters:

  1. Name of working tool, which data is used to calculate the indicator. NULL - means the current tool. You can use Symbol () instead of NULL, not much difference. In case of the Symbol(), the name of an instrument is transfered to iCustom(), but in case of NULL, iCustom() finds the financial instrument itself.
  2. Chart period. To specify the period you can use predefined chart timeframes, as well as 0 - to refer to the current timeframe.
  3. Indicator name. Called indicator should be compiled and should be in the "MQL5\Indicators\" folder or any of its sub-folders.
  4. Remaining parameters. These values will be used as input parameters for the called indicator, so their type and the sequence must match to each other. If they are not specified, then default values will be used.

As it was said - this method is more convenient to use with indicators, if you you don't have library that you have used to work. Now, when handles are received, you can begin to work with the indicators values!

Getting indicator's data

The cute thing of working with indicator's data in MQL5 is that now we have the opportunity to get data only at such interval, that we need. You can set the interval in different ways:

To read the indicator's date the CopyBuffer() function is used. What method of getting data will be used depends on what input parameters will be transferred to this function.

Let's write the code that copies indicator data (we've got their handles earlier) to the arrays:

void OnTick()
  {
//---

// Dynamic arrays to store indicators values
double _ma1[],
       _ma2[],
       _sar[];

// Setting the indexing in arrays the same as in timeseries, i.e. array element with zero
// index will store the values of the last bar, with 1th index - the last but one, etc.
   ArraySetAsSeries(_ma1, true);
   ArraySetAsSeries(_ma2, true);
   ArraySetAsSeries(_sar, true);

// Using indicators handles, let's copy the values of indicator
// buffers to arrays, specially prepared for this purpose
   if (CopyBuffer(MA1,0,0,20,_ma1) < 0){Print("CopyBufferMA1 error =",GetLastError());}
   if (CopyBuffer(MA2,0,0,20,_ma2) < 0){Print("CopyBufferMA2 error =",GetLastError());}
   if (CopyBuffer(SAR,0,0,20,_sar) < 0){Print("CopyBufferSAR error =",GetLastError());}


//---
  }

Now all our work will focus in OnTick() function, because on each tick  indicators data are changing and must be updated.

First, let's declare dynamic arrays for each indicator buffer. In my opinion, it is more convenient to work with indicator data, if the array indexing will be the same as in timeseries, i.e. in the array element 0 there will be data of the last bar, in number 1 element - the last but one, etc. The method of indexing is set using the ArraySetAsSeries() function.

Once the method of indexing is set, we can begin filling the arrays. To calculate each indicator we will call the CopyBuffer() function with following set of parameters:

  1. Indicator handle, which data you want to get.
  2. Indicator buffer number. As the MA and SAR indicators have one buffer, then we'll set them as 0. If there are more than one buffers, then the second will be number 1, the third will be number 2, etc.
  3. Initial bar, from which the counting starts.
  4. The number of bars you want to copy.
  5. The array{/0} to which the data is copied.

On successful call, the CopyBuffer() function will return the number of copied elements, on failure it will return -1. In case of failure, the error must be tracked, so if the returned value is less than 0 - we will write the error in the experts log using Print() function.

Conclusion

For now our work with the indicators is far from end. This article covers only the basic methods of accessing their data. And for convenience it is best to use object-oriented approach, that is implemented in MQL5 on a fairly good level!