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.
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.
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.
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.
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.