MQL5 Wizard: How to Create a Module of Trading Signals

MetaQuotes | 11 January, 2011


Introduction

MetaTrader 5 provides a powerful tool for quick checking of trading ideas. This is the generator of trading strategies of the MQL5 Wizard. The use of the MQL5 Wizard for automatic creation of Expert Advisor codes is described in the article "MQL5 Wizard: Creating Expert Advisors without Programming". Openness of the code generation system allows you to add your own classes of trading signals, money management systems and trailing modules to the standard ones.

This article describes the principles of writing modules of trading signals to use them when creating Expert Advisors with the MQL5 Wizard.

The Expert Advisor created with MQL5 Wizard, is based on four pillars - four base classes:

Figure 1. The structure of the CExpert base class

Figure 1. The structure of the CExpert base class

The CExpert class (or its subclass) is the main "engine" of a trading robot. An instance of CExpert contains one copy of each class: CExpertSignal, CExpertMoney and CExpertTrailing (or their subclasses):

  1. CExpertSignal is the basis of the trading signals generator. An instance of the CExpertSignal derived class, included in CExpert, provides an Expert Advisor with information about the possibility of entering the market, levels of entry and placing of protective orders, based on built-in algorithms. The final decision on execution of trading operations is made by the EA.
  2. CExpertMoney is the basis of the money and risk management systems. An instance of CExpertMoney derived class calculates volumes for opening positions and placing pending orders. The final decision on the volume is made by the EA.
  3. CExpertTrailing - is the basis of the module of open positions support. An instance of the CExpertTrailing derived class informs an EA about the necessity to modify protective orders of a position. The final decision on the order modification is made by the EA.

In addition, the members of the CExpert class are instances of the following classes:

Hereinafter, under "expert" we mean an instance of CExpert or its subclass.

More details of CExpert and work with it will be described in a separate article.


1. Base Class CExpertSignal

CExpertSignal is the basis of the trading signals generator. For communication with the "outside world", CExpertSignal has a set of public virtual method:

Initialization

 Description

virtual Init

Initialization of the class instance provides synchronization of the module data with the data of the EA

virtual ValidationSettings

Validation of set parameters

virtual InitIndicators

Creating and initializing all indicators and timeseries required for operation of the trading signals generator

Signals of position opening/reversal/closing

 

virtual CheckOpenLong

Generating the signal of long position opening, defining the levels of entry and placing of protective orders

virtual CheckOpenShort

Generating the signal of a short position opening, defining the levels of entry and placing of protective orders

virtual CheckCloseLong

Generating the signal of long position closing, defining the exit level

virtual CheckCloseShort

Generating the signal of short position closing, defining the exit level

virtual CheckReverseLong

Generating the signal of long position reversal, defining the levels of reversal and placing of protective orders

virtual CheckReverseShort

Generating the signal of short position reversal, defining the levels of reversal and placing of protective orders

Managing pending orders

 

virtual CheckTrailingOrderLong

Generating the signal of modification of a pending Buy order, defining the new order price

virtual CheckTrailingOrderShort

Generating the signal of modification of a pending Sell order, defining the new order price

Description of Methods

1.1. Initialization methods:

1.1.1 Init

The Init() method is called automatically right after a class instance is added to the expert. Method overriding is not required.

virtual bool Init(CSymbolInfo* symbol, ENUM_TIMEFRAMES period, double adjusted_point);

1.1.2 ValidationSettings

The ValidationSettings() method is called right from the expert after all the parameters are set. You must override the method if there are any setup parameters.

virtual bool ValidationSettings();

The overridden method must return true, if all options are valid (usable). If at least one of the parameters is incorrect, it must return false (further work is impossible).

Base class CExpertSignal has no adjustable parameters, therefore, the base class method always returns true without performing any checks.

1.1.3 InitIndicators

The InitIndicators () method implements the creation and initialization of all necessary indicators and timeseries. It is called from the expert after all the parameters are set and their correctness is successful verified. The method should be overridden if the trading signal generator uses at least one indicator or timeseries.

virtual bool InitIndicators(CIndicators* indicators);

Indicators and/or timeseries should be used through the appropriate classes of the Standard Library. Pointers of all indicators and/or timeseries should be added to the collection of indicators of an expert (a pointer to which is passed as a parameter).

The overridden method must return true, if all manipulations with the indicators and/or timeseries were successful (they are suitable for use). If at least one operation with the indicators and/or timeseries failed, the method must return false (further work is impossible).

Base class CExpertSignal does not use indicators or timeseries, therefore, the base class method always returns true, without performing any action.


1.2. Methods of checking the signal of position opening:

1.2.1 CheckOpenLong

The CheckOpenLong() method generates a signal of opening of a long position, defining the entry level and levels of protective orders placing. It is called by an expert to determine whether it is necessary to open a long position. The method must be overridden, if it is expected that a signal of a long position opening will be generated.

virtual bool CheckOpenLong(double& price, double& sl, double& tp, datetime& expiration);

The method should implement the algorithm of checking the condition of a long position opening. If the condition is met, the variables price, sl, tp, and expiration (references to which are passed as parameters) must be assigned appropriate values and the method should return true. If the condition is not fulfilled, the method must return false.

Base class CExpertSignal has no built-in algorithm for generating a signal of a long position opening, so the base class method always returns false.

1.2.2 CheckOpenShort

The CheckOpenShort() method generates a signal of opening of a short position, defining the entry level and levels of protective orders placing. It is called by an expert to determine whether it is necessary to open a short position. The method must be overridden, if it is expected that a signal of a short position opening will be generated. 

virtual bool CheckOpenShort(double& price, double& sl, double& tp, datetime& expiration);

The method must implement the algorithm for checking the condition to open a short position. If the condition is satisfied, the variables price, sl, tp, and expiration (references to which are passed as parameters) must be assigned appropriate values and the method should return true. If the condition is not fulfilled, the method must return false.

Base class CExpertSignal has no built-in algorithm for generating a signal of a short position opening, so the base class method always returns false.


1.3. Methods of checking the signal of position closing:

1.3.1 CheckCloseLong

The CheckCloseLong() method generates a signal of closing of a long position, defining the exit level. It is called by an expert to determine whether it is necessary to close a long position. The method must be overridden, if it is expected that a signal of a long position closing will be generated.

virtual bool CheckCloseLong(double& price);

The method must implement the algorithm for checking the condition to close the long position. If the condition is satisfied, the variable price (the reference to which is passed as a parameter) must be assigned the appropriate value and the method should return true. If the condition is not fulfilled, the method must return false.

Base class CExpertSignal has no built-in algorithm for generating a signal of a long position closing, so the base class method always returns false.

1.3.2 CheckCloseShort

The CheckCloseShort() method generates a signal of closing of a short position, defining the exit level. It is called by an expert to determine whether it is necessary to close a short position. The method must be overridden, if it is expected that a signal of a short position closing will be generated.

virtual bool CheckCloseShort(double& price);

The method must implement the algorithm for checking the condition to close a short position. If the condition is satisfied, the variable price (the reference to which is passed as a parameter) must be assigned the appropriate value and the method should return true. If the condition is not fulfilled, the method must return false.

Base class CExpertSignal has no built-in algorithm for generating a signal of a short position closing, so the base class method always returns false.


1.4. Methods of checking the signal of position reversal:

1.4.1 CheckReverseLong

The CheckReverseLong method generates a signal of reversal of a long position, defining the reversal level and levels of protective orders placing. It is called by an expert to determine whether it is necessary to reverse a long position. The method must be overridden, if it is expected that a signal of a long position reversal will be generated.

virtual bool CheckReverseLong(double& price, double& sl, double& tp, datetime& expiration);

The method must implement the algorithm for checking the condition of long position reversal. If the condition is satisfied, the variables price, sl, tp, and expiration (references to which are passed as parameters) must be assigned appropriate values and the method should return true. If the condition is not fulfilled, the method must return false.

In the CExpertSignal base class, the following algorithm for generating a long position reversal signal is implemented:

  1. Checking for a signal to close a long position.
  2. Checking for a signal to open a short position.
  3. If both signals are active (the conditions are met) and the close and open prices match, the variables price, sl, tp, and expiration (references to which are passed as parameters) are assigned the appropriate values and the method returns true.
If the condition is not fulfilled, the method returns false.

1.4.2 CheckReverseShort

The CheckReverseShort method generates a signal of reversal of a short position, defining the reversal level and levels of protective orders placing. It is called by an expert to determine whether it is necessary to reverse a short position. The method must be overridden, if it is expected that a signal of a long position reversal will be generated according to the algorithm that differs from the one implemented in the base class.

virtual bool CheckReverseShort(double& price, double& sl, double& tp, datetime& expiration);

The method must implement the algorithm for checking the condition of short position reversal. If the condition is satisfied, the variables price, sl, tp, and expiration (references to which are passed as parameters) must be assigned appropriate values and the method should return true. If the condition is not fulfilled, the method must return false.

In the CExpertSignal base class, the following algorithm for generating a short position reversal signal is implemented:

  1. Checking for a signal to close a short position.
  2. Checking for a signal to open a long position.
  3. If both signals are active (the conditions are met) and the close and open prices match, the variables price, sl, tp, and expiration (references to which are passed as parameters) are assigned the appropriate values and the method returns true.

If the condition is not fulfilled, the method returns false.


1.5. Methods of checking the signal of pending order modification:

1.5.1 CheckTrailingOrderLong

The CheckTrailingOrderLong() method generates the signal of modification of a pending Buy order, defining a new order price. It is called by an expert to determine whether it is necessary to modify a pending Buy order. The method must be overridden, if it is expected that a signal of modification of a pending Buy order will be generated.

virtual bool CheckTrailingOrderLong(COrderInfo* order, double& price)

The method must implement the algorithm for checking the condition of modification of a pending Buy order. If the condition is satisfied, the variable price (the reference to which is passed as a parameter) must be assigned the appropriate value and the method should return true. If the condition is not fulfilled, the method must return false.

Base class CExpertSignal has no built-in algorithm for generating a signal of modification of a pending Buy order, so the base class method always returns false.

1.5.2 CheckTrailingOrderShort

The CheckTrailingOrderShort() method generates the signal of modification of a pending Sell order, defining a new order price. It is called by an expert to determine whether it is necessary to modify a pending Sell order. The method must be overridden, if it is expected that a signal of modification of a pending Sell order will be generated.

virtual bool CheckTrailingOrderShort(COrderInfo* order, double& price)

The method must implement the algorithm for checking the condition of modification of a pending Sell order. If the condition is satisfied, the variable price (the reference to which is passed as a parameter) must be assigned the appropriate value and the method should return true. If the condition is not fulfilled, the method must return false.

Base class CExpertSignal has no built-in algorithm for generating a signal of modification of a pending Sell order, so the base class method always returns false.


2. Develop Your Own Generator of Trading Signals

Now, after we have reviewed the structure of the CExpertSignal base class, you can start creating your own trading signals generator.

As mentioned above, the CExpertSignal class is a set of public virtual "ropes" - methods, using which the expert may know the opinion of the trading signals generator about entering the market in one direction or another.

Therefore, our primary goal is to create our own class of trading signals generator, deriving it from the CExpertSignal class and overriding the appropriate virtual methods, implementing the required algorithms.

Our second problem (which is not less important) - to make our class "visible" to MQL5 Wizard. But, first things first.

2.1. Creating the class of the trading signals generator

Let's begin.

First, we create (for example, using the same MQL5 Wizard) an include file with the mqh extension.

In the File menu select "Create" (or press Ctrl+N key combination) and indicate the creation of an included file:

Figure 2. Create an include file using MQL5 Wizard.

Figure 2. Create an include file using MQL5 Wizard

It should be noted that in order for the file to be then "detected" by MQL5 Wizard as a signal generator, it should be created in the folder Include\Expert\Signal\.

In order not to trash in Standard Library, create our own folder Include\Expert\Signal\MySignals, in which we create file SampleSignal.mqh, specifying these parameters in MQL5 Wizard:

Figure 3. Setting the location of the include file

Figure 3. Setting the location of the include file

As a result of MQL5 Wizard operation we have the following pattern:

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

The following is only "manual" work. Remove the unnecessary parts and add what is required (include file ExpertSignal.mqh of the Standard Library and a class description which is now empty).

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| The CSampleSignal class.                                         |
//| Purpose: Class of trading signal generator.                      |
//|          It is derived from the CExpertSignal class.             |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
  };
//+------------------------------------------------------------------+

Now, it is necessary to choose the algorithms.

As a basis for our trading signals generator, we take the widespread model "price crosses the moving average". But we make one more assumption: "After crossing the moving average, the price moves back, and only then goes in the right direction." Reflect this in our file.

Generally, when you are writing something, do not skimp on the comments. After some time, reading a carefully commented code will be so comfortable.

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| Class CSampleSignal.                                             |
//| Purpose: Class of trading signal generator when price            |
//|          crosses moving average,                                 |
//|          entering on the subsequent back movement.               |
//|          It is derived from the CExpertSignal class.             |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
  };
//+------------------------------------------------------------------+

Now let's define what data is needed for making decisions about the generation of trading signals. In our case, this is the open price and the close price of the previous bar, and the value of the moving average on the same previous bar.

To get access to these data, we use the standard library classes CiOpen, CiClose and CiMA. We'll discuss indicators and timeseries later.

In the meantime, let's define a list of settings for our generator. First, we need to set up the moving average. These parameters include the period, the shift along the time axis, the averaging method and the object of averaging. Secondly, we need to set up the entry level and the levels of placing of protective orders, and the lifetime of a pending order, because we are going to work with pending orders.

All settings of the generator will be stored in protected data members of the class. Access to the settings will be implemented through appropriate public methods.

Let's include these changes in our file:

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| The CSampleSignal class.                                         |
//| Purpose: Class of trading signal generator when price            |
//|             crosses moving average,                              |
//|             entering on the subsequent back movement.            |
//|             It is derived from the CExpertSignal class.          |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
protected:
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;           // level to place a pending order relative to the MA
   double             m_stop_loss;       // level to place a stop loss order relative to the open price
   double             m_take_profit;     // level to place a take profit order relative to the open price
   int                m_expiration;      // lifetime of a pending order in bars

public:
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;   }
   void               ShiftMA(int value)                  { m_shift_ma=value;    }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;   }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;  }
   void               Limit(double value)                 { m_limit=value;       }
   void               StopLoss(double value)              { m_stop_loss=value;   }
   void               TakeProfit(double value)            { m_take_profit=value; }
   void               Expiration(int value)               { m_expiration=value;  }
  };
//+------------------------------------------------------------------+

Since we are using protected data members, we need to add a class constructor, in which we will initialize these data by default values.

To check the parameters, let's override the virtual method ValidationSettings according to the description of the base class.

Description of the class:

class CSampleSignal : public CExpertSignal
  {
protected:
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;       // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;   }
   void               ShiftMA(int value)                  { m_shift_ma=value;    }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;   }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;  }
   void               Limit(double value)                 { m_limit=value;       }
   void               StopLoss(double value)              { m_stop_loss=value;   }
   void               TakeProfit(double value)            { m_take_profit=value; }
   void               Expiration(int value)               { m_expiration=value;  }
   //--- Methods to validate the parameters
   virtual bool       ValidationSettings();
  };

Implementation of the ValidationSettings() method:


//+------------------------------------------------------------------+
//| Validation of the setup parameters.                              |
//| INPUT:  No.                                                      |
//| OUTPUT: true if the settings are correct, otherwise false.       |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::ValidationSettings()
  {
//--- Validation of parameters
   if(m_period_ma<=0)
     {
      printf(__FUNCTION__+": the MA period must be greater than zero");
      return(false);
     }
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+

Now, when we've finished the bulk of the preparatory work, we'll talk more about indicators and timeseries.

Indicators and timeseries are the main source of information for decision-making (you can certainly use the coin toss, or phases of the moon, but they are quite hard to formalize).

As we have already defined above, to make decisions, we need the following information: the open price of the previous bar, the close price of the previous bar, and the value of the moving average on the same previous bar.

To gain access to these data, we will use the following classes of the Standard Library:

You may ask: "Why use the indicator or timeseries, " wrapped " in a class, in order to get a single number?"

There is a hidden meaning, which we are going to reveal now.

How to use the data of an indicator or timeseries?

First, we need to create an indicator.

Second, we need to copy the necessary amount of data into an intermediate buffer.

Third, we need to check whether copying is complete.

Only after these steps, you can use the data.

Using the classes of the Standard Library, you avoid the necessity of creating an indicator, of caring about the availability of intermediate buffers and about data loading or release of a handle. The object of an appropriate class will do that for you. All the required indicators will be generated by our signal generator during the initialization stage, and all indicators will be provided with the necessary temporary buffer. And besides, once we add an indicator or timeseries object in the collection (the object of a special class), you can stop caring about the relevance of the data (the data will be updated automatically by the expert).

We'll place the objects of these classes in the protected data members. For each object, we create a method of initialization and data access method.

Let's override the virtual method InitIndicators (according to the description of the base class).

Description of the class:

class CSampleSignal : public CExpertSignal
  {
protected:
   CiMA               m_MA;              // object to access the values om the moving average
   CiOpen             m_open;            // object to access the bar open prices
   CiClose            m_close;           // object to access the bar close prices
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;      // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;              }
   void               ShiftMA(int value)                  { m_shift_ma=value;               }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;              }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;             }
   void               Limit(double value)                 { m_limit=value;                  }
   void               StopLoss(double value)              { m_stop_loss=value;              }
   void               TakeProfit(double value)            { m_take_profit=value;            }
   void               Expiration(int value)               { m_expiration=value;             }
   //--- Method to validate the parameters
   virtual bool       ValidationSettings();
   //--- Method to validate the parameters
   virtual bool       InitIndicators(CIndicators* indicators);

protected:
   //--- Object initialization method
   bool               InitMA(CIndicators* indicators);
   bool               InitOpen(CIndicators* indicators);
   bool               InitClose(CIndicators* indicators);
   //--- Methods to access object data
   double             MA(int index)                       { return(m_MA.Main(index));       }
   double             Open(int index)                     { return(m_open.GetData(index));  }
   double             Close(int index)                    { return(m_close.GetData(index)); }
  };

Implementation of methods InitIndicators, InitMA, InitOpen, InitClose:

//+------------------------------------------------------------------+
//| Initialization of indicators and timeseries.                     |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitIndicators(CIndicators* indicators)
  {
//--- Validation of the pointer
   if(indicators==NULL)       return(false);
//--- Initialization of the moving average
   if(!InitMA(indicators))    return(false);
//--- Initialization of the timeseries of open prices
   if(!InitOpen(indicators))  return(false);
//--- Initialization of the timeseries of close prices
   if(!InitClose(indicators)) return(false);
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the moving average                             |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitMA(CIndicators* indicators)
  {
//--- Initialization of the MA object
   if(!m_MA.Create(m_symbol.Name(),m_period,m_period_ma,m_shift_ma,m_method_ma,m_applied_ma))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
   m_MA.BufferResize(3+m_shift_ma);
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_MA)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of open prices.                 |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitOpen(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_open.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_open)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of close prices.                |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitClose(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_close.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_close)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+

All the preparatory works are completed. As you can see, our class has grown significantly.

But now we are ready to generate trading signals.

Figure 4. Trading signals for the price crossing the moving average

Figure 4. Trading signals for the price crossing the moving average

Let's consider our algorithms again in more detail.

1. The signal to buy appears when the following conditions have been fulfilled on the previous bar:

In this case, we offer to place a pending Buy order with the parameters defined by the settings. For this purpose, we override the virtual method CheckOpenLong and fill it with the corresponding functional.

2. The signal to sell appears when the following conditions have been fulfilled on the previous bar:

In this case, we offer to place a pending Sell order with the parameters defined by the settings. For this purpose, we override the virtual method CheckOpenShort and fill it with the corresponding functional.

3. We will not generate signals to close positions. Let the positions be closed by Stop Loss/Take Profit ордерам.

Accordingly, we will not override virtual methods CheckCloseLong and CheckCloseShort.

4. We will propose the modification of a pending order along the moving average at the "distance" specified by the settings.

 For this purpose, we override the virtual methods CheckTrailingOrderLong and CheckTrailingOrderShort, filling them with corresponding functional.

Description of the class:

class CSampleSignal : public CExpertSignal
  {
protected:
   CiMA               m_MA;              // object to access the values of the moving average
   CiOpen             m_open;            // object to access the bar open prices
   CiClose            m_close;           // object to access the bar close prices
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;       // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters

   void               PeriodMA(int value)                 { m_period_ma=value;              }
   void               ShiftMA(int value)                  { m_shift_ma=value;               }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;              }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;             }
   void               Limit(double value)                 { m_limit=value;                  }
   void               StopLoss(double value)              { m_stop_loss=value;              }
   void               TakeProfit(double value)            { m_take_profit=value;            }
   void               Expiration(int value)               { m_expiration=value;             }
   //--- Method to validate the parameters
   virtual bool       ValidationSettings();
   //--- Method to validate the parameters
   virtual bool       InitIndicators(CIndicators* indicators);
   //--- Methods to generate signals to enter the market
   virtual bool      CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration);
   virtual bool      CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration);
   //--- Methods to generate signals of pending order modification
   virtual bool      CheckTrailingOrderLong(COrderInfo* order,double& price);
   virtual bool      CheckTrailingOrderShort(COrderInfo* order,double& price);

protected:
   //--- Object initialization method
   bool               InitMA(CIndicators* indicators);
   bool               InitOpen(CIndicators* indicators);
   bool               InitClose(CIndicators* indicators);
   //--- Methods to access object data
   double             MA(int index)                       { return(m_MA.Main(index));       }
   double             Open(int index)                     { return(m_open.GetData(index));  }
   double             Close(int index)                    { return(m_close.GetData(index)); }
  };

Implementation of methods CheckOpenLong, CheckOpenShort, CheckTrailingOrderLong, CheckTrailingOrderShort:

//+------------------------------------------------------------------+
//| Check whether a Buy condition is fulfilled                       |
//| INPUT:  price      - variable for open price                     |
//|         sl         - variable for stop loss price,               |
//|         tp         - variable for take profit price              |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double spread=m_symbol.Ask()-m_symbol.Bid();
   double ma    =MA(1);
   double unit  =PriceLevelUnit();
//--- Checking the condition
   if(Open(1)<ma && Close(1)>ma && ma>MA(2))
     {
      price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
      sl   =m_symbol.NormalizePrice(price-m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price+m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether a Sell condition is fulfilled.                     |
//| INPUT:  price      - variable for open price,                    |
//|         sl         - variable for stop loss,                     |
//|         tp         - variable for take profit                    |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
//--- Checking the condition
   if(Open(1)>ma && Close(1)<ma && ma<MA(2))
     {
      price=m_symbol.NormalizePrice(ma+m_limit*unit);
      sl   =m_symbol.NormalizePrice(price+m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price-m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//|  of a Buy order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderLong(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double spread   =m_symbol.Ask()-m_symbol.Bid();
   double ma       =MA(1);
   double unit     =PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//| of a Sell order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderShort(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma+m_limit*unit);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+

So we've solved the first problem. The above code is a source code of the class of trading signals generator that meets our main task.


2.2. Preparing a description of the created class of the trading signals for MQL5 Wizard

We now turn to solving the second problem. Our signal should be "recognized" by the generator of trading strategies MQL5 Wizard.

We've done the first necessary condition: we've placed the file where it will be "found" by the MQL5 Wizard. But this is not enough. The MQL5 Wizard must not only "find" the file, but also "recognize" it. To do this we must add to the original text the class descriptor for the MQL5 Wizard.

A class descriptor is a block of comments composed according to certain rules.

Let's consider these rules.

1. The block of comments should start with the following lines:

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |

2. The next line is a text descriptor (what we will see in the MQL5 Wizard when choosing the signal) in the format "//| Title=<Text> |". If the text is too big for one line, you can add one more line (but not more) after it. 

In our case, we have the following:

//| Title=Signal on the crossing of a price and the MA               |
//| entering on its back movement                                    |

3. Then comes a line with the class type specified in the format "//| Type=<Type> |". The <Type> field must have the Signal value (in addition to signals, the MQL5 Wizard knows other types of classes).

Write:

//| Type=Signal                                                      |

4. The following line in the format "//| Name=<Name> |" is the short name of the signal (it is used by the MQL5 Wizard for generating the names of the global variables of the expert).

We get the following:

//| Name=Sample                                                      |

5. The name of a class is an important element of the description. In the line with the format "//| Class=<ClassNameĞ°> |", the <ClassName> parameter must match with the name of our class:

//| Class=CSampleSignal                                              |

6. We do not fill in this line, but it must be present (this is a link to the language reference section):

//| Page=                                                            |

7. Further, there are descriptions of the signal setup parameters.

This is a set of rows (the number of rows is equal to the number of parameters).

The format of each line is "//| Parameter=<NameOfMethod>,<TypeOfParameter>,<DefaultValue> |".

Here is our set of parameters:

//| Parameter=PeriodMA,int,12                                        |
//| Parameter=ShiftMA,int,0                                          |
//| Parameter=MethodMA,ENUM_MA_METHOD,MODE_EMA                       |
//| Parameter=AppliedMA,ENUM_APPLIED_PRICE,PRICE_CLOSE               |
//| Parameter=Limit,double,0.0                                       |
//| Parameter=StopLoss,double,50.0                                   |
//| Parameter=TakeProfit,double,50.0                                 |
//| Parameter=Expiration,int,10                                      |

8. The block of comment should end with the following lines:

//+------------------------------------------------------------------+
// wizard description end

Let's add the descriptor to the source code.

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signal on crossing of the price and the MA                 |
//| entering on the back movement                                    |
//| Type=Signal                                                      |
//| Name=Sample                                                      |
//| Class=CSampleSignal                                              |
//| Page=                                                            |
//| Parameter=PeriodMA,int,12                                        |
//| Parameter=ShiftMA,int,0                                          |
//| Parameter=MethodMA,ENUM_MA_METHOD,MODE_EMA                       |
//| Parameter=AppliedMA,ENUM_APPLIED_PRICE,PRICE_CLOSE               |
//| Parameter=Limit,double,0.0                                       |
//| Parameter=StopLoss,double,50.0                                   |
//| Parameter=TakeProfit,double,50.0                                 |
//| Parameter=Expiration,int,10                                      |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| CSampleSignal class.                                             |
//| Purpose: Class of trading signal generator when price            |
//|             crosses moving average,                              |
//|             entering on the subsequent back movement.            |
//|             It is derived from the CExpertSignal class.          |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
protected:
   CiMA               m_MA;               // object to access the values of the moving average
   CiOpen             m_open;             // object to access the bar open prices
   CiClose            m_close;            // object to access the bar close prices
   //--- Setup parameters
   int                m_period_ma;        // averaging period of the MA
   int                m_shift_ma;         // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;        // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;       // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;       // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;              }
   void               ShiftMA(int value)                  { m_shift_ma=value;               }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;              }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;             }
   void               Limit(double value)                 { m_limit=value;                  }
   void               StopLoss(double value)              { m_stop_loss=value;              }
   void               TakeProfit(double value)            { m_take_profit=value;            }
   void               Expiration(int value)               { m_expiration=value;             }
   //---Method to validate the parameters
   virtual bool       ValidationSettings();
   //--- Method to validate the parameters
   virtual bool       InitIndicators(CIndicators* indicators);
   //--- Methods to generate signals to enter the market
   virtual bool      CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration);
   virtual bool      CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration);
   //--- Methods to generate signals of pending order modification
   virtual bool      CheckTrailingOrderLong(COrderInfo* order,double& price);
   virtual bool      CheckTrailingOrderShort(COrderInfo* order,double& price);

protected:
   //--- Object initialization method
   bool               InitMA(CIndicators* indicators);
   bool               InitOpen(CIndicators* indicators);
   bool               InitClose(CIndicators* indicators);
   //--- Methods to access object data
   double             MA(int index)                       { return(m_MA.Main(index));       }
   double             Open(int index)                     { return(m_open.GetData(index));  }
   double             Close(int index)                    { return(m_close.GetData(index)); }
  };
//+------------------------------------------------------------------+
//| CSampleSignal Constructor.                                       |
//| INPUT:  No.                                                      |
//| OUTPUT: No.                                                      |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
void CSampleSignal::CSampleSignal()
  {
//--- Setting the default values
   m_period_ma  =12;
   m_shift_ma   =0;
   m_method_ma  =MODE_EMA;
   m_applied_ma =PRICE_CLOSE;
   m_limit      =0.0;
   m_stop_loss  =50.0;
   m_take_profit=50.0;
   m_expiration =10;
  }
//+------------------------------------------------------------------+
//| Validation of parameters.                                        |
//| INPUT:  No.                                                      |
//| OUTPUT: true if the settings are correct, otherwise false.       |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::ValidationSettings()
  {
//--- Validation of parameters
   if(m_period_ma<=0)
     {
      printf(__FUNCTION__+": the MA period must be greater than zero");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of indicators and timeseries.                     |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitIndicators(CIndicators* indicators)
  {
//--- Validation of the pointer
   if(indicators==NULL)       return(false);
//--- Initialization of the moving average
   if(!InitMA(indicators))    return(false);
//--- Initialization of the timeseries of open prices
   if(!InitOpen(indicators))  return(false);
//--- Initialization of the timeseries of close prices
   if(!InitClose(indicators)) return(false);
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the moving average                             |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitMA(CIndicators* indicators)
  {
//--- Initialization of the MA object
   if(!m_MA.Create(m_symbol.Name(),m_period,m_period_ma,m_shift_ma,m_method_ma,m_applied_ma))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
   m_MA.BufferResize(3+m_shift_ma);
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_MA)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of open prices.                 |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitOpen(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_open.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_open)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of close prices.                |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitClose(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_close.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_close)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Check whether a Buy condition is fulfilled                       |
//| INPUT:  price      - variable for open price                     |
//|         sl         - variable for stop loss price,               |
//|         tp         - variable for take profit price              |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double spread=m_symbol.Ask()-m_symbol.Bid();
   double ma    =MA(1);
   double unit  =PriceLevelUnit();
//--- Checking the condition
   if(Open(1)<ma && Close(1)>ma && ma>MA(2))
     {
      price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
      sl   =m_symbol.NormalizePrice(price-m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price+m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether a Sell condition is fulfilled.                     |
//| INPUT:  price      - variable for open price,                    |
//|         sl         - variable for stop loss,                     |
//|         tp         - variable for take profit                    |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
//--- Checking the condition
   if(Open(1)>ma && Close(1)<ma && ma<MA(2))
     {
      price=m_symbol.NormalizePrice(ma+m_limit*unit);
      sl   =m_symbol.NormalizePrice(price+m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price-m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//|  of a Buy order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderLong(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double spread   =m_symbol.Ask()-m_symbol.Bid();
   double ma       =MA(1);
   double unit     =PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//| of a Sell order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderShort(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma+m_limit*unit);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+

Well, that's all. The signal is ready to use.

For the generator trading strategies MQL5 Wizard to be able to use our signal, we should restart MetaEditor (MQL5 Wizard scans the folder Include\Expert only at boot).

After restarting MetaEditor, the created module of trading signals can be used in the MQL5 Wizard:

Figure 5. The created generator of trading signals in the MQL5 Wizard

Figure 5. The created generator of trading signals in the MQL5 Wizard

The input parameters specified in the section of description of the parameters of the trading signals generator are now available:

Figure 6. Input parameters of the created generator of trading signals in the MQL5 Wizard

Figure 6. Input parameters of the created generator of trading signals in the MQL5 Wizard

The best values of the input parameters of the implemented trading strategy can be found using the Strategy Tester of the MetaTrader 5 terminal.


Conclusion

The generator of trading strategies of the MQL5 Wizard greatly simplifies the testing of trading ideas. The code of the generated expert is based on the classes of trading strategies of the Standard Library, which are used for creating certain implementations of trading signal classes, money and risk management classes and position support classes. 

The article discusses how to write your own class of trading signals with the implementation of signals on the crossing of the price and the moving average, and how to include it to the generator of trading strategies of the MQL5 Wizard, as well as describes the structure and format of the description of the generated class for the MQL5 Wizard.