A Pause between Trades

Andrey Khatimlianskii | 8 August, 2006


1. A Must or a Goodwill?

In the МetaТrader 3 Client Terminal, it was impossible to perform 2 trades at an interval under 10 seconds. Developing its МТ4, the MetaQuotes Software Corporation met the wishes of traders to remove this limitation. Indeed, there are situations where it is quite acceptable to perform a number of trades one after another (moving of StopLoss levels for several positions, removing of pending orders, etc.). But some traders took it in a wrong way and set about writing "killer" experts that could open positions one-by-one without a break. This resulted in blocked accounts or, at least, in the broker's unfriendly attitude.

This article is not for the above writers. It is for those who would like to make their trading comfortable for both themselves and their broker.


2. One Expert or Several Experts: What Is the Difference?

If you have only one terminal launched and only one expert working on it, it is simplicity itself to arrange a pause between trades: it is sufficient to create a global variable (a variable declared at global level, not to be confused with the terminal's Global Variables) and store the time for the last operation in it. And, of course, you should check before performing of each trade operation, whether the time elapsed after the last attempt to trade is sufficient.

This will look like this:

datetime LastTradeTime = 0;
int start()
 {
  // check whether we should enter the market
  ...
  // calculate the levels of StopLoss and TakeProfit and the lot size
  ...
  // check whether sufficient time has elapsed after the last trade
  if(LocalTime() - LastTradeTime < 10)
   {
    Comment("Less than 10 seconds have elapsed after the last trade!",
            " The expert won't trade!"); 
    return(-1);
   }
 
  // open a position
  if(OrderSend(...) < 0)
   {
    Alert( "Error opening position # ", GetLastError() );
    return(-2);
   }
    
  // memorize the time of the last trade
  LastTradeTime = LocalTime();
 
  return(0);
 }

This example is suitable for one expert working on one terminal. If one or several more experts are launched simultaneously, they will not keep the 10-second pause. They will not just be aware of when another expert was trading. Every expert will have its own, local LastTradeTime variable. The way out of this is obvious: You should create a Global Variable and store the time of the trade in it. Here we mean the terminal's Global Variable, all experts will be able to access to it.


3. The _PauseBeforeTrade() Function

Since the code realizing the pause will be the same for all experts, it will be more reasonable to arrange it as a function. This will provide maximum usability and minimum volume of the expert code.

Prior to writing the code, let as define our task more concretely - this will save our time and efforts. Thus, this is what the function must do:

If the function detects that not enough time has elapsed after the last trade, it must wait. The Sleep() function will provide both waiting and checking of the IsStopped(). I.e., if you delete the expert from the chart during it "sleeps", it will not hang up or be stopped forcedly.

But, for more self-descriptiveness, let us (every second during the "sleep") display information about how long it still remains to wait.

This is what we have to get as a result:

extern int PauseBeforeTrade = 10; // pause between trades (in seconds)
 
/////////////////////////////////////////////////////////////////////////////////
// int _PauseBeforeTrade()
//
// The function sets the local time value for the global variable LastTradeTime.
// If the local time value at the moment of launch is less than LastTradeTime + 
// PauseBeforeTrade value, the function will wait.
// If there is no global variable LastTradeTime at all, the function will create it.
// Return codes:
//  1 - successful completion
// -1 - the expert was interrupted by the user (the expert was deleted from the chart, 
//      the terminal was closed, the chart symbol or period was changed, etc.)
/////////////////////////////////////////////////////////////////////////////////
int _PauseBeforeTrade()
 {
  // there is no reason to keep the pause during testing - just terminate the function
  if(IsTesting()) 
    return(1); 
  int _GetLastError = 0;
  int _LastTradeTime, RealPauseBeforeTrade;
 
  //+------------------------------------------------------------------+
  //| Check whether the global variable exists and, if not, create it  |
  //+------------------------------------------------------------------+
  while(true)
   {
    // if the expert was interrupted by the user, stop working
    if(IsStopped()) 
     { 
      Print("The expert was stopped by the user!"); 
      return(-1); 
     }
    // check whether a global variable exists
    // if yes, exit this loop
    if(GlobalVariableCheck("LastTradeTime")) 
      break;
    else
     // if the GlobalVariableCheck returns FALSE, it means that either global variable does not exist, or 
     // an error occurred during checking
     {
      _GetLastError = GetLastError();
      // if it is still an error, display information, wait during 0.1 sec, and start to 
      // recheck
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableCheck(\"LastTradeTime\")-Error #",
              _GetLastError );
        Sleep(100);
        continue;
       }
     }
    // if no error occurs, it just means that there is no global variable, try to create it
    // if GlobalVariableSet > 0, it means that the global variable has been successfully created. 
    // Exit the function
    if(GlobalVariableSet("LastTradeTime", LocalTime() ) > 0) 
      return(1);
    else
     // if GlobalVariableSet returns a value <= 0, it means that an error occurred during creation 
     // of the variable
     {
      _GetLastError = GetLastError();
      // display information, wait 0.1 sec, and restart the attempt 
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableSet(\"LastTradeTime\", ", 
              LocalTime(), ") - Error #", _GetLastError );
        Sleep(100);
        continue;
       }
     }
   }
  //+--------------------------------------------------------------------------------+
  //| If the function performance has reached this point, it means that the global   |
  //| variable exists.                                                                    |
  //| Wait until LocalTime() becomes > LastTradeTime + PauseBeforeTrade               |
  //+--------------------------------------------------------------------------------+
  while(true)
   {
    // if the expert was stopped by the user, stop working
    if(IsStopped()) 
     { 
      Print("The expert was stopped by the user!"); 
      return(-1); 
     }
    // get the value of the global variable
    _LastTradeTime = GlobalVariableGet("LastTradeTime");
    // if an error occurs at this, display information, wait 0.1 sec, and try 
    // again
    _GetLastError = GetLastError();
    if(_GetLastError != 0)
     {
      Print("_PauseBeforeTrade()-GlobalVariableGet(\"LastTradeTime\")-Error #", 
            _GetLastError );
      continue;
     }
    // count how many seconds have been elapsed since the last trade
    RealPauseBeforeTrade = LocalTime() - _LastTradeTime;
    // if less than PauseBeforeTrade seconds have elapsed,
    if(RealPauseBeforeTrade < PauseBeforeTrade)
     {
      // display information, wait one second, and check again
      Comment("Pause between trades. Remaining time: ", 
               PauseBeforeTrade - RealPauseBeforeTrade, " sec" );
      Sleep(1000);
      continue;
     }
    // if the time elapsed exceeds PauseBeforeTrade seconds, stop the loop
    else
      break;
   }
  //+--------------------------------------------------------------------------------+
  //| If the function performance has reached this point, it means that the global   |
  //| variable exists and the local time exceeds LastTradeTime + PauseBeforeTrade    |
  //| Set the local time value for the global variable LastTradeTime                 |
  //+--------------------------------------------------------------------------------+
  while(true)
   {
    // if the expert was stopped by the user, stop working
    if(IsStopped()) 
     { 
      Print("The expert was stopped by the user!"); 
      return(-1);
     }

    // Set the local time value for the global variable LastTradeTime.
    // In case it succeeds - exit
    if(GlobalVariableSet( "LastTradeTime", LocalTime() ) > 0) 
     { 
      Comment(""); 
      return(1); 
     }
    else
    // if the GlobalVariableSet returns a value <= 0, it means that an error occurred
     {
      _GetLastError = GetLastError();
      // display the information, wait 0.1 sec, and restart attempt
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableSet(\"LastTradeTime\", ", 
              LocalTime(), " ) - Error #", _GetLastError );
        Sleep(100);
        continue;
       }
     }
   }
 }


4. Integration into Experts and How to Use It

To check operability of the function, we created a diagnostic expert that had to trade keeping pauses between trades. The _PauseBeforeTrade() function was preliminarily placed into the PauseBeforeTrade.mq4 file included into the expert using the #include directive.

Attention! This expert is only dedicated for checking the function operability! It may not be used for trading!

#include <PauseBeforeTrade.mq4>
 
int ticket = 0;
int start()
 {
  // if there is no position opened by this expert
  if(ticket <= 0)
   {
    // keep a pause between trades, if an error has occurred, exit
    if(_PauseBeforeTrade() < 0) 
      return(-1);
    // update the market information
    RefreshRates();
 
    // and try to open a position
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "PauseTest", 123, 0, 
                       Lime);
    if(ticket < 0)
      Alert("Error OrderSend № ", GetLastError());
   }
  // if there is a position opened by this expert
  else
   {
    // keep the pause between trades (if an error has occurred, exit)
    if(_PauseBeforeTrade() < 0)
      return(-1);
    // update the market information
    RefreshRates();


 
    // and try to close the position
    if (!OrderClose( ticket, 0.1, Bid, 5, Lime ))
      Alert("Error OrderClose № ", GetLastError());
    else
      ticket = 0;
   }
  return(0);
 }

After that, one expert was attached to the EURUSD-M1 chart and another one, absolutely identical, to the GBPUSD-M1 chart. The result did not keep one waiting long: Both experts started trading keeping the prescribed 10-second pause between trades:





5. Possible Problems

When several experts work with one global variable, error can occur. To avoid this, we must delimit access to the variable. A working algorithm of such "delimitation" is described in details in the article named "Error 146 ("Trade context busy") and How to Deal with It". It is this algorithm that we will use.

The final version of the expert will look like this:

#include <PauseBeforeTrade.mq4>
#include <TradeContext.mq4>
 
int ticket = 0;
int start()
 {
  // if there is no a position opened by this expert
  if(ticket <= 0)
   {
    // wait until the trade is not busy and occupy it (if an error has occurred, 
    // exit)
    if(TradeIsBusy() < 0)
      return(-1);
    // keep the pause between trades
    if(_PauseBeforeTrade() < 0)
     {
      // if an error has occurred, free the trade and exit
      TradeIsNotBusy();
      return(-1);
     }
    // update the market information
    RefreshRates();
 
    // and try to open a position
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "PauseTest", 123, 0, 
                       Lime);
    if (ticket < 0)
      Alert("Error OrderSend № ", GetLastError());
    // free the trade
    TradeIsNotBusy();
   }
  // if there is a position opened by this expert
  else
   {
    // wait until the trade is not busy and occupy it (if an error has occurred, 
    // exit)
    if(TradeIsBusy() < 0)
      return(-1);
    // keep the pause between trades
    if(_PauseBeforeTrade() < 0)
     {
      // if an error occurs, free the trade and exit
      TradeIsNotBusy();
      return(-1);
     }
    // update the market information
    RefreshRates();
 
    // and try to close the position
    if(!OrderClose( ticket, 0.1, Bid, 5, Lime))
      Alert("Error OrderClose № ", GetLastError());
    else
      ticket = 0;
 
    // free the trade
    TradeIsNotBusy();
   }
  return(0);
 }