Creating an indicator based on expert signal modules
 
As an example, let’s create an indicator on the basis of two signal modules SignalMA and SignalMACD, which files are located in the folder MQL5\Include\Expert\Signal.
With the MQL5 Wizard, let’s create a basis of the indicator by selecting the option Custom Indicator.
Now we need to specify properties of the indicator, as well as determine the functions OnInit and OnCalculate.
When creating an indicator based on trading signal modules of an expert, we will base on the expert code generated using the MQL5 Wizard.
 
When such an expert is working, in the OnTick function, the function OnTick of the class CExpert is called.
This function firstly calls the Refresh function updating symbol prices and indicator values, and then later it calls the Processing function that gets trading signals and place orders.
Since the function Refresh is protected, and we need to update data of modules of trading signals in the OnCalculate function, let’s create a custom class CExpertInd extending the class CExpert.
#include <Expert\Expert.mqh>
 
class CExpertInd : public CExpert
  {
  public:
  virtual bool      RefreshInd(void);
  };
  
  //+------------------------------------------------------------------+
//| Refreshing data for processing                                   |
//+------------------------------------------------------------------+
bool CExpertInd::RefreshInd(void)
  {
   MqlDateTime time;
//--- refresh rates
   if(!m_symbol.RefreshRates())
      return(false);
//--- check need processing
   TimeToStruct(m_symbol.Time(),time);
   if(m_period_flags!=WRONG_VALUE && m_period_flags!=0)
      if((m_period_flags&TimeframesFlags(time))==0)
         return(false);
   m_last_tick_time=time;
//--- refresh indicators
   m_indicators.Refresh();
//--- ok
   return(true);
  }
Looking at the CSignalMA and CSignalMACD classes, we can see that the functions LongCondition and ShortCondition that provide trade models are called on the current bar. However, we need to call these functions on the entire history of a symbol. In addition, we need a function BarsCalculated that returns a number of the calculated values in the signal module. Therefore, we create classes CSignalMAInd and CSignalMACDInd, which extend the classes CSignalMA and CSignalMACD.
#include <Expert\Signal\SignalMA.mqh>
class CSignalMAInd : public CSignalMA
  {
  public:
  virtual int       BarsCalculatedInd();               
  virtual int       LongConditionInd(int ind);
  virtual int       ShortConditionInd(int ind);                  
  };
  
//+------------------------------------------------------------------+
//| Refresh indicators.                                               |
//+------------------------------------------------------------------+  
int CSignalMAInd:: BarsCalculatedInd(){
return m_ma.BarsCalculated();
}
 
//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalMAInd::LongConditionInd(int idx)
  {
  
   int result=0;
   
//--- analyze positional relationship of the close price and the indicator at the first analyzed bar
   if(DiffCloseMA(idx)<0.0)
     {
      //--- the close price is below the indicator
      if(IS_PATTERN_USAGE(1) && DiffOpenMA(idx)>0.0 && DiffMA(idx)>0.0)
        {
         //--- the open price is above the indicator (i.e. there was an intersection), but the indicator is directed upwards
         result=m_pattern_1;
         //--- consider that this is an unformed "piercing" and suggest to enter the market at the current price
         m_base_price=0.0;
        }
     }
   else
     {
      //--- the close price is above the indicator (the indicator has no objections to buying)
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;
      //--- if the indicator is directed upwards
      if(DiffMA(idx)>0.0)
        {
         if(DiffOpenMA(idx)<0.0)
           {
            //--- if the model 2 is used
            if(IS_PATTERN_USAGE(2))
              {
               //--- the open price is below the indicator (i.e. there was an intersection)
               result=m_pattern_2;
               //--- suggest to enter the market at the "roll back"
               m_base_price=m_symbol.NormalizePrice(MA(idx));
              }
           }
         else
           {
            //--- if the model 3 is used and the open price is above the indicator
            if(IS_PATTERN_USAGE(3) && DiffLowMA(idx)<0.0)
              {
               //--- the low price is below the indicator
               result=m_pattern_3;
               //--- consider that this is a formed "piercing" and suggest to enter the market at the current price
               m_base_price=0.0;
              }
           }
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalMAInd::ShortConditionInd(int idx)
  {
   int result=0;
   
//--- analyze positional relationship of the close price and the indicator at the first analyzed bar
   if(DiffCloseMA(idx)>0.0)
     {
      //--- the close price is above the indicator
      if(IS_PATTERN_USAGE(1) && DiffOpenMA(idx)<0.0 && DiffMA(idx)<0.0)
        {
         //--- the open price is below the indicator (i.e. there was an intersection), but the indicator is directed downwards
         result=m_pattern_1;
         //--- consider that this is an unformed "piercing" and suggest to enter the market at the current price
         m_base_price=0.0;
        }
     }
   else
     {
      //--- the close price is below the indicator (the indicator has no objections to buying)
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;
      //--- the indicator is directed downwards
      if(DiffMA(idx)<0.0)
        {
         if(DiffOpenMA(idx)>0.0)
           {
            //--- if the model 2 is used
            if(IS_PATTERN_USAGE(2))
              {
               //--- the open price is above the indicator (i.e. there was an intersection)
               result=m_pattern_2;
               //--- suggest to enter the market at the "roll back"
               m_base_price=m_symbol.NormalizePrice(MA(idx));
              }
           }
         else
           {
            //--- if the model 3 is used and the open price is below the indicator
            if(IS_PATTERN_USAGE(3) && DiffHighMA(idx)>0.0)
              {
               //--- the high price is above the indicator
               result=m_pattern_3;
               //--- consider that this is a formed "piercing" and suggest to enter the market at the current price
               m_base_price=0.0;
              }
           }
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
#include <Expert\Signal\SignalMACD.mqh>
 
class CSignalMACDInd : public CSignalMACD
  {
  public: 
  virtual int       BarsCalculatedInd();                  
  virtual int       LongConditionInd(int ind);
  virtual int       ShortConditionInd(int ind);
  
  };
 //+------------------------------------------------------------------+
//| Refresh indicators.                                               |
//+------------------------------------------------------------------+  
int CSignalMACDInd:: BarsCalculatedInd(){
return m_MACD.BarsCalculated();
}
 
//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalMACDInd::LongConditionInd(int idx)
  {
   int result=0;
   
//--- check direction of the main line
   if(DiffMain(idx)>0.0)
     {
      //--- the main line is directed upwards, and it confirms the possibility of price growth
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, look for a reverse of the main line
      if(IS_PATTERN_USAGE(1) && DiffMain(idx+1)<0.0)
         result=m_pattern_1;      // signal number 1
      //--- if the model 2 is used, look for an intersection of the main and signal line
      if(IS_PATTERN_USAGE(2) && State(idx)>0.0 && State(idx+1)<0.0)
         result=m_pattern_2;      // signal number 2
      //--- if the model 3 is used, look for an intersection of the main line and the zero level
      if(IS_PATTERN_USAGE(3) && Main(idx)>0.0 && Main(idx+1)<0.0)
         result=m_pattern_3;      // signal number 3
      //--- if the models 4 or 5 are used and the main line turned upwards below the zero level, look for divergences
      if((IS_PATTERN_USAGE(4) || IS_PATTERN_USAGE(5)) && Main(idx)<0.0)
        {
         //--- perform the extended analysis of the oscillator state
         ExtState(idx);
         //--- if the model 4 is used, look for the "divergence" signal
         if(IS_PATTERN_USAGE(4) && CompareMaps(1,1)) // 0000 0001b
            result=m_pattern_4;   // signal number 4
         //--- if the model 5 is used, look for the "double divergence" signal
         if(IS_PATTERN_USAGE(5) && CompareMaps(0x11,2)) // 0001 0001b
            return(m_pattern_5);  // signal number 5
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalMACDInd::ShortConditionInd(int idx)
  {
   int result=0;
   
//--- check direction of the main line
   if(DiffMain(idx)<0.0)
     {
      //--- main line is directed downwards, confirming a possibility of falling of price
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, look for a reverse of the main line
      if(IS_PATTERN_USAGE(1) && DiffMain(idx+1)>0.0)
         result=m_pattern_1;      // signal number 1
      //--- if the model 2 is used, look for an intersection of the main and signal line
      if(IS_PATTERN_USAGE(2) && State(idx)<0.0 && State(idx+1)>0.0)
         result=m_pattern_2;      // signal number 2
      //--- if the model 3 is used, look for an intersection of the main line and the zero level
      if(IS_PATTERN_USAGE(3) && Main(idx)<0.0 && Main(idx+1)>0.0)
         result=m_pattern_3;      // signal number 3
      //--- if the models 4 or 5 are used and the main line turned downwards above the zero level, look for divergences
      if((IS_PATTERN_USAGE(4) || IS_PATTERN_USAGE(5)) && Main(idx)>0.0)
        {
         //--- perform the extended analysis of the oscillator state
         ExtState(idx);
         //--- if the model 4 is used, look for the "divergence" signal
         if(IS_PATTERN_USAGE(4) && CompareMaps(1,1)) // 0000 0001b
            result=m_pattern_4;   // signal number 4
         //--- if the model 5 is used, look for the "double divergence" signal
         if(IS_PATTERN_USAGE(5) && CompareMaps(0x11,2)) // 0001 0001b
            return(m_pattern_5);  // signal number 5
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+  
Now we can start coding of our indicator.
We will draw the indicator as a line at opening prices, and the line color will be changed depending on the forecast for the increase or decrease in prices.
#property indicator_chart_window
 
#include <Expert\ExpertInd.mqh>
#include <Expert\Signal\SignalMACDInd.mqh>
#include <Expert\Signal\SignalMAInd.mqh>
 
#property indicator_buffers 2
#property indicator_plots   1
#property indicator_type1    DRAW_COLOR_LINE
#property indicator_color1  clrBlack,clrRed,clrLawnGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
 
double         InBuffer[];
double         ColorBuffer[];
int    bars_calculated=0;
 
input int                Signal_ThresholdOpen          =20;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose         =20;          // Signal threshold value to close [0...100]
 
input int                Signal_MACD_PeriodFast        =12;          // MACD(12,24,9,PRICE_CLOSE) Period of fast EMA
input int                Signal_MACD_PeriodSlow        =24;          // MACD(12,24,9,PRICE_CLOSE) Period of slow EMA
input int                Signal_MACD_PeriodSignal      =9;           // MACD(12,24,9,PRICE_CLOSE) Period of averaging of difference
input ENUM_APPLIED_PRICE Signal_MACD_Applied           =PRICE_CLOSE; // MACD(12,24,9,PRICE_CLOSE) Prices series
input double             Signal_MACD_Weight            =1.0;         // MACD(12,24,9,PRICE_CLOSE) Weight [0...1.0]
input int                Signal_MA_PeriodMA            =12;          // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift               =0;           // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method              =MODE_SMA;    // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied             =PRICE_CLOSE; // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight              =1.0;         // Moving Average(12,0,...) Weight [0...1.0]
 
CExpertInd ExtExpert;
CSignalMAInd *filter0 = new CSignalMAInd;
CSignalMACDInd *filter1 = new CSignalMACDInd;
 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
  
//--- Initializing expert
if(!ExtExpert.Init(Symbol(),Period(),true,100))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Creating signal
   CExpertSignal *signal=new CExpertSignal;
   if(signal==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//---
   ExtExpert.InitSignal(signal);  
 
filter0.PeriodMA(Signal_MA_PeriodMA);
filter0.Shift(Signal_MA_Shift);
filter0.Method(Signal_MA_Method);
filter0.Applied(Signal_MA_Applied);
 
filter1.PeriodFast(Signal_MACD_PeriodFast);
filter1.PeriodSlow(Signal_MACD_PeriodSlow);
filter1.PeriodSignal(Signal_MACD_PeriodSignal);
filter1.Applied(Signal_MACD_Applied);
 
signal.AddFilter(filter0);
signal.AddFilter(filter1);
if(!ExtExpert.ValidationSettings())
     {
      //--- failed
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- ok
   //--- indicator buffers mapping
   SetIndexBuffer(0,InBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ColorBuffer,INDICATOR_COLOR_INDEX);
   
ArraySetAsSeries(InBuffer,true);   
ArraySetAsSeries(ColorBuffer,true);
 
   
//---
   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[])
  {
   int values_to_copy;
int calculated=MathMin(filter0.BarsCalculatedInd(), filter1.BarsCalculatedInd());
   if(calculated<=0)
     {
      PrintFormat("BarsCalculated() returns %d, error code %d",calculated,GetLastError());
      return(0);
     }
   if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
     {
      
      if(calculated>rates_total) values_to_copy=rates_total;
      else                       values_to_copy=calculated;
     }
   else
     {
      
      values_to_copy=(rates_total-prev_calculated)+1;
     }
 
 bars_calculated=calculated;
 
 ArraySetAsSeries(open,true);
 
 ExtExpert.RefreshInd();
 
 if(values_to_copy>1)
{
   
for (int i=0; i<values_to_copy; i++){
 
ColorBuffer[i]=0;
InBuffer[i]=open[i];
 
double result0=Signal_MA_Weight*(filter0.LongConditionInd(i)-filter0.ShortConditionInd(i));
double result1=Signal_MACD_Weight*(filter1.LongConditionInd(i)-filter1.ShortConditionInd(i));
double result=(result0+result1)/2;
 
if(result>=Signal_ThresholdOpen)
     {
 ColorBuffer[i]=2;   
     } 
     
 if(-result>=Signal_ThresholdOpen){
 ColorBuffer[i]=1;
 } 
}
}
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
Here, we have got the input parameters and the initialization code from the code of the generated expert.
Since signal modules work with data as timeseries, let’s apply the ArraySetAsSeries function to the buffers of the indicator.
In the OnCalculate function of the indicator, we firstly update all the data, and then get the weighted signals of modules and compare them with a threshold value. As a result, we get a forecast for the increase or decrease in prices.
 

You can download the book MQL5 programming language: Advanced use of the trading platform MetaTrader 5: Creating trading robots and indicators here.

Our services

Our developments are at the forefront of high technologies

We design and develop web sites

Individual site design
Creating dynamic sites
Website layout and programming

We develop information systems

Automation of business processes for small and medium business
Collection, storage and processing of data
Using cloud technologies

We create mobile apps

Integration with a Web site
Creating mobile versions of web sites
Using Augmented Reality and Computer Vision

About us

NOV Tech Solutions specializes in developing services for operational management of business, creating web and mobile applications on order

NOV Tech Solutions is a team of highly qualified professionals working for the result

We work quickly and efficiently, using the most modern technologies

We appreciate and take care of our customers, constantly improving the service and expanding the list of services

Our partners

Contacts