![Limitations and Verifications in Expert Advisors](https://c.mql5.com/i/articles/overlay.png)
Limitations and Verifications in Expert Advisors
Introduction
When creating an algorithm for automated trading you should be able not only to process the prices for making trade signals, but to be able to get a lot of auxiliary information about limitations imposed on the operation of Expert Advisors. This article will tell you how to:
- Get information about trading sessions;
- Check if you have enough assets to open a position;
- Impose a limitation on the total trading volume by a symbol;
- Impose a limitation on the total number of orders;
- Calculate the potential loss between the entry price and Stop Loss;
- Check if there is a new bar.
Trading and Quotation Sessions
To receive the information about trading sessions, you should use the SymbolInfoSessionTrade() function, for quotation sessions use the corresponding SymbolInfoSessionQuote() function. Both functions work in the same way: if there is a session with the specified index for the specified day of week (the indexation of sessions starts from zero), then the function returns true. The time of start and end of a session is written to the fourth and fifth parameters passed by the link.
//--- check if there is a quotation session with the number session_index bool session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish);To find out all the session of the specified day, call this function in a loop until it returns false.
//+------------------------------------------------------------------+ //| Display information about quotation sessions | //+------------------------------------------------------------------+ void PrintInfoForQuoteSessions(string symbol,ENUM_DAY_OF_WEEK day) { //--- start and end of session datetime start,finish; uint session_index=0; bool session_exist=true; //--- go over all sessions of this day while(session_exist) { //--- check if there is a quotation session with the number session_index session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish); //--- if there is such session if(session_exist) { //--- display the day of week, the session number and the time of start and end Print(DayToString(day),": session index=",session_index," start=", TimeToString(start,TIME_MINUTES)," finish=",TimeToString(finish-1,TIME_MINUTES|TIME_SECONDS)); } //--- increase the counter of sessions session_index++; } }
The day of week is displayed in the string format using the custom function DayToString() that receives the value of the ENUM_DAY_OF_WEEK enumeration as the parameter.
//+------------------------------------------------------------------+ //| Receive the string representation of a day of week | //+------------------------------------------------------------------+ string DayToString(ENUM_DAY_OF_WEEK day) { switch(day) { case SUNDAY: return "Sunday"; case MONDAY: return "Monday"; case TUESDAY: return "Tuesday"; case WEDNESDAY: return "Wednesday"; case THURSDAY: return "Thursday"; case FRIDAY: return "Friday"; case SATURDAY: return "Saturday"; default: return "Unknown day of week"; } return ""; }
The final code of the SymbolInfoSession.mq5 script is attached to the bottom of the article. Let's show here its main part only.
void OnStart() { //--- the array where the days of week are stored ENUM_DAY_OF_WEEK days[]={SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY}; int size=ArraySize(days); //--- Print("Quotation sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForQuoteSessions(Symbol(),days[d]); } //--- Print("Trading sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForTradeSessions(Symbol(),days[d]); } }
Checking Margin
To find out the amount of margin required for opening or increasing a position, you can use the OrderCalcMargin() function; the first parameter that is passed to it is a value from the ENUM_ORDER_TYPE enumeration. For a buy operation you should call it with the ORDER_TYPE_BUY parameter; to sell, use ORDER_TYPE_SELL parameter. The function returns the amount of margin depending on the number of lots and the open price.
void OnStart() { //--- the variable to receive the value of margin double margin; //--- to receive information about the last tick MqlTick last_tick; //--- try to receive the value from the last tick if(SymbolInfoTick(Symbol(),last_tick)) { //--- reset the last error code ResetLastError(); //--- calculate margin value bool check=OrderCalcMargin(type,Symbol(),lots,last_tick.ask,margin); if(check) { PrintFormat("For the operation %s %s %.2f lot at %G required margin is %.2f %s",OrderTypeToString(type), Symbol(),lots,last_tick.ask,margin,AccountInfoString(ACCOUNT_CURRENCY)); } } else { Print("Unsuccessful execution of the SymbolInfoTick() function, error ",GetLastError()); } }
It should be noted that the OrderCalcMargin() function allows to calculate the value of margin not only for the market orders, but for pending orders as well. You can check the values that are returned for all the types of orders using the Check_Money.mq5 script.
The OrderCalcMargin() function is intended for calculation of the size of margin for pending orders, since a money backing may be required in some trading systems for pending orders as well. Usually, the margin size for pending orders is calculated through a coefficient to the size of margin for the long and short positions.
Identifier |
Description |
Type of Property |
SYMBOL_MARGIN_LONG |
Rate of margin charging on long positions |
double |
SYMBOL_MARGIN_SHORT |
Rate of margin charging on short positions |
double |
SYMBOL_MARGIN_LIMIT |
Rate of margin charging on Limit orders |
double |
SYMBOL_MARGIN_STOP |
Rate of margin charging on Stop order |
double |
SYMBOL_MARGIN_STOPLIMIT |
Rate of margin charging on Stop Limit orders |
double |
You can get the values of those coefficients using the simple code:
//--- Calculate the rates of margin charging for different types of orders PrintFormat("Rate of margin charging on long positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LONG)); PrintFormat("Rate of margin charging on short positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_SHORT)); PrintFormat("Rate of margin charging on Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LIMIT)); PrintFormat("Rate of margin charging on Stop orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOP)); PrintFormat("Rate of margin charging on Stop Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOPLIMIT));
For Forex symbols, the rates of margin charging for pending orders are usually equal to 0, i.e. there are no margin requirements for the them.
The results of execution of the Check_Money.mq5 script.
Depending on the way of margin charging, the system of money management may change, as well as the trading system itself may experience some limitations if a margin is required for pending orders. That is why these parameters may also be natural limitations of an Expert Advisor's operation.
Accounting Possible Profits and Losses
When placing a protecting stop level, you should be ready to its triggering. The risk of potential loss should be taken into account in terms of money; and the OrderCalcProfit() is intended for this purpose. It is very similar to the already considered OrderCalcMargin() function, but it requires both open and close prices for calculations.
Specify one of two values of the ENUM_ORDER_TYPE enumeration as the first parameter - ORDER_TYPE_BUY or ORDER_TYPE_SELL; other types of orders will lead to an error. In the last parameter, you should pass a variable using the reference, to which the OrderCalcProfit() function will write the value of profit/loss in case of successful execution.
The example of using the CalculateProfitOneLot() function that calculates the profit or loss, when closing a long position with specified levels of entering and exiting:
//+------------------------------------------------------------------+ //| Calculate potential profit/loss for buying 1 lot | //+------------------------------------------------------------------+ double CalculateProfitOneLot(double entry_price,double exit_price) { //--- receive the value of profit to this variable double profit=0; if(!OrderCalcProfit(ORDER_TYPE_BUY,Symbol(),1.0,entry_price,exit_price,profit)) { Print(__FUNCTION__," Failed to calculate OrderCalcProfit(). Error ",GetLastError()); } //--- return(profit); }
The result of calculation of this function is shown in the figure.
The example of calculation and displaying the potential loss on the chart using the OrderCalcProfit() function.
The whole code can be found in the attached Expert Advisor CalculateProfit_EA.mq5.
Checking If There Is a New Bar
Development of many trading system assumes that the trade signals are calculated only when a new bar appears; and all the trade actions are performed only once. The "Only open prices" mode of the strategy tester in the MetaTrader 5 client terminal is good for checking such automated trading systems.
In the "Open prices only" mode, all the calculations of indicators and the call of the OnTick() function in Expert Advisor are performed only once per bar while testing. It is the fastest mode of trading; and, as a rule, the most fault-tolerant to insignificant price oscillations way of creating trading systems. At the same time, of course, indicators used in an Expert Advisor should be written correctly and shouldn't distort their values when a new bar comes.
The strategy tester in "Open prices only" mode allows to relieve you from caring about the Expert Advisor to be launched only once per bar; and it is very convenient. But while working in the real time mode on a demo or on a real account, a trader should control the activity of their Expert Advisor, to make it perform only one trade operation per one received signal. The easiest way for this purpose is tracking of opening of the current unformed bar.
To get the time of opening of the last bar, you should use the SeriesInfoInteger() function with specified name of symbol, timeframe and the SERIES_LASTBAR_DATE property. By constant comparing of the time of opening of the current bar with the one of the bar stored in a variable, you can easily detect the moment when a new bar appears. It allows to create the custom function isNewBar() that can look as following:
//+------------------------------------------------------------------+ //| Return true if a new bar appears for the symbol/period pair | //+------------------------------------------------------------------+ bool isNewBar() { //--- remember the time of opening of the last bar in the static variable static datetime last_time=0; //--- current time datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if it is the first call of the function if(last_time==0) { //--- set time and exit last_time=lastbar_time; return(false); } //--- if the time is different if(last_time!=lastbar_time) { //--- memorize time and return true last_time=lastbar_time; return(true); } //--- if we pass to this line then the bar is not new, return false return(false); }
An example of using the function is given in the attached Expert Advisor CheckLastBar.mq5.
The messages of the CheckLastBar Expert Advisor about appearing of new bars on M1 timeframe.
Limiting Number of Pending Orders
If you need to limit the number of active pending orders that can be simultaneously placed at an account, you can write your own custom function. Let's name it IsNewOrderAllowed(); it will check if it is allowed to place another pending order. Let's write it to comply with the rules of the Automated Trading Championship.
//+------------------------------------------------------------------+ //| Checks if it is allowed to place another order | //+------------------------------------------------------------------+ bool IsNewOrderAllowed() { //--- get the allowed number of pending orders on an account int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS); //--- if there is no limitations, return true; you can send an order if(max_allowed_orders==0) return(true); //--- if we pass to this line, then there are limitations; detect how many orders are already active int orders=OrdersTotal(); //--- return the result of comparing return(orders<max_allowed_orders); }
The function is simple: get the allowed number of orders to the max_allowed_orders variable; and if its value is not equal to zero, compare with the current number of orders. However, this function doesn't consider another possible limitation - the limitation on the allowed total volume of open positions and pending orders by a specific symbol.
Limiting Number of Lots by a Specific Symbol
To get the size of open position by a specific symbol, first of all, you need to select a position using the PositionSelect() function. And only after that, you can request the volume of the open position using the PositionGetDouble(); it returns various properties of the selected position that have the double type. Let's write the PostionVolume() function to get the position volume by a given symbol.
//+------------------------------------------------------------------+ //| Returns the size of position by a specific symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a positions by a symbol bool selected=PositionSelect(symbol); //--- the position exists if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- report about the unsuccessful attempt to select the position Print(__FUNCTION__," Failed to execute PositionSelect() for the symbol ", symbol," Error ",GetLastError()); return(-1); } }
Before making a trade request for placing a pending order by a symbol, you should check the limitation on the total volume of open position and pending orders by that symbol - SYMBOL_VOLUME_LIMIT. If there is no limitation, then the volume of a pending order cannot exceed the maximum allowed volume that can be received using the SymbolInfoDouble() volume.
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
However, this approach doesn't consider the volume of current pending orders by the specified symbol. Let's write a function that calculates this value:
//+------------------------------------------------------------------+ //| Returns the size of position by a specified symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a position by a symbol bool selected=PositionSelect(symbol); //--- the position exist if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- return zero if there is no position return(0); } }
With the consideration of the volume of open position and the volume of pending orders, the final checking will look as following:
//+------------------------------------------------------------------+ //| Returns maximum allowed volume for an order by a symbol | //+------------------------------------------------------------------+ double NewOrderAllowedVolume(string symbol) { double allowed_volume=0; //--- get the limitation on the maximum volume of an order double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); //--- get the limitation of volume by a symbol double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); //--- get the volume of open position by a symbol double opened_volume=PositionVolume(symbol); if(opened_volume>=0) { //--- if we already used available volume if(max_volume-opened_volume<=0) return(0); //--- volume of the open position doen't exceed max_volume double orders_volume_on_symbol=PendingsVolume(symbol); allowed_volume=max_volume-opened_volume-orders_volume_on_symbol; if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume; } return(allowed_volume); }
The whole code of the Check_Order_And_Volume_Limits.mq5 Expert Advisor that contains the functions, mentioned in this section, is attached to the article.
The example of checking using the Check_Order_And_Volume_Limits Expert Advisor on the account of a participant of the Automated Trading Championship 2010.
Checking the Correctness of Volume
A significant part of any trading robot is the ability to choose a correct volume for performing a trade operation. Here, we are not going to talk about the systems of money management and risk management, but about the volume to be correct according to the corresponding properties of a symbol.
Identifier |
Description |
Type of Property |
SYMBOL_VOLUME_MIN |
Minimal volume for a deal |
double |
SYMBOL_VOLUME_MAX |
Maximal volume for a deal |
double |
SYMBOL_VOLUME_STEP |
Minimal volume change step for deal execution |
double |
To perform such verification, we can write the custom function CheckVolumeValue():
//+------------------------------------------------------------------+ //| Check the correctness of volume of an order | //+------------------------------------------------------------------+ bool CheckVolumeValue(double volume,string &description) { //--- Minimum allowed volume for trade operations double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); if(volume<min_volume) { description=StringFormat("Volume is less than the minimum allowed SYMBOL_VOLUME_MIN=%.2f",min_volume); return(false); } //--- Maximum allowed volume for trade opertations double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); if(volume>max_volume) { description=StringFormat("Volume is greater than the maximum allowed SYMBOL_VOLUME_MAX=%.2f",max_volume); return(false); } //--- get the minimal volume change step double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP); int ratio=(int)MathRound(volume/volume_step); if(MathAbs(ratio*volume_step-volume)>0.0000001) { description=StringFormat("Volume is not a multiple of minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f", volume_step,ratio*volume_step); return(false); } description="Correct value of volume "; return(true); }
You can check the working of this function using the CheckVolumeValue.mq5 script attached to the article.
The messages of the CheckVolumeValue.mq5 that checks the volume to be correct.
Conclusion
The article describes the basic verifications for possible limitations on working of an Expert Advisor, that can be faced when creating your own automated trading system. These examples don't cover all the possible conditions that should be checked during the operation of an Expert Advisor on a trade account. But I hope, these examples will help newbies to understand how to implement the most popular verifications in the MQL5 language.
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/22
![MetaTrader 5 and MATLAB Interaction](https://c.mql5.com/2/0/matlab.png)
![Creating Multi-Colored Indicators in MQL5](https://c.mql5.com/2/0/paint_indicator_MQL5__1.png)
![How to Create Your Own Trailing Stop](https://c.mql5.com/2/0/Trailing_Stop_MQL5.png)
![Creating and Publishing of Trade Reports and SMS Notification](https://c.mql5.com/2/0/trade_reports_SMS_MQL5.png)
![MQL5 - Language of trade strategies built-in the MetaTrader 5 client terminal](https://c.mql5.com/i/registerlandings/logo-2.png)
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Due to changes in MQL5, now the maximal overall volume allowed for one symbol can be obtained as following:
Do not use the old variant! It was like this:
The article has been corrected and the new Check_Order_And_Volume_Limits.mq5 expert code has been attached to it.(build 306)
compile errors:
'SYMBOL_VOLUME_LIMIT' - undeclared identifier test.mq5 4 46
'SymbolInfoDouble' - no one of the overloads can be applied to the function call test.mq5 4 20
Due to changes in MQL5, now the maximal overall volume allowed for one symbol can be obtained as following:
Do not use the old variant! It was like this:
The article has been corrected and the new Check_Order_And_Volume_Limits.mq5 expert code has been attached to it.Dear Admin
I try to use this function but it will return 0 in all cases.
My MT5 build is 2280
double orders_volume_on_symbol=PendingsVolume(symbol);
gives error, no such function
And after I modify function to not include pending orders, it gives 0
It is because max_volume is 0 and opened_volume is 0;
Why max volume is 0 if there is no opened positions?
Build 2715