MQL4 Indicator Alerts

Custom Indicators 103: How to implement visual alerts in custom indicators

5
(2)

Today I am going to teach you how to implement alerts in your custom indicators. To do so, we’ll further ellaborate on top of our previously coded Custom Indicator 102. The resulting code will serve as a template for a filtered signals indicator with alerts.

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

How alerts work in MQL4

The MQL4 language has a native function called Alert() which we can use to raise a visual alert in Metatrader, informing the user about a signal or situation that requires attention. The prototype is quite simple.

void  Alert(
   argument,     // first value
   ...           // other values
   );

The issue when it comes to indicators is to raise alerts properly. A proper alert requires auxiliary code to avoid raising alerts when the indicator loads and iterates past bars, as well as alerting just once per bar and not every single tick.

Create a new file

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

Save As Sample_Two.mq4

Add inputs and variables

First things first: we need to make alerts an optional feature and let the user enable and disable alerts at will. This might seem unnecessary but if we intend to use this indicator to later code other indicators or expert advisors, we need to be able to disable alerts to avoid alerts permeating into other programs later.

To do so, we need to add another input parameter to the indicator like so, above the OnInit() function in the input parameters block. I usually declare alerts disabled by default and let the user enable them at will.

extern bool DisplayAlerts = false;

Secondly, we also need two global variables to control when the alerts are raised. We want to raise alerts after the indicator has loaded and a new bar has closed storing a trading signal. We don’t want the indicator to raise alerts on past bars or every single tick.

Declare the following variables above the OnInit() function.

static datetime TimeStamp;
static int AlertCount = 1;

The first variable will hold the open time of the unclosed bar and will help us to avoid raising repeated signals every single tick. The second variable will avoid the indicator from raising an alert just after loading. We’ll arrange the code to update these values to meet our needs below.

Implement the alert logic

All that remains to be done is implementing the alert code logic. The alert code logic must be implemented outside the loop that iterates past bars from past to present, to avoid the indicator from raising alerts on past bars which are no longer actionable. Paste the following code outside the bar loop, inside the OnCalculate(…) function.

// Alerts
if(DisplayAlerts)
{
   // On new bar closing
   if(TimeStamp != Time[0])
   {
      // Check buy signal
      if(ExtMapBuffer1[Shift] != 0 && 
         ExtMapBuffer1[Shift] != EMPTY_VALUE && 
         AlertCount == 0)
      {
         Alert(ShortName +" ["+ Symbol() +"] Buy");
           
      // Check sell signal 
      } else if(ExtMapBuffer2[Shift] != 0 && 
                ExtMapBuffer2[Shift] != EMPTY_VALUE && 
                AlertCount == 0) {
         Alert(ShortName +" ["+ Symbol() +"] Sell");
      }
      TimeStamp = Time[0];
      AlertCount = 0;
   }
}

The above code evaluates theĀ  buffers of the indicator and raises alerts when needed with the text “Buy” or “Sell”, only for the recently closed bar. It ignores past bars and does not execute when the indicator has just loaded. Additionaly, the code block is not executed if the user has disabled alerts in inputs.

The complete code

The final source code of Indicator103.mq4 is the following. It implements all the features of Indicator 102, plus the alerts we recently added in this post. Two other constants have been added with the indicator name and shift to read signals from, and default values for the signal values have been set to empty value on the main bar iteration.

//---- File Properties
#property copyright   "Arthur Lopez"
#property link        "http://www.pointzero-trading.com"
#property description "101: Sample indicator that plots breakout arrows."
#property description "102: Implements mtf native filters."
#property description "103: Implements alerts."
#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

//---- Constants
#define  ShortName   "Indicator 103"
#define  Shift       1

//---- 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
extern bool    DisplayAlerts  = false;          // Alerts

//---- Buffer Arrays
double ExtMapBuffer1[];
double ExtMapBuffer2[];

//---- Alert control
static datetime TimeStamp;
static int AlertCount = 1;

//+------------------------------------------------------------------+
//| 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--)
   {
      // Set signals to empty
      ExtMapBuffer1[i] = EMPTY_VALUE;
      ExtMapBuffer2[2] = EMPTY_VALUE;
      
      // If not enough data...
      if(i > Bars-3) continue;
      
      // Set values to empty
      ExtMapBuffer1[i] = EMPTY_VALUE;
      ExtMapBuffer2[2] = EMPTY_VALUE;
      
      // 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];
      }
   }
    
   // Alerts
   if(DisplayAlerts)
   {
      // On new bar closing
      if(TimeStamp != Time[0])
      {
         // Check buy signal
         if(ExtMapBuffer1[Shift] != 0 && 
            ExtMapBuffer1[Shift] != EMPTY_VALUE && 
            AlertCount == 0)
         {
           Alert(ShortName +" ["+ Symbol() +"] Buy");
           
         // Check sell signal 
         } else if(ExtMapBuffer2[Shift] != 0 && 
                   ExtMapBuffer2[Shift] != EMPTY_VALUE && 
                   AlertCount == 0) {
           Alert(ShortName +" ["+ Symbol() +"] Sell");
         }
         TimeStamp = Time[0];
         AlertCount = 0;
      }
   }
    
   // Exit
   return(rates_total);
}

Test the result

To make sure everything works fine, all we need to do is run the indicator in the tester. The Journal tab will store and display all the alerts raised. During live trading, these alerts will be raised as dialogs.

Indicator 103 in the Tester

I hope this serves as an useful template for your own custom signal indicators with alerts. This guide can also be used to implement alerts on existing custom indicators.

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

390 total views, 10 views today

How useful was this post?

Average rating 5 / 5. Vote count: 2

Thanks for rating!

Would you like to share this post?

We are sorry that this post was not useful for you!

Let us improve this post!

Published by

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 *