Download MetaTrader 5

Adding a control panel to an indicator or an Expert Advisor in no time

26 February 2016, 15:02
Karputov Vladimir
1
8 729

Using Graphical Panels

Your MQL4/MQL5 indicator or Expert Advisor may be the most efficient in the world but there is always a room for improvements. In most cases, you need to enter the program's settings to change its inputs. However, this step can be avoided.

Develop your own control panel based on Standard Library classes. This will allow you to change the settings without restarting a program. Besides, this will make your program more attractive allowing it to stand out from the competitors. You can browse through multiple graphical panels in the Market.

In this article, I will show you how to add a simple panel to your MQL4/MQL5 program. You will also find out how to teach a program to read the inputs and react to changes of their values.

 

1. Combining the indicator with the panel


1.1. Indicator

The NewBar.mq5 indicator performs a single action. It prints a message in the terminal's Experts log when a new bar arrives. The indicator code is provided below:

//+------------------------------------------------------------------+
//|                                                       NewBar.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "The indicator identifies a new bar"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
   static datetime prev_time;
//--- revert access to array time[] - do it like in timeseries 
   ArraySetAsSeries(time,true);
//--- first calculation or number of bars was changed
   if(prev_calculated==0)// first calculation
     {
      prev_time=time[0];
      return(rates_total);
     }
//---
   if(time[0]>prev_time)
      Print("New bar!");
//---
   prev_time=time[0];
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Now, let's delve into some details of NewBar.mq5 operation.

The prev_time static variable is declared in the OnCalculate() function. This variable stores the time[0] open time. During the next pass, the time[0] open time is compared with the prev_time variable. In other words, the current tick's time[0] open time is compared to the previous tick's one. If the following condition is met:

if(time[0]>prev_time)

a new bar is considered to be detected.

The next example shows in details how NewBar.mq5 detects a new bar:

New bar

Fig. 1. Detecting a new bar in the indicator

Let's consider 10 ticks on a very quiet market.

Ticks 1-3: open time of a bar with the index 0 (time[0]) is equal to the time stored in the prev_time static variable meaning that there is no new bar.

Tick 4: the tick arrived on a new bar. When entering the OnCalculate() function, time[0] has the bar open time (2015.12.01 00:02:00), while the prev_time variable still stores the previous tick's time (2015.12.01 00:01:00). Therefore, we detect the new bar when checking the time[0]>prev_time condition. Before exiting OnCalculate(), the prev_time variable obtains the time from time[0] (2015.12.01 00:02:00).

Ticks 5-8: open time of a bar with the index 0 (time[0]) is equal to the time stored in the prev_time static variable meaning that there is no new bar.

Tick 9: the tick arrived on a new bar. When entering the OnCalculate() function, time[0] has the bar open time (2015.12.01 00:03:00), while the prev_time variable still stores the previous tick's time (2015.12.01 00:02:00). Therefore, we detect the new bar when checking the time[0]>prev_time condition. Before exiting OnCalculate(), the prev_time variable obtains the time from time[0] (2015.12.01 00:03:00).

Tick 10: open time of a bar with the index 0 (time[0]) is equal to the time stored in the prev_time static variable meaning that there is no new bar.


1.2. Panel

All panel plotting parameters (amount, size, and coordinates of control elements) are gathered in a single include file PanelDialog.mqh, which serves as a panel implementation class.

The panel looks as follows:

Panel

Fig. 2. Panel

The code of the PanelDialog.mqh include file is presented below:

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\CheckGroup.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define INDENT_BOTTOM                       (11)      // indent from bottom (with allowance for border width)
//--- for buttons
#define BUTTON_WIDTH                        (100)     // size by X coordinate
//+------------------------------------------------------------------+
//| Class CControlsDialog                                            |
//| Usage: main dialog of the Controls application                   |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CCheckGroup       m_check_group;                   // CCheckGroup object

public:
                     CControlsDialog(void);
                    ~CControlsDialog(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);
   //--- handlers of the dependent controls events
   void              OnChangeCheckGroup(void);
  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CControlsDialog)
ON_EVENT(ON_CHANGE,m_check_group,OnChangeCheckGroup)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CControlsDialog::CControlsDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CControlsDialog::~CControlsDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateCheckGroup())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "CheckGroup" element                                  |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateCheckGroup(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=x1+BUTTON_WIDTH;
   int y2=ClientAreaHeight()-INDENT_BOTTOM;
//--- create
   if(!m_check_group.Create(m_chart_id,m_name+"CheckGroup",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_check_group))
      return(false);
   m_check_group.Alignment(WND_ALIGN_HEIGHT,0,y1,0,INDENT_BOTTOM);
//--- fill out with strings
   if(!m_check_group.AddItem("Mail",1<<0))
      return(false);
   if(!m_check_group.AddItem("Push",1<<1))
      return(false);
   if(!m_check_group.AddItem("Alert",1<<2))
      return(false);
   Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value()));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CControlsDialog::OnChangeCheckGroup(void)
  {
   Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value()));
  }
//+------------------------------------------------------------------+

As you can see, the class of our panel does not contain the methods for setting and reading the status of switches with independent fixing.

Our objective is to make the NewBar.mq5 the main file and add the inputs, for example, the ability to choose new bar arrival alert methods (Mail, Push, or Alert). Besides, the PanelDialog.mqh include file should contain the methods for setting and reading the status of Mail, Push, and Alert switches with independent fixing.


1.3. Changing the indicator

Note: all implemented changes are marked with color.

First, we should implement the PanelDialog.mqh include file:

#property indicator_chart_window
#property indicator_plots 0
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()

Then, add the inputs:

#property indicator_chart_window
#property indicator_plots 0
#include "PanelDialog.mqh"
//--- input parameters
input bool     bln_mail=false;      // Notify by email
input bool     bln_push=false;      // Notify by push
input bool     bln_alert=true;      // Notify by alert
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()

Compile the indicator (F7 in MetaEditor) and make sure the input parameters are displayed correctly in the terminal:

Input parameters

Fig. 3. Indicator input parameters


1.4. Changing the panel

Now, we should add Mail, Push, and Alert methods for setting and reading the status of switches with independent fixing to the panel.

Let's add the new methods to the panel class:

class CControlsDialog : public CAppDialog
  {
private:
   CCheckGroup       m_check_group;                   // CCheckGroup object

public:
                     CControlsDialog(void);
                    ~CControlsDialog(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set check for element
   virtual bool      SetCheck(const int idx,const int value);
   //--- get check for element
   virtual int       GetCheck(const int idx) const;

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Implementing the methods:

//+------------------------------------------------------------------+
//| Set check for element                                            |
//+------------------------------------------------------------------+
bool CControlsDialog::SetCheck(const int idx,const bool check)
  {
   return(m_check_group.Check(idx,check));
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)
  {
   return(m_check_group.Check(idx));
  }


1.5. The final stage of combining the indicator with the panel

Declare the variable of our panel class in the block of global variables declaration of the NewBar.mq5 indicator:

#property indicator_chart_window
#property indicator_plots 0
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CControlsDialog ExtDialog;
//--- input parameters
input bool     bln_mail=false;      // Notify by email
input bool     bln_push=false;      // Notify by push
input bool     bln_alert=true;      // Notify by alert

and add the OnChartEvent() function at the very end:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }

Create the panel in the OnInit() function of the NewBar.mq5 indicator and click the check boxes programmatically according to the input parameters:

int OnInit()
  {
//--- indicator buffers mapping
//--- create application dialog
   if(!ExtDialog.Create(0,"Notification",0,50,50,180,160))
      return(INIT_FAILED);
//--- run application
   if(!ExtDialog.Run())
      return(INIT_FAILED);
//---
   ExtDialog.SetCheck(0,bln_mail);
   ExtDialog.SetCheck(1,bln_push);
   ExtDialog.SetCheck(2,bln_alert);
//---
   return(INIT_SUCCEEDED);
  }

Thus, we have combined the indicator with the panel. We have implemented the method for determining a check box status – pressed/released (SetCheck), as well as the method for receiving it (GetCheck).

 

2. Combining the Expert Advisor with the panel


2.1. Expert Advisor

Let's use the EA from the standard delivery set ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5 as a basis.


2.2. Panel

The finalized PanelDialog2.mqh panel looks as follows:

Panel number two

Fig. 4. Panel number two

What are the benefits of combining the MACD Sample.mq5 EA with the PanelDialog2.mqh panel? This allows us to quickly change the EA parameters (Lots, Trailing Stop Level (in pips), and others), as well as trade event notification settings (Mail, Push, and Alert) on the current timeframe the EA is to be launched at.

Changed EA parameters (Lots, Trailing Stop Level (in pips), and others) are applied after clicking the Apply changes button. Changes of the trade event notification settings (Mail, Push, and Alert) are applied automatically. There is no need to press the Apply changes button.


2.3. The EA and the panel should have means of communication

Communication between the EA and the panel

Fig. 5. Communication between the EA and the panel

After the launch, the EA should send its parameters to the panel. After clicking the Apply changes button and changing the parameters, the panel should return the altered parameters to the EA for its initialization with the new parameters.


2.4. Step one. Changing the EA

Take the EA from the standard delivery set ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5 and copy it to your folder. For example, you can create the Notification folder and copy the EA there:

Creating a new folder

Fig. 6. Creating a new folder


In the area of the EA's global variables (not to be confused with the terminal's ones), declare the new variables defining a method of sending notifications of the EA's trading activity. Please note that these variables have the Inp prefix just like other external variables:

#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
//--- input parameters
input bool     InpMail=false;          // Notify by email
input bool     InpPush=false;          // Notify by push
input bool     InpAlert=true;          // Notify by alert
//---
input double InpLots          =0.1; // Lots
input int    InpTakeProfit    =50;  // Take Profit (in pips)

Add the duplicate copies of all the EA's external variables just below. The duplicate copies have the Ext prefix:

input int    InpMACDCloseLevel=2;   // MACD close level (in pips)
input int    InpMATrendPeriod =26;  // MA trend period
//--- ext variables
bool           ExtMail;
bool           ExtPush;
bool           ExtAlert;

double         ExtLots;
int            ExtTakeProfit;
int            ExtTrailingStop;
int            ExtMACDOpenLevel;
int            ExtMACDCloseLevel;
int            ExtMATrendPeriod;
//---
int ExtTimeOut=10; // time out in seconds between trade operations
//+------------------------------------------------------------------+
//| MACD Sample expert class                                         |
//+------------------------------------------------------------------+

Use OnInit() to set copying the values from external variables to duplicate variable values:

//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   ExtMail=InpMail;
   ExtPush=InpPush;
   ExtAlert=InpAlert;

   ExtLots=InpLots;
   ExtTakeProfit=InpTakeProfit;
   ExtTrailingStop=InpTrailingStop;
   ExtMACDOpenLevel=InpMACDOpenLevel;
   ExtMACDCloseLevel=InpMACDCloseLevel;
   ExtMATrendPeriod=InpMATrendPeriod;
//--- create all necessary objects
   if(!ExtExpert.Init())

At this stage, the EA's external variables with the Inp prefix are used in the EA's CSampleExpert::InitIndicators, CSampleExpert::InitCheckParameters, and CSampleExpert::Init functions. We need to replace the external variables in these functions with their duplicate copies (having the Ext prefix). I suggest quite an unconventional solution here:

After the replacement is performed, compile the file to make sure that all has been done correctly. There should be no errors.


2.5. Step two. Changing the panel

The panel shown in Fig. 4 is a blank. It has neither the function for "communicating" with the EA, nor the function for processing the input data yet. Copy the panel blank file PanelDialog2Original.mqh to the Notification folder as well.

Add the internal variables to the panel class. They will be used to store the status of the entire input data. Note the mModification variable. I will provide more details on it in p. 2.7.

private:
   //--- get check for element
   virtual int       GetCheck(const int idx);
   //---
   bool              mMail;
   bool              mPush;
   bool              mAlert_;
   double            mLots;               // Lots
   int               mTakeProfit;         // Take Profit (in pips)
   int               mTrailingStop;       // Trailing Stop Level (in pips)
   int               mMACDOpenLevel;      // MACD open level (in pips)
   int               mMACDCloseLevel;     // MACD close level (in pips)
   int               mMATrendPeriod;      // MA trend period
   //---
   bool              mModification;       // Values have changed
  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |

Initialize the internal variables in the panel class constructor just below:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CControlsDialog::CControlsDialog(void) : mMail(false),
                                         mPush(false),
                                         mAlert_(true),
                                         mLots(0.1),
                                         mTakeProfit(50),
                                         mTrailingStop(30),
                                         mMACDOpenLevel(3),
                                         mMACDCloseLevel(2),
                                         mMATrendPeriod(26),
                                         mModification(false)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |

Add installing the groups of switcher elements according to the internal variables to the CControlsDialog::Create function:

if(!CreateButtonOK())
      return(false);

//---
   SetCheck(0,mMail);
   SetCheck(1,mPush);
   SetCheck(2,mAlert_);

//--- succeed
   return(true);
  }

 

2.6. Step three. Changing the EA

Until now, the EA and the panel were two separate files independent of each other. Let's connect them and declare the ExtDialog variable of our panel class:

#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
#include "PanelDialog2Original.mqh"
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CControlsDialog ExtDialog;
//--- input parameters
input bool     InpMail=false;          // Notify by email
input bool     InpPush=false;          // Notify by push

In order to make the panel operational and visible, it should be created and launched. Also, make sure to add the OnChartEvent() (for handling the ChartEvent) and OnDeinit() functions. OnInit() in the EA looks as follows:

int OnInit(void)
  {
   ExtMail=InpMail;
   ExtPush=InpPush;
   ExtAlert=InpAlert;

   ExtLots=InpLots;
   ExtTakeProfit=InpTakeProfit;
   ExtTrailingStop=InpTrailingStop;
   ExtMACDOpenLevel=InpMACDOpenLevel;
   ExtMACDCloseLevel=InpMACDCloseLevel;
   ExtMATrendPeriod=InpMATrendPeriod;
//--- create all necessary objects
   if(!ExtExpert.Init())
      return(INIT_FAILED);
//--- create application dialog
   if(!ExtDialog.Create(0,"Notification",0,100,100,360,380))
      return(INIT_FAILED);
//--- run application
   if(!ExtDialog.Run())
      return(INIT_FAILED);
//--- succeed
   return(INIT_SUCCEEDED);
  }

Let's destroy our panel in OnDeinit() and set the OnDeinit() function right after OnInit():

//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 
   Comment("");
//--- destroy dialog
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert new tick handling function                                |
//+------------------------------------------------------------------+
void OnTick(void)

Add the OnChartEvent() function to the very end of the EA (after the OnTick function):

//--- change limit time by timeout in seconds if processed
         if(ExtExpert.Processing())
            limit_time=TimeCurrent()+ExtTimeOut;
        }
     }
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Now, the EA can be compiled and checked on the chart. The EA is launched with the panel:

EA and panel

Fig. 7. The EA and the panel


2.7. Step four. Changing the panel. Big integration

The EA is launched first. Then, its inputs are defined by a user. Only after that, the panel is launched. Therefore, the panel should have the functions for exchanging data with the EA.

Let's add the Initialization() method which accepts the parameters and uses them to initialize the panel's internal variables. Declaration:

virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
      //--- initialization
   virtual bool      Initialization(const bool Mail,const bool Push,const bool Alert_,
                                    const double Lots,const int TakeProfit,
                                    const int  TrailingStop,const int MACDOpenLevel,
                                    const int  MACDCloseLevel,const int MATrendPeriod);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

The method's body (insert it before CControlsDialog::GetCheck):

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
bool CControlsDialog::Initialization(const bool Mail,const bool Push,const bool Alert_,
                                     const double Lots,const int TakeProfit,
                                     const int  TrailingStop,const int MACDOpenLevel,
                                     const int  MACDCloseLevel,const int MATrendPeriod)
  {
   mMail=Mail;
   mPush=Push;
   mAlert_=Alert_;

   mLots=Lots;
   mTakeProfit=TakeProfit;
   mTrailingStop=TrailingStop;
   mMACDOpenLevel=MACDOpenLevel;
   mMACDCloseLevel=MACDCloseLevel;
   mMATrendPeriod=MATrendPeriod;
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)

Since the panel's internal variables have been initialized by the data, we need to fill in the panel's control elements (entry fields) correctly. Since we have six entry fields, I will provide an example based on m_edit1. The string the text was assigned at looked as follows:

...
   if(!m_edit1.Text("Edit1"))
...

But now it looks differently:

...
   if(!m_edit1.Text(DoubleToString(mLots,2)))
...

Thus, each entry field corresponds to a certain internal variable.

The next method named GetValues() returns the values of internal variables:

virtual bool      Initialization(const bool Mail,const bool Push,const bool Alert_,
                                    const double Lots,const int TakeProfit,
                                    const int  TrailingStop,const int MACDOpenLevel,
                                    const int  MACDCloseLevel,const int MATrendPeriod);
   //--- get values
   virtual void      GetValues(bool &Mail,bool &Push,bool &Alert_,
                               double &Lots,int &TakeProfit,
                               int &TrailingStop,int &MACDOpenLevel,
                               int &MACDCloseLevel,int &MATrendPeriod);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Insert its body after CControlsDialog::Initialization()):

//+------------------------------------------------------------------+
//| Get values                                                       |
//+------------------------------------------------------------------+
void CControlsDialog::GetValues(bool &Mail,bool &Push,bool &Alert_,
                                double &Lots,int &TakeProfit,
                                int &TrailingStop,int &MACDOpenLevel,
                                int &MACDCloseLevel,int &MATrendPeriod)
  {
   Mail=mMail;
   Push=mPush;
   Alert_=mAlert_;

   Lots=mLots;
   TakeProfit=mTakeProfit;
   TrailingStop=mTrailingStop;
   MACDOpenLevel=mMACDOpenLevel;
   MACDCloseLevel=mMACDCloseLevel;
   MATrendPeriod=mMATrendPeriod;
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)

Since the panel is to send a notification in response to any trading action performed by the EA, it should have a special method responsible for that. Let's declare it:

virtual void      GetValues(bool &Mail,bool &Push,bool &Alert_,
                               double &Lots,int &TakeProfit,
                               int &TrailingStop,int &MACDOpenLevel,
                               int &MACDCloseLevel,int &MATrendPeriod);   //--- send notifications
   virtual void      Notifications(const string text);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Insert its body after CControlsDialog::GetValues()):

//+------------------------------------------------------------------+
//|  Send notifications                                              |
//+------------------------------------------------------------------+
void CControlsDialog::Notifications(const string text)
  {
   int i=m_check_group.ControlsTotal();
   if(GetCheck(0))
      SendMail(" ",text);
   if(GetCheck(1))
      SendNotification(text);
   if(GetCheck(2))
      Alert(text);
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)

The mModification flag (mentioned in p. 2.5) is used in order to remember, whether the parameters in the panel were changed.

virtual void      Notifications(const string text);
   //---
   virtual bool      Modification(void) const { return(mModification);          }
   virtual void      Modification(bool value) { mModification=value;            }

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

The changes are to be controlled in CControlsDialog::OnClickButtonOK, which handles the event of pressing the Apply changes button:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CControlsDialog::OnClickButtonOK(void)
  {
//--- verifying changes
   if(m_check_group.Check(0)!=mMail)
      mModification=true;
   if(m_check_group.Check(1)!=mPush)
      mModification=true;
   if(m_check_group.Check(2)!=mAlert_)
      mModification=true;

   if(StringToDouble(m_edit1.Text())!=mLots)
     {
      mLots=StringToDouble(m_edit1.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit2.Text())!=mTakeProfit)
     {
      mTakeProfit=(int)StringToDouble(m_edit2.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit3.Text())!=mTrailingStop)
     {
      mTrailingStop=(int)StringToDouble(m_edit3.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit4.Text())!=mMACDOpenLevel)
     {
      mMACDOpenLevel=(int)StringToDouble(m_edit4.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit5.Text())!=mMACDCloseLevel)
     {
      mMACDCloseLevel=(int)StringToDouble(m_edit5.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit6.Text())!=mMATrendPeriod)
     {
      mMATrendPeriod=(int)StringToDouble(m_edit6.Text());
      mModification=true;
     }
  }

Also, the panel checks the input data in the handlers:

void              OnChangeCheckGroup(void);
   void              OnChangeEdit1(void);
   void              OnChangeEdit2(void);
   void              OnChangeEdit3(void);
   void              OnChangeEdit4(void);
   void              OnChangeEdit5(void);
   void              OnChangeEdit6(void);
   void              OnClickButtonOK(void);

I will skip their description.

2.8. Step five. Changing the EA. Last edits

At the moment, the panel does not work in the strategy tester, therefore we need to implement the protection and introduce the internal variable – the bool_tester flag.

//---
int ExtTimeOut=10; // time out in seconds between trade operations
bool           bool_tester=false;      // true - mode tester
//+------------------------------------------------------------------+
//| MACD Sample expert class                                         |
//+------------------------------------------------------------------+
class CSampleExpert

Insert changes to OnInit() – protect from launching in the strategy tester. Also, initialize the panel's parameters before visualizing it:

//--- create all necessary objects
   if(!ExtExpert.Init())
      return(INIT_FAILED);
//--- 
   if(!MQLInfoInteger(MQL_TESTER))
     {
      bool_tester=false;
      //---
      ExtDialog.Initialization(ExtMail,ExtPush,ExtAlert,
                               ExtLots,ExtTakeProfit,ExtTrailingStop,
                               ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod);
      //--- create application dialog
      if(!ExtDialog.Create(0,"Notification",0,100,100,360,380))
         return(INIT_FAILED);
      //--- run application
      if(!ExtDialog.Run())
         return(INIT_FAILED);
     }
   else
      bool_tester=true;
//--- secceed
   return(INIT_SUCCEEDED);
  }

Check if the parameters in the panel were changed in OnChartEvent(). If yes, the EA should be initialized with the new parameters:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
// Ask the bool variable in the panel if the parameters were changed
// If yes, ask the panel parameters and call
// CSampleExpert::Init(void)
   if(ExtDialog.Modification())
     {
      ExtDialog.GetValues(ExtMail,ExtPush,ExtAlert,
                          ExtLots,ExtTakeProfit,ExtTrailingStop,
                          ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod);
      if(ExtExpert.Init())
        {
         ExtDialog.Modification(false);
         Print("Parameters changed, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ",
               ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod);
        }
      else
        {
         ExtDialog.Modification(false);
         Print("Parameter change error");
        }
     }
  }
//+------------------------------------------------------------------+

 

Conclusion

Combining the panel with the indicator has turned out to be easy enough. To achieve this, we have implemented the entire functionality (control elements' size and location, response to events) in the panel class, as well as declared the variable of our panel class and added the OnChartEvent() function in the indicator.

Combining the EA with the more complex panel has been more challenging, mainly due to the need to arrange "communication" between the EA and the panel. The complexity of the problem mostly depends on whether the panel is ready for connection. In other words, if the panel initially has a decent amount of functions and possibilities for integration with other programs, it will be much easier to combine it with another application (indicator or EA).

The following files are attached to the article:

  • NewBarOriginal.mq5 — initial indicator file.
  • PanelDialogOriginal.mqh — initial panel file.
  • NewBar.mq5 — changed indicator file.
  • PanelDialog.mqh — changed panel file.
  • PanelDialog2Original.mqh — initial second panel file.
  • PanelDialog2.mqh — changed second panel file.
  • MACD Sample.mq5 — changed EA file.

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

Attached files |
newbar.mq5 (3.24 KB)
paneldialog.mqh (6.39 KB)
newbaroriginal.mq5 (2.05 KB)
macd_sample.mq5 (24.12 KB)
paneldialog2.mqh (28.48 KB)
Last comments | Go to discussion (1)
Aaditya Bhagra
Aaditya Bhagra | 1 Mar 2016 at 13:11
Pretty useful. Thanks.
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.