Download MetaTrader 5

Limitations and Verifications in Expert Advisors

3 August 2010, 18:50
MetaQuotes Software Corp.
2
6 334

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.

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.

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.

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.

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 Software Corp.
Original article: https://www.mql5.com/ru/articles/22

Last comments | Go to discussion (2)
Rashid Umarov
Rashid Umarov | 18 Aug 2010 at 10:16

Due to changes in MQL5, now the maximal overall volume allowed for one symbol can be obtained as following:

//--- get symbol limitation for volume
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);

Do not use the old variant! It was like this:

//--- get symbol limitation for volume
   double max_volume=AccountInfoDouble(ACCOUNT_LIMIT_VOLUME);
The article has been corrected and the new Check_Order_And_Volume_Limits.mq5 expert code has been attached to it.

Jinsong Zhang
Jinsong Zhang | 18 Aug 2010 at 10:40

(build 306)

void OnStart()
{
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
}

 

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

Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.

How to create an indicator of non-standard charts for MetaTrader Market How to create an indicator of non-standard charts for MetaTrader Market

Through offline charts, programming in MQL4, and reasonable willingness, you can get a variety of chart types: "Point & Figure", "Renko", "Kagi", "Range bars", equivolume charts, etc. In this article, we will show how this can be achieved without using DLL, and therefore such "two-for-one" indicators can be published and purchased from the Market.