Custom Indicators 102: How to implement native filters in custom indicators

Learn to code native indicator filters such as MACD and RSI into your mql4 indicators for Metatrader4 (MT4).

Loading

Indicators 102

Today I am going to teach you how to implement native indicator filters via iCustom() in MQL4. To do so, we’ll further ellaborate on top of our previously coded Custom Indicator 101. The resulting code will serve as a template for your future custom indicators.


If you haven’t read my previous post entitled Custom Indicators 101, start there instead. Else, you’ll be lost in translation reading this post.

What are native indicators?

The MQL4 programming language offers a comprehensive set of native indicators to call from our code in order to apply logic and filters to our trading decisions. We can easily read values from RSI, MACD, Stochastic, CCI, Bollinger Bands and many other indicators from our code, without creating compilation dependencies.

For education purposes, we’ll further ellaborate on our previouly coded Custom Indicator 101, adding RSI and MACD filters. We’ll make inputs for the parameters and timeframe selection of these indicators.

Create a new file

Before proceeding, open the Indicator101.mq4 indicator in MetaEditor, and click on File -> Save As. Next, save the file as Indicator102.mq4 to make this a new project and avoid overriding the original indicator.

Save As Sample_Two.mq4
With that out of the way, let’s start coding.

Add the needful input parameters

The first thing we need to do is to add some parameters of MACD and RSI into our indicator, as well as a timeframe input to choose the timeframe of the applied filter.

In total we need to add seven parameters:

    • Timeframe of the filter
    • RSI Period
    • RSI Oversold level
    • RSI Oversbought level
    • MACD Fast EMA
    • MACD Slow EMA
    • MACD Signal EMA.

To do so, we’ll declare external variables in the source code, below the buffer properties. After compilation, these variables can be edited from outside the indicator, in the Inputs Tab of the indicator dialog.

extern ENUM_TIMEFRAMES FTF = PERIOD_CURRENT; // Filter Timeframe
extern int RSIPeriod       = 14;             // RSI Period
extern int RSIOverbought   = 70;             // RSI Overbought
extern int RSIOversold     = 30;             // RSI Oversold
extern int MACDFastEMA     = 12;             // MACD Fast EMA
extern int MACDSlowEMA     = 26;             // MACD Slow EMA
extern int MACDSignal      = 9;              // MACD Signal

If you load the indicator to the chart, the inputs are now editable.

Sample_Two.mql4 Indicator Inputs
But the indicator’s behavior has not changed. We still need to code the actual logic to filter out signals based on these indicators. That is what we are going to do next.

Call the indicators from the code

Remember how the Custom Indicator 101 iterated bars from past to present inside the OnCalculate(…) function? Well, for our filters to work properly, we need to call RSI and MACD for each bar as well, inside the same loop. To do so, we need to implement the iMACD(…) and iRSI(…) functions are described by Metaquotes here and here.

We can very well copy/paste the prototype of these functions in our code and from there, edit the parameters to match ours. However, keep in mind that the iMACD(…) function has to be called twice, because we need two values: the main and the signal line. The resulting code would look like this, which needs to be called for each bar in the chart: try to insert it yourself inside the correct loop inside the OnCalculate(…) function.

// RSI
double rsi_value   = iRSI(
                        Symbol(),
                        FTF,
                        RSIPeriod,
                        PRICE_CLOSE,
                        i);
// MACD Main
double macd_main   = iMACD(
                        Symbol(),
                        FTF,
                        MACDFastEMA,
                        MACDSlowEMA,
                        MACDSignal,
                        PRICE_CLOSE,
                        MODE_MAIN,
                        i);
// MACD Signal
double macd_signal = iMACD(
                        Symbol(),
                        FTF,
                        MACDFastEMA,
                        MACDSlowEMA,
                        MACDSignal,
                        PRICE_CLOSE,
                        MODE_SIGNAL,
                        i);

The MQL4 language implements many native indicators for our usage. Each indicator is available via a prototype function that we should invoke in our code. To learn more about the indicators available and how to call them, visit the technical indicators documentation.

Implement the filters in the code

The only thing left to do is to implement the logic of the filter in the indicator code. We need to evaluate the variables called rsi_value, macd_main and macd_signal before drawing an arrow on the chart.

The trading logic would be the same as in the previously coded indicator, but adding the following filtering criteria to each trading direction.
The conditions to add for a buy signal are:

    • RSI not overbought
    • MACD Main > MACD Signal

The conditions to add for a sell signal are:

    • RSI not oversold
    • MACD Main < MACD Signal

Hence, the complete code of the indicator would be as follows.

//---- File Properties
#property copyright   "Arthur Lopez"
#property link        "http://www.pointzero-trading.com"
#property description "Sample breakout indicator with RSI/MACD Filters."
#property version     "1.0"
#property strict
//---- Indicator drawing and buffers
#property indicator_chart_window
#property indicator_buffers 2
//---- Colors and sizes for buffers
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrTomato
#property indicator_width1 2
#property indicator_width2 2
//---- Input Parameters
extern ENUM_TIMEFRAMES FTF = PERIOD_CURRENT; // Filter Timeframe
extern int RSIPeriod       = 14;             // RSI Period
extern int RSIOverbought   = 70;             // RSI Overbought
extern int RSIOversold     = 30;             // RSI Oversold
extern int MACDFastEMA     = 12;             // MACD Fast EMA
extern int MACDSlowEMA     = 26;             // MACD Slow EMA
extern int MACDSignal      = 9;              // MACD Signal
//---- Buffer Arrays
double ExtMapBuffer1[];
double ExtMapBuffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function
//+------------------------------------------------------------------+
int init()
{
    // First buffer
    SetIndexBuffer(0, ExtMapBuffer1);  // Assign buffer array
    SetIndexStyle(0, DRAW_ARROW);      // Style to arrow
    SetIndexArrow(0, 233);             // Arrow code
    //Second buffer
    SetIndexBuffer(1, ExtMapBuffer2);  // Assign buffer array
    SetIndexStyle(1, DRAW_ARROW);      // Style to arrow
    SetIndexArrow(1, 234);             // Arrow code
    // Exit
    return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    // Start and limit
    int start = 1;
    int limit;
    // Bars counted so far
    int counted_bars = IndicatorCounted();
    // No more bars?
    if(counted_bars < 0)
        return(-1);
    // Do not check repeated bars
    limit = Bars - 1 - counted_bars;
    // Iterate bars from past to present
    for(int i = limit; i >= start; i--)
    {
      // If not enough data...
      if(i > Bars-3) continue;
      // RSI
      double rsi_value   = iRSI(
                           Symbol(),
                           FTF,
                           RSIPeriod,
                           PRICE_CLOSE,
                           i);
      // MACD Main
      double macd_main   = iMACD(
                              Symbol(),
                              FTF,
                              MACDFastEMA,
                              MACDSlowEMA,
                              MACDSignal,
                              PRICE_CLOSE,
                              MODE_MAIN,
                              i);
      // MACD Signal
      double macd_signal = iMACD(
                              Symbol(),
                              FTF,
                              MACDFastEMA,
                              MACDSlowEMA,
                              MACDSignal,
                              PRICE_CLOSE,
                              MODE_SIGNAL,
                              i);
      // Check buy signal
      if(Close[i] > High[i+1] &&
         !(Close[i+1] > High[i+2]) &&
         rsi_value < RSIOverbought &&
         macd_main > macd_signal)
      {
         ExtMapBuffer1[i] = Low[i];
      }
      // Sell check signal
      if(Close[i] < Low[i+1] &&
         !(Close[i+1] < Low[i+2]) &&
         rsi_value > RSIOversold &&
         macd_main < macd_signal)
      {
         ExtMapBuffer2[i] = High[i];
      }
    }
    // Exit
    return(rates_total);
}

Compile and run the indicator

Make sure to paste the complete code in the MetaEditor, save the file and click on compile. The compilation process will generate the indicator binary which we can load to the chart. Make sure the compilation raises no errors.

Compile the indicator
Now we are ready to load the indicator to the chart. The indicator will be listed in the Indicators menu in the Navigator. If the Navigator is hidden, click View -> Navigator -> Indicators.

Load the Indicator102 Indicator to the chart, et voilĂ . Our recently created indicator will load, and filter out signals using RSI and MACD with our desired parameters.

Sample_Two Indicator Coded

Using the indicator

We’ve coded the indicator in such a way that we can control the indicator settings from the inputs, as well as the timeframe of application of the filter. This allows us, for instance, to trade breakouts in the H1 chart using RSI and MACD filters from the D1 chart. Additionally, we can declare our desired RSI overbought and oversold levels to make the indicator more or less sensitive to the RSI reading.

I hope this serves as an useful template for your own custom indicators. The indicator has been coded to avoid repainting, iterating bars from past to present and ignoring the current bar.

Feel free to post your comments or questions below. Thank you.

Loading

Author: Arthur Lopez

Private investor and speculator, software engineer and founder of PZ Trading Solutions.

Leave a Reply

Your email address will not be published. Required fields are marked *