MQL5 Wizard: How to Create a Module of Trailing of Open Positions
Introduction
MetaTrader 5 has a powerful tool for quick checking of trade ideas. This is the generator of trade strategies MQL5 Wizard. The use of MQL5 Wizard for automatic creation of source code of Experts Advisors is described in the "MQL5 Wizard: Creating Expert Advisors without Programming" article. Openness of the system of generation of code allows supplementing the standard classes with custom classes of trade signals, money management systems and trailing modules.
This article describes the principles of writing modules of trailing open position for their further use in the MQL5 Wizard.
An Expert Advisor created using the MQL5 Wizard is based on four base classes:
Figure 1. Structure of the base class CExpert.
The CExpert class (or its subclass) is the main "engine" of the trade robot. The instance of the CExpert class contains instances of the CExpertSignal, CExpertMoney and CExpertTrailing classes (or their subclasses):
- CExpertSignal - is the main generator of trade signals. The instance of the CExpertSignal subclass included in the CExpert class provides the Expert Advisor with the information about possibilities of entering the market, entry levels and set up of protection orders on the basis of internal algorithms. The final decision on performing trade operations is made by the Expert Advisor. You can read about how to write a module of trade signals in the "MQL5 Wizard: How to Create a Module of Trading Signals" article.
- CExpertMoney is the main system of managing money and risk. The instance of the CExpertMoney subclass calculates volumes of position to be opened and pending orders to be placed. The final decision on volumes is made the Expert Advisor. The principles of development of modules of money and risk management are described in the "MQL5 Wizard: How to Create a Module of Money and Risk Management" article.
- CExpertTrailing is the main module of following of open positions. The instance of the CExpertTrailing subclass tells the Expert Advisor if it's necessary to modify the protection orders of a position. The final decision on modification of order is made by the Expert Advisor.
In addition to it, the following class instances are the members of the CExpert class:
- CExpertTrade (for performing trade operations)
- CIndicators (for managing indicators and timeseries used in working of the Expert Advisor)
- CSymbolInfo (for getting the information about a symbol)
- CAccountInfo (for getting information about the state of trade account)
- CPositionInfo (for getting information about positions)
- COrderInfo (for getting information about pending orders)
Further in this text, when saying "Expert Advisor" we will mean an instance of the CExpert class of its subclass.
A more detailed description of the CExpert class and process of working with it will be given in a separate article.
1. Base Class CExpertTrailing
The CExpertTrailing is the basis of the module of following open positions. To interact with the "outer space" the CExpertTrailing class has a set of public virtual methods:
Initialization | Description |
virtual Init | Initialization of the class instance provides synchronization of data of the module with data of the EA |
virtual ValidationSettings | Validation of set parameters |
virtual InitIndicators | Creation and initialization of all indicators and timeseries required for operation of the generator of trade signals |
Signals of Modification of Positions |
|
virtual CheckTrailingStopLong | Generation of a signal for modification of a long position with determination of new price for Stop order |
virtual CheckTrailingStopShort | Generation of a signal for modification of a short position with determination of new price for Stop order |
Description of Methods
1.1. Methods of Initialization
The Init() method is automatically called right after adding the class instance to the Expert Advisor. Overriding of the method is not required.
virtual bool Init(CSymbolInfo* symbol, ENUM_TIMEFRAMES period, double adjusted_point);
The ValidationSettings() method is called from the Expert Advisor after setting up of all parameters. It is necessary to override the method if there are setup settings.
virtual bool ValidationSettings();
The overridden method must return true if all parameters are correct (suitable for use). If any of parameters is invalid, the parameter must return false (further operation is impossible).
The InitIndicators() method creates and initializes all required indicators and timeseries. It is called from the Expert Advisor after all the parameters are set and validated. The method should be overridden in case the generator of trade signals uses at least one indicator or timeseries.
virtual bool InitIndicators(CIndicators* indicators);
Indicators and/or timeseries should be used through the corresponding of the Standard Library. Pointers of all indicators and/or timeseries should be added to the collection of indicators of the Expert Advisor (pointer to which is passes as a parameter).
The overridden method should 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 operation is impossible).
1.2. Methods of Checking the Signal of Position Modification
The CheckTrailingStopLong() method generates a signal of modification of a long position, defining a new price of the Stop Loss order (as well as for the Take Profit order if it's necessary). It's called by the Expert Advisor to determine whether it is necessary to modify a long position. It should be overridden, if you want to generate a signal of a long position modification.
virtual bool CheckTrailingStopLong(CPositionInfo* position,double& sl,double& tp)
The method must implement the algorithm of checking the condition of modification of the long position. If the condition is satisfied, the sl variable (as well as tp, if it's necessary) must be assigned the appropriate value and the method should return true. Links to the sl and tp variables must be passed as parameters. If the condition is not satisfied, the method must return false.
The CheckTrailingStopShort() method generates a signal of a short position modification, determining a new price of the Stop Loss order (as well as the Take Profit order, if it's necessary). It is called by the Expert Advisor for determining whether it is necessary to modify a short position. The method should be overridden, if you want to generate a signal of a short position modification.
virtual bool CheckTrailingStopShort(CPositionInfo* position,double& sl,double& tp)
The method must implement the algorithm for checking of the condition to modify a short position. If the condition is satisfied, the sl variable (as well as tp, if it's necessary) must be assigned the appropriate value and the method should return true. Links to the sl and tp variables must be passed as parameters. If the condition is not satisfied, the method must return false.
2. Writing You Own Module for Following of Open Positions
Now, after we have reviewed the structure of the base class CExpertTrailing, you can start creating your own module for following of open position.
As mentioned above, the CExpertTrailing class is a set of public virtual "ropes" - methods, using which the Expert Advisor may know the opinion of the module of trailing open positions about the necessity of modification of protection orders.
Therefore, our primary goal is to create our own class for following of open positions, deriving it from the CExpertTrailing class and overriding the appropriate virtual methods, implementing the required algorithms.
Our second goal (which is no less important) - to make our class "visible" to MQL5 Wizard. But, first things first.
2.1. Creating the Class of 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.
It should be noted that in order for the file to be then "detected" by MQL5 Wizard as a module of trailing open positions, it should be created in the folder Include\Expert\.
In order to trash in Standard Library, create our own folder Include\Expert\Trailing\MyTrailing, in which we create file SampleTrailing.mqh, specifying these parameters in МQL5 Wizard:
Figure 3. Setting the location of the include file.
As a result of MQL5 Wizard operation we have the following pattern:
//+------------------------------------------------------------------+ //| SampleTrailing.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 ExpertTrailing.mqh of the Standard Library and a class description which is now empty).
//+------------------------------------------------------------------+ //| SampleTrailing.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\ExpertTrailing.mqh> //+------------------------------------------------------------------+ //| Class CSampleTrailing. | //| Purpose: Class for trailing of open positions. | //| Is derived from the CExpertTrailing class. | //+------------------------------------------------------------------+ class CSampleTrailing : public CExpertTrailing { }; //+------------------------------------------------------------------+
Now, it is necessary to choose the algorithms.
Let's take the following algorithm as a basis of our module of trailing open positions: move the Stop order in to a lossless level, if the price goes in a required direction by a specified distance. Reflect this in our file.
//+------------------------------------------------------------------+ //| SampleTrailing.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\ExpertTrailing.mqh> //+------------------------------------------------------------------+ //| Class CSampleTrailing. | //| Purpose: Class for trailing of open positions by | //| moving the Stop order "to the loseless level". | //| Is derived from the CExpertTrailingclass. | //+------------------------------------------------------------------+ class CSampleTrailing : public CExpertTrailing { }; //+------------------------------------------------------------------+
No let's define what data is needed for making decisions about the modification of protection orders. In our case - it is the profit of a modified position in points.
Define the list of parameters of setting up of our module for following open positions. We need two parameters:
- Number of points of position profit required to suggest moving the Stop order to a lossless level.
- The lossless level, i.e. how many profit points we fix by the moved Stop order.
Settings of the module will be stored on 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:
//+------------------------------------------------------------------+ //| SampleTrailing.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\ExpertTrailing.mqh> //+------------------------------------------------------------------+ //| Class CSampleTrailing. | //| Purpose: Class for trailing of open positions | //| by moving Stop order to a lossless level. | //| Is derived from the CExpertTrailing class. | //+------------------------------------------------------------------+ class CSampleTrailing : public CExpertTrailing { protected: int m_profit; //threshold level of profit int m_stop_level; // lossless level public: //--- methods of setting adjustable parameters void Profit(int value) { m_profit=value; } void StopLevel(int value) { m_stop_level=value; } }; //+------------------------------------------------------------------+
To initialize adjustable parameters with default values, we need to add the class constructor.
To validate settings, override the virtual method ValidationSettings (according to description of the base class).
Description of the class:
//+------------------------------------------------------------------+ //| SampleTrailing.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\ExpertTrailing.mqh> //+------------------------------------------------------------------+ //| Class CSampleTrailing. | //| Purpose: Class for trailing of open positions | //| by moving Stop order to a lossless level. | //| Is derived from the CExpertTrailing class. | //+------------------------------------------------------------------+ class CSampleTrailing : public CExpertTrailing { protected: int m_profit; // threshold level of profit int m_stop_level; // lossless level public: CSampleTrailing(); //--- methods of setting adjustable parameters void Profit(int value) { m_profit=value; } void StopLevel(int value) { m_stop_level=value; } //--- method of validating the adjustable parameters virtual bool ValidationSettings(); }; //+------------------------------------------------------------------+
Implementation of the ValidationSettings() method:
//+------------------------------------------------------------------+ //| Validation of adjustable parameters. | //| INPUT: no. | //| OUTPUT: true if parameter are correct, false - if not. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleTrailing::ValidationSettings() { if(!CExpertTrailing::ValidationSettings()) return(false); //--- check wheter the Init method is called if(m_symbol==NULL) return(false); //--- check parameters if((m_profit-m_stop_level)*m_adjusted_point<=m_symbol.StopsLevel()*m_symbol.Point() && m_profit!=0.0) { printf(__FUNCTION__+": threshold level of profit must be greater than the level of setting of orders"); return(false); } //--- ok return(true); }
All the preparatory works are completed.
Let's consider our algorithms again in more detail.
1. A signal for modification of a long position appear when the following conditions are satisfied:
- Parameters of module settings imply that the position should be modified (if you set Profit=0, modification will not be performed);
- Position has not been modified yet (Stop order is not moved to a lossless level);
- Profit of the position exceeded a threshold level set in parameters.
In this case, suggest to modify the Stop order according to the settings. For this purpose, override the virtual method CheckTrailingStopLong and fill it with the corresponding functionality.
2. A signal of a short position modification appears when the following conditions are satisfied:
- Parameters of module settings imply that the position should be modified (if you set Profit=0, modification will not be performed);
- Position has not been modified yet (Stop order is not moved to a lossless level);
- Profit of the position exceeded a threshold level set in parameters.
In this case, suggest to modify the Stop order according to the settings. For this purpose, override the virtual method CheckTrailingStopShort and fill it with the corresponding functionality.
Description of the class:
class CSampleTrailing : public CExpertTrailing { protected: int m_profit; // threshold level of profit int m_stop_level; // lossless level public: CSampleTrailing(); //--- methods of setting adjustable parameters void Profit(int value) { m_profit=value; } void StopLevel(int value) { m_stop_level=value; } //--- method of validation of adjustable parameters virtual bool ValidationSettings(); //--- methods of generation of position modification signals virtual bool CheckTrailingStopLong(CPositionInfo* position,double& sl,double& tp); virtual bool CheckTrailingStopShort(CPositionInfo* position,double& sl,double& tp); };
Implementation of the CheckTrailingStopLong and CheckTrailingStopShort methods:
//+------------------------------------------------------------------+ //| Check for modification of stop orders of a long position. | //| INPUT: position - pointer to a position object, | //| sl - link for a new price of stop loss order, | //| tp - link for a new price of take profit order. | //| OUTPUT: true if condition is satisfied, false - if not. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleTrailing::CheckTrailingStopLong(CPositionInfo* position,double& sl,double& tp) { //--- check of pointer if(position==NULL) return(false); //--- check of parameter if(m_profit==0.0) return(false); //--- already in a lossless zone? double open=position.PriceOpen(); if(position.StopLoss()>=open) return(false); //--- check of profit sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(m_symbol.Bid()-open>m_profit*m_adjusted_point) sl=m_symbol.NormalizePrice(open+m_stop_level*m_adjusted_point); //--- return(sl!=EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Check for modification of stop orders of a short position. | //| INPUT: position - pointer to a position object, | //| sl - link to a new price of stop loss order, | //| tp - link to a new price of take profit order. | //| OUTPUT: true if condition is satisfied, false - if not. | //| REMARK: нет. | //+------------------------------------------------------------------+ bool CSampleTrailing::CheckTrailingStopShort(CPositionInfo* position,double& sl,double& tp) { //--- check of pointer if(position==NULL) return(false); //--- check of parameter if(m_profit==0.0) return(false); //--- already in a lossless zone? double open=position.PriceOpen(); if(position.StopLoss()<=open) return(false); //--- check of profit sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(open-m_symbol.Ask()>m_profit*m_adjusted_point) sl=m_symbol.NormalizePrice(open-m_stop_level*m_adjusted_point); //--- return(sl!=EMPTY_VALUE); }
2.2. Writing a description of the created class of the trading signals for MQL5 Wizard
We now turn to solving the second problem. Our module of following of open positions 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.
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. </p>
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=Trailing |
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=BreakEven |
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=CSampleTrailing |
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 parameters of the module settings.
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=Profit,int,20 | //| Parameter=StopLevel,int,0 |
8. The block of comment should end with the following lines:
//+------------------------------------------------------------------+ // wizard description end
Let's add the descriptor to the source code.
//+------------------------------------------------------------------+ //| SampleTrailing.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\ExpertTrailing.mqh> // wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Moving a position to a lossless level | //| Type=Trailing | //| Name=BreakEven | //| Class=CSampleTrailing | //| Page= | //| Parameter=Profit,int,20 | //| Parameter=StopLevel,int,0 | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CSampleTrailing. | //| Purpose: Class for trailing of open positions | //| by moving Stop order to a lossless level. | //| Is derived from the CExpertTrailing class. | //+------------------------------------------------------------------+ class CSampleTrailing : public CExpertTrailing { protected: int m_profit; // threshold level of profit int m_stop_level; // lossless level public: CSampleTrailing(); //--- method of setting adjustable parameters void Profit(int value) { m_profit=value; } void StopLevel(int value) { m_stop_level=value; } //--- method of validation of adjustable settings virtual bool ValidationSettings(); //--- methods of generation of position modification signals virtual bool CheckTrailingStopLong(CPositionInfo* position,double& sl,double& tp); virtual bool CheckTrailingStopShort(CPositionInfo* position,double& sl,double& tp); }; //+------------------------------------------------------------------+ //| Constructor CSampleTrailing. | //| INPUT: no. | //| OUTPUT: no. | //| REMARK: no. | //+------------------------------------------------------------------+ void CSampleTrailing::CSampleTrailing() { //--- setting default values m_profit =20; m_stop_level=0; } //+------------------------------------------------------------------+ //| Check of adjustable parameters. | //| INPUT: no. | //| OUTPUT: true if the parameters are correct, false if not. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleTrailing::ValidationSettings() { //--- what if the Init has not been called? if(m_symbol==NULL) return(false); //--- check of parameters if((m_profit-m_stop_level)*m_adjusted_point<=m_symbol.StopsLevel()*m_symbol.Point() && m_profit!=0.0) { printf(__FUNCTION__+": threshold level of profit must be greater than the level of setting stop orders"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Check for modification of stop orders of a long position. | //| INPUT: position - pointer to a position object, | //| sl - link for a new price of stop loss order, | //| tp - link for a new price of take profit order. | //| OUTPUT: true if condition is satisfied, false if not. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleTrailing::CheckTrailingStopLong(CPositionInfo* position,double& sl,double& tp) { //--- check of pointer if(position==NULL) return(false); //--- check of parameters if(m_profit==0.0) return(false); //--- already in a lossless zone? double open=position.PriceOpen(); if(position.StopLoss()>=open) return(false); //--- check of profit sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(m_symbol.Bid()-open>m_profit*m_adjusted_point) sl=m_symbol.NormalizePrice(open+m_stop_level*m_adjusted_point); //--- return(sl!=EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Check for modification of stop orders of a short position. | //| INPUT: position - pointer to a position object, | //| sl - link for a new price of stop loss order, | //| tp - link for a new take profit order. | //| OUTPUT: true if condition is satisfied, false if not. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleTrailing::CheckTrailingStopShort(CPositionInfo* position,double& sl,double& tp) { //--- check of pointer if(position==NULL) return(false); //--- check of parameters if(m_profit==0.0) return(false); //--- already in a lossless zone? double open=position.PriceOpen(); if(position.StopLoss()<=open) return(false); //--- check of profit sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(open-m_symbol.Ask()>m_profit*m_adjusted_point) sl=m_symbol.NormalizePrice(open-m_stop_level*m_adjusted_point); //--- return(sl!=EMPTY_VALUE); } //+------------------------------------------------------------------+
Well, that's all. The module of trailing is ready to use.
For the generator of trade strategies MQL5 Wizard to be able to use our module, we should restart MetaEditor (MQL5 Wizard scans the folder Include\Expert only at start).
After restarting MetaEditor, the created module of managing open positions can be used in the MQL5 Wizard:
Figure 5. The created module of managing open positions on the MQL5 Wizard.
The input parameters specified in the section of description of parameters of the module for managing open positions are now available:
Figure 6. The input parameters of the created module of managing open positions 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 and connect to the generator of trade strategies MQL5 Wizard your own class of managing open positions by moving the Stop Loss level to a lossless zone when the price goes in the position direction, allowing to decrease drawdowns when trading. It also tells about the structure and format of the description of the created class for the MQL5 Wizard.
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/231
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
There is a oversight in code, if you open position with no SL, for short position CheckTrailingStopShort() won't change it.. (I set SL one bar after)
should be:
if(position.StopLoss()<=open&&position.StopLoss()!=0.0) return(false);
I guess good idea always use SL... Thanks for the article..
Thank you for your great and helpful article,
May I kindly ask you bring an example how could use this class in writing an expert adviser, my mean is that using this class in EA template not EA generate..
Thank you again,..