Projects assist in creating profitable trading robots! Or at least, so it seems
Creation of a trading robot always starts with a small file, which then grows in size as you implement more additional functions and custom objects. Most of MQL5 programmers utilize include files (MQH) to handle this problem. However, there is a better solution: start developing any trading application in a project. There are so many reasons to do so.
Project benefits
A project is a separate file with the MQPROJ extension, which stores program settings, compilation parameters and information about all files used in the project. A separate tab in the Navigator is provided for a convenient work with the project. All files, such as include, resource, header and other files are arranged into categories in this tab.
You see, the project is not just a set of files and folders arranged under a separate directory. It allows breaking down a complex program into elements arranged into a well-balanced structure. All the required information is at hand:
- set files with input parameters for testing and optimization
- source codes of OpenCL programs
- image and sound media files
- resources and other data
All connections between program parts are clearly visible in the project, so you can easily navigate between all used files. Furthermore, multiple programmers can collaborate on a project via the built-in MQL5 Storage.
Creating a Project
A new project is created using the MQL5 Wizard as an ordinary MQL5 program. Click "New project" and go through the required steps: set the program name, add input parameters and specify utilized event handlers. Upon the completion of the MQL5 Wizard, an MQPROJ file will be opened. This file allows managing project properties.
>
Here you can specify the version, set a program description, add an icon and manage additional options:
- Maximum optimization — optimization of the EX5 executable file for maximum performance. If the option is disabled, compilation of the source code can be completed faster, but the resulting EX5 file can run much slower.
- Check floating point dividers — check whether real numbers of double and float types are not equal to zero in division operations. The operation speed can be higher if the option is disabled. However, you should be totally confident in your code.
- Use tester optimization cache — the tester is enabled by default, and thus the tester saves all results of completed passes to the optimization cache. The data can further be used in re-calculations. The cache can be disabled using the tester_no_cache property. Optionally, you can uncheck the relevant option in the project.
If the project file is closed, it can be reopened using the appropriate command of the Properties context menu. For a deeper understanding of the MQPROJ file contents, you can open it in the text format using the Open command. Thus, you can view the internal structure of projects.
{ "platform" :"mt5", "program_type":"expert", "copyright" :"Copyright 2019, MetaQuotes Software Corp.", "link" :"https:\/\/www.mql5.com", "version" :"1.00", "description" :"The mean reversion strategy: the price breaks the channel border outwards and reverts back towards the average. The channel is represented by Bollinger Bands. The Expert Advisor enters the market using limit orders, which can only be opened in the trend direction.", "icon" :"Mean Reversion.ico", "optimize" :"1", "fpzerocheck" :"1", "tester_no_cache":"0", "tester_everytick_calculate":"0", "files": [ { "path":".\\Mean Reversion.mq5", "compile":"true", "relative_to_project":"true" }, { "path":"MQL5\\Include\\Trade\\Trade.mqh", "compile":"false", "relative_to_project":"false" }, ....
Trading rules
Let us apply classical rule: enter the market when the price touches a Bollinger Band. This is one of the trading strategies, expecting the price to return to its mean value.
Only limit orders will be used for market entries. The additional rule will be as follows: trade only in the trend direction. Thus, a Buy Limit will be placed at the lower channel border in an uptrend. During a downtrend, a Sell Limit will be placed at the upper border.
There are many ways to determine trend direction. Let's use the simplest one: the relative position of two moving averages. If the Fast EMA is above the Slow EMA, an uptrend is defined. The downtrend is recognized when the lines are arranged the opposite way.
This simple rule has one disadvantage: there will always be either an uptrend or a downtrend. Therefore, such a system can produce a lot of false entries during flat. To avoid this, add another rule which allows placing pending orders when the distance between the Bollinger bands is large enough. The optimal way is to measure the channel width in relative values rather than points. The width can be determined based on the ATR indicator that measures volatility in points.
- Channel width below k*ATR indicates a flat, and thus placing orders is not allowed.
- If the channel width is greater than k*ATR, place a pending limit order at the channel border, in trend direction.
Here k is a certain coefficient which needs to be found.
Thus, during project creation we need to specify 8 input parameters for determining trading signals. The Expert Advisor will trade a fixed lot which should be specified in the InpLot parameter. Another non-optimizable parameter is InpMagicNumber. By using it, we can instruct the EA to handle only its own orders and positions.
//--- Channel parameters input int InpBBPeriod =20; // Bollinger indicator period input double InpBBDeviation=2.0; // Deviation of Bollinger bands from the MA //-- EMA periods for trend calculation input int InpFastEMA =12; // Fast EMA period input int InpSlowEMA =26; // Slow EMA period //-- ATR parameters input int InpATRPeriod =14; // ATR period input double InpATRCoeff =1.0; // ATR coefficient for determining the flat //--- Capital managements input double InpLot =0.1; // Trading volume in lots //--- timeframe parameters input ENUM_TIMEFRAMES InpBBTF =PERIOD_M15; // the timeframe for Bollinger values calculation input ENUM_TIMEFRAMES InpMATF =PERIOD_M15; // the timeframe for trend determining //--- Expert Advisor identifier for trading transactions input long InpMagicNumber=245600; // Magic Number
The InpBBTF and InpMATF parameters have been added to avoid the manual selection of timeframes for determining the trend and the channel width. In this case, optimal timeframe values can be found during optimization. The EA can run on the M1 timeframe, while using Bollinger Bands data from M15 and Moving Averages from M30. No input parameter is used for ATR, otherwise there would be to many parameters for this example.
Writing functions
After creating a project, we can proceed to developing the Expert Advisor. The below code shows the main three functions describing the rules.
The calculation of the Bollinger channel width is simple: copy the values from the indicator buffers.
//+------------------------------------------------------------------+ //| Gets the values of the channel borders | //+------------------------------------------------------------------+ bool ChannelBoundsCalculate(double &up, double &low) { //--- get the Bollinger Bands indicator values double bbup_buffer[]; double bblow_buffer[]; if(CopyBuffer(ExtBBHandle, 1, 1, 1, bbup_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtBBHandle,0,1,2,bbup_buffer), code=%d", __FILE__, GetLastError()); return(false); } if((CopyBuffer(ExtBBHandle, 2, 1, 1, bblow_buffer)==-1)) { PrintFormat("%s: Failed CopyBuffer(ExtBBHandle,0,1,2,bblow_buffer), code=%d", __FILE__, GetLastError()); return(false); } low=bblow_buffer[0]; up =bbup_buffer[0]; //--- successful return(true); }
Flat determining method is also simple enough. First, get the values of the channel borders, then calculate the width and compare to the ATR value multiplied by the InpATRCoeff coefficient.
//+------------------------------------------------------------------+ //| Returns true if the channel is too narrow (indication of flat) | //+------------------------------------------------------------------+ int IsRange() { //--- get the ATR value on the last completed bar double atr_buffer[]; if(CopyBuffer(ExtATRHandle, 0, 1, 1, atr_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtATRHandle,0,1,2,atr_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } double atr=atr_buffer[0]; //--- get the channel borders if(!ChannelBoundsCalculate(ExtUpChannel, ExtLowChannel)) return(NO_VALUE); ExtChannelRange=ExtUpChannel-ExtLowChannel; //--- if the channel width is less than ATR*coefficients, this is a flat if(ExtChannelRange<InpATRCoeff*atr) return(true); //--- flat not detected return(false); }
As can be seen from the code, the NO_VALUE macro code is returned sometimes, which means that calculation of a certain parameter failed.
#define NO_VALUE INT_MAX // invalid value when calculating Signal or Trend
Trend determining function has the longest code.
//+------------------------------------------------------------------+ //| Returns 1 for UpTrend or -1 for DownTrend (0 = no trend) | //+------------------------------------------------------------------+ int TrendCalculate() { //--- first, check the flat int is_range=IsRange(); //--- check the result if(is_range==NO_VALUE) { //--- if the check failed, early termination with "no value" response return(NO_VALUE); } //--- do not determine direction during flat if(is_range==true) // narrow range, return "flat" return(0); //--- get the ATR value on the last completed bar double atr_buffer[]; if(CopyBuffer(ExtBBHandle, 0, 1, 1, atr_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtATRHandle,0,1,2,atr_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } //--- get the Fast EMA value on the last completed bar double fastma_buffer[]; if(CopyBuffer(ExtFastMAHandle, 0, 1, 1, fastma_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtFastMAHandle,0,1,2,fastma_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } //--- get the Slow EMA value on the last completed bar double slowma_buffer[]; if(CopyBuffer(ExtSlowMAHandle, 0, 1, 1, slowma_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtSlowMAHandle,0,1,2,slowma_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } //--- trend is not defined by default int trend=0; //--- if fast EMA is above the slow one if(fastma_buffer[0]>slowma_buffer[0]) trend=1; // uptrend //--- if fast EMA is below the slow one if(fastma_buffer[0]<slowma_buffer[0]) trend=-1; // downtrend //--- return trend direction return(trend); }
The last function of the trading algorithm is the determining of a new bar. According to the logic, the trend is determined when a new bar appears.
//+------------------------------------------------------------------+ //| Checks the emergence of a new bar on the current timeframe, | //| also calculates the trend and the signal | //+------------------------------------------------------------------+ bool IsNewBar(int &trend) { //--- permanently stores the current bar opening time between function calls static datetime timeopen=0; //--- get the current bar open time datetime time=iTime(NULL, InpMATF, 0); //--- if the time has not changed, the bar is not new, so exit with the 'false' value if(time==timeopen) return(false); //--- the bar is new, and this trend direction should be calculated trend=TrendCalculate(); //--- if trend direction could not be obtained, exit and try again during the next call if(trend==NO_VALUE) return(false); //--- all checks performed successfully: the bar is new and trend direction has been obtained timeopen=time; //remember current time open time for further calls. //--- return(true); }
The above logic allows organizing the EA operation so that all trading operations are performed only once during the entire bar. Therefore, testing results do not depend on the tick generation mode.
The entire trading algorithm is presented in the OnTick() handler:
- The emergence of a new bar and the trend direction are determined first.
- If there is no trend or a position is open, an attempt is performed to delete pending orders and to exit from the handler.
- If there is a directional trend and no pending order, an attempt is performed to place a limit order at the channel border.
- If a pending order already exists and it has not been modified on the new bar, an attempt is performed to move it to the current channel border.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { static bool order_sent =false; // failed to place a limit order on the current bar static bool order_deleted =false; // failed to delete a limit order on the current bar static bool order_modified=false; // failed to modify a limit order on the current bar //--- if input parameters are invalid, stop testing at the first tick if(!ExtInputsValidated) TesterStop(); //--- check the emergence of a new bar and the trend direction if(IsNewBar(ExtTrend)) { //--- reset values of static variables to their original state order_sent =false; order_deleted =false; order_modified=false; } //--- create auxiliary variables to make check calls only once, on the current bar bool order_exist =OrderExist(); bool trend_detected=TrendDetected(ExtTrend); //--- if there is no trend or there is an open position, delete pending orders if(!trend_detected || PositionExist()) if(!order_deleted) { order_deleted=DeleteLimitOrders(); //--- if the orders have been successfully deleted, no other operations are needed at this bar if(order_deleted) { //--- prohibit placing and modification of orders order_sent =true; order_modified=true; return; } } //--- there is trend if(trend_detected) { //--- place an order at the channel border if no order is found if(!order_exist && !order_sent) { order_sent=SendLimitOrder(ExtTrend); if(order_sent) order_modified=true; } //--- try to move the order to the channel border if it has not been moved on the current bar if(order_exist && !order_modified) order_modified=ModifyLimitOrder(ExtTrend); } //--- }
Other trading functions of the Expert Advisor are standard. The source codes are available in the MetaTrader 5 terminal standard package. They are located under MQL5\Experts\Examples.
Optimizing parameters and adding set files
Now that the EA is ready, let's find optimal parameters in the strategy tester. Did you know that the tester provides options for easy copying of values from the "Settings" and "Inputs" tabs into the clipboard using the Ctr+C combination? Thus, you can provide your settings to another person, for example to a Customer via the Freelance chat, without having to save them to a set file. The customer can copy the data to the clipboard and paste into the Settings tab of the tester using Ctr+V.
Saving to a set file is also a convenient solution. Many sellers in the Market provide such files, so that product buyers can instantly load the appropriate sets of parameters and test or optimize the EA on a required instrument. Separate set files need to be created for each of the traded instruments. The number of such files on the computer can be quite large, if many Expert Advisors exist in the platform. With projects, your customers can instantly access the required files without the need to search for them on the disk each time the symbol is changed.
Here is an example of how projects can help to add appropriate parameter sets straight in the EA's EX5 file. Select the symbol for which the optimization will be performed. For example, EURUSD. Set Start, Step and Stop for the parameters you want to optimize and launch the optimization process. Once it is over, double click on the best pass in the Optimizations tab, and the values of input parameters from this pass will be inserted in the Parameters tab, as well as a single test will be run. The found parameters can be saved to a set file. However, there is no need to provide it separately. Save the set of parameters under a clear name, such as EURUSD.set. It means that the parameters should be applied for this pair and not GBPJPY.
Repeat this operation for each symbol which your EA can trade. Thus, you have a number of ready set files, say 9. Add these files to your project. Create the appropriate folder "Settings and files\Set", to separate them from source files. With Projects, you can maintain order and the correct file structure.
Now, compile the project and open the strategy tester with the MeanReversion EA. A new item "Load from EA" will appear in the context menu, on the Inputs tab. All available set files can be accessed from this menu.
Thus, the compiled EX5 file of the Expert Advisor is a fully completed product, with ready sets of parameters. The strategy can be instantly tested without having to set borders and steps for each of the desired symbols. Users and buyers of your trading robots will definitely appreciate this convenience.
Strategy running on real data
In September 2019, the MeanReversion Expert Advisor was launched on a demo account. The purpose was to find out programming and trading errors in real time. The EA was launched in a portfolio mode on multiple symbols (this was the initial idea during the optimization). A built-in VPS was rented for the EA, based on which a private signal Many MeanReversion Optimized was created for monitoring purposes.
The first month after the launch the EA showed positive results. This was followed by consecutive 5 losing months. The virtual hosting was rented with the automated renewal feature, and thus the EA was running in a fully autonomous mode. It kept trading towards a complete deposit loss. Then, in March, something changed in the forex market and the EA suddenly generated a record profit. During the next 2 months, the results were contradictory. The same growth can probably never be repeated again.
The analysis of deals and results by symbols shows that loss was made by three yen pairs and AUDUSD. The Expert Adviser did not show impressive results. Nevertheless, even with such simple trading logic, it has been running for 9 months in a portfolio mode, due to which losses on some symbols are covered by profit on other pairs.
The Expert Advisor parameters have never been modified since its launch, no additional migrations were performed during this time. The EA was compiled 9 months ago and was launched on eight charts, on a built-in VPS. It is still running without any human interference. We cannot even remember why only eight out of nine set files were launched. Moreover, we cannot remember the parameters used. Nevertheless, the MeanReversion Expert Advisor project created for educational purposes is still running and is showing profit as of June 10, 2020.
Switch to Projects and enjoy the benefits
Projects allow developers to create programs of any complexity level, as well as to collaborate during development. When working together with like-minded people, you can develop applications faster, exchange useful ideas and skills, as well as improve the quality of the code.
The trading rules utilized within this Expert Advisor are very simple, but it can be used as a template for creating many other trading robots. Replace functions determining the trend direction, the flat state, or the entry levels and methods (for example, you may use market orders instead of limit ones). Perhaps, better results might be obtained if trading only during flat periods. Also, the EA lacks trailing stop, Stop Loss and TakeProfit settings. There is much more you can do to improve the EA.
Using the MeanReversion EA from the MetaTrader 5 standard package, you can study and evaluate the advantages of projects. Create your own project or copy this one into a new folder and start experimenting. Start using Projects and evaluate the convenience for yourself!
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/7863
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
This Signal is still working