Download MetaTrader 5

How to call indicators in MQL5

12 March 2010, 11:34
KlimMalgin
5
7 914

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:

  • OnInit() - Runs once at expert start, at character change or at period change, on which the expert is set.
  • OnDeinit(const int reason) - Also executes once, when the expert finishes it's work or when the reinitialization is carried out. The reason parameter contains the reason code of deinitialization.
  • OnTick() - Starts every time, when you receive a new tick on the chart, where the expert is attached.

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:\

  • Calling IndicatorCreate() function that will return indicator handle in case of success, and -1 in case of failure.
  • Calling one of the technical indicators functions. The result of their execution is similar.

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:

  • First is the name of the working tool. The Symbol() function will return the name of the current tool, on which the expert is running. 
  • The second parameter specifies the timeframe, on which the indicator values are counted.
  • The third parameter specifies the type of indicator, that will be called.
  • The fourth parameter specifies the number of parameters to be passed in the params array.
  • And, finally, the fifth parameter is the input parameters array of the indicator.

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:

  • Getting the specified number of bars relative to selected bar.
  • Getting the specified number of bars relative to selected date.
  • Getting data from the indicator at a given time interval, i.e. specifying the start and the end dates.

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!

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

Attached files |
samplemql5_en.mq5 (5.13 KB)
Last comments | Go to discussion (5)
ts_
ts_ | 18 Mar 2010 at 03:16
useful article. thanks a lot.

OldZ
OldZ | 12 May 2010 at 15:08

Good ++ 

Thanks.

jordiqqq
jordiqqq | 9 Jan 2013 at 18:30

To me it doesn-t let me compile the file.

I don-t know if there is any idea what id going on.

Thanks,

 

Jordi 

iJSmile
iJSmile | 7 Mar 2015 at 00:26
Thank you for huge work!
gr101
gr101 | 15 Oct 2015 at 21:29

Thanks!

Below is a simple example of this that I put together while playing around.

If it's wrong please correct me and I'll amend it.

What I do find strange is the values don't seem to match up to a simple moving average on the chart.

################### In a script

   int period = 21;        // The 21 bar moving average
   int sampleSize = 100;   //This is the number of bars of data you want to fetch
   int handle = iCustom(Symbol(),0,"Examples\\Custom Moving Average",
                          period,          // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

 

void OnTick()
{

   // This is the array where you moving averages will get copied into.
   // Each item in here is a moving average value
   // The first item in this array (item at position 0) is the most recent
   // The last item is the last item in the array ArraySize() - 1
   double movingAverageValues[];   
  
   ArraySetAsSeries(movingAverageValues, true);
   if (CopyBuffer(handle,0,0,sampleSize,movingAverageValues) < 0){Print("CopyBufferMA1 error =",GetLastError());}
  
   double currentMovingAverage = movingAverageValues[0];

   double earliestMovingAverage = movingAverageValues[ArraySize(movingAverageValues) - 1];


################################# In an EA (NOTE: the OnInit and OnTick)

  int OnInit(){

   int period = 21;        // The 21 bar moving average

   int sampleSize = 100;   //This is the number of bars of data you want to fetch
   int handle = iCustom(Symbol(),0,"Examples\\Custom Moving Average",
                          period,          // Period
                          0,          // Offset
                          MODE_SMA,   // Calculation method
                          PRICE_CLOSE // Calculating on Close prices
                 );

   }

OnTick()
{

   // This is the array where you moving averages will get copied into.
   // Each item in here is a moving average value
   // The first item in this array (item at position 0) is the most recent
   // The last item is the last item in the array ArraySize() - 1
   double movingAverageValues[];   
  
   ArraySetAsSeries(movingAverageValues, true);
   if (CopyBuffer(handle,0,0,sampleSize,movingAverageValues) < 0){Print("CopyBufferMA1 error =",GetLastError());}
  
   double currentMovingAverage = movingAverageValues[0];

   double earliestMovingAverage = movingAverageValues[ArraySize(movingAverageValues) - 1];

}

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.