Download MetaTrader 5

To Sleep, or Not to Sleep?

10 December 2008, 14:58
Sergey Gridnev
4
4 549

Introduction

During its operations, an Expert Advisor sometimes produces situations in which it must make pauses between operations. This may be caused both by the necessity to meet the requirement of holding a certain interval between repeated requests to the trade server (in case of execution errors) and by waiting for a certain event (for example, for reconnection to the server, for free trade context, etc.).

To Sleep(), or Not to Sleep()?

For realization of pauses, MQL4 has a function named Sleep() that takes as a parameter the value of the time interval expressed in the amount of milliseconds. The Sleep() function stops the execution of the program code and allows its continuation only after the given time interval has passed.

In my opinion, the use of this function has two disadvantages. First, machine time is used impractically: during the pause in the actions of one type, the program could perform the actions of another type independent on the former one (for example, during a pause in trades, the program could perform some calculations, monitor arriving ticks, etc.). Second, which is more essential, function Sleep() cannot be called from custom indicators (see Documentation). However, a programming language must be used for programming!

Let's consider how a ten-second pause is realized in an MQL4 program. An example using function Sleep() is given below:

if ( /* condition that requires to hold a pause */ )
   Sleep(10000); // sleep for 10 seconds

// program block to be executed upon the expiry of the pause
// ...

As an alternative, let's consider a program code using an additional variable. In case you need to have a pause before performing an action, that pause should be initialized by the local time value of its end. Comparing the local time value with the value of this variable, you can establish the fact of the end of the pause. To enjoy the advantages of using this alternative approach, you should organize for the period of waiting for the end of the pause a cyclic execution of the program code for which there is no need to set a pause. You can do this both by using a cycle (for example, while) and by the corresponding realization of function start() that can be called by cycles according to the tick arriving cycle. The former alternative is suitable for custom scripts, the latter one is for EAs and indicators. Both are given below.

Alternative 1 (using cycle 'while').

int _time_waiting=0;
// ...
if ( ... ) // condition that requires to hold a pause
   _time_waiting = TimeLocal() + 10; // the pause ends in 10 seconds after the current local time
while ( TimeLocal() < _time_waiting )
{ // program block to be executed cyclically while waiting for the end of the pause
   // ...
}
// program block to be executed after the end of the pause
// ...

Alternative 2 (using the cycle of function start() calls).

static int _time_waiting=0;
// ...
if ( ... ) // condition that requires to hold a pause
   _time_waiting = TimeLocal() + 10; // the pause ends in 10 seconds after the current local time
if ( TimeLocal() >= _time_waiting )
{ // program block to be executed after the end of the pause
   // ...
}
// program block to be executed at every tick, not related to waiting for the end of the pause
// ...

The main difference between the represented alternatives is that alternative 1 guarantees the execution of the program block stopped for the period of the pause, whereas alternative 2 doesn't. This is related to the fact that the tick flow can be interrupted for some reasons without restarting. Besides, in alternative 2, variable _time_waiting is described as static, which preserves its value between the start() function calls, which is not necessary in alternative 1.

Some redundancy of the code in the alternatives, as compared to the realization of the pause through the Sleep() function call, is a payment for getting rid of the down time. However, no down time generates the problem of the repeated uses of variable _time_waiting in the program code, because you cannot initialize it with a new value until the preceding pause is expired. This problem can be solved in at least two ways, the choice of one or another alternative depending on the programmer's style and preferences:

1) you can use your own variable for each group of conditions to store the expiry time of the corresponding pause; or

2) you can describe the _time_waiting as an array.

I suppose it to be reasonable to create a small library with the realization of timer functions. This timer must contain a sufficient amount of counter variables and have the functions of initialization, adding and deletion of controlled extracts. Besides, you can realize both countdown (wait timer) and up-counter (stopwatch). An example of how to realize such a library is attached to this article (file named ExLib_Timer.mq4 and containing the source text). A header file containing the prototypes of the realized functions is given below:

#import "ExLib_Timer.ex4"
//
// Note.
// The counter identifier <CounterID> can take any values except "0",
// since "0" is reserved for the designation of nonactivated counters
// in a general array.
//
void timer_Init();
// start initialization
//
int timer_NumberTotal();
// request for the total amount of timer counters
// Return:
//    total amount of counters
//
int timer_NumberUsed();
// request for the amount of activated counters
// Return:
//    amount of activated counters
//
int timer_NumberFree();
// request for the amount of nonactivated (free) counters
// Return:
//    amount of free counters
//
void timer_ResetAll();
// resetting (zeroing) all counters
//
bool timer_Reset(int CounterID);
// resetting (zeroing) a counter by ID
// Parameters:
//    <CounterID> - counter identifier
// Return:
//    true  - resetted
//    false - no counter with such ID is found
//
void timer_DeleteAll();
// deletion of all counters
//
bool timer_Delete(int CounterID);
// deleting a counter by ID
// Parameters:
//    <CounterID> - counter identifier
// Return:
//    true  - deleted
//    false - no counter with such ID is found
//

bool timer_StartWaitable(int CounterID, int timeslice);
// starting a counter of the "wait timer" type
// Parameters:
//    <CounterID> - counter identifier (if it is not available, a counter is added)
//    <timeslice> - wait period (seconds)
// Return:
//    true  - started
//    false - no free counters
//
bool timer_StartStopwatch(int CounterID);
// starting a counter of the "stopwatch" type
// Parameters:
//    <CounterID> - counter identifier (if it is not available, a counter is added)
// Return:
//    true  - started
//    false - no free counters
//
int timer_GetCounter(int CounterID);
// request for the indications of the counter with ID <CounterID>
// Return:
//    for a counter of the "stopwatch" type, it is the amount of seconds elapsed since the start
//    for a counter of the "wait timer" type, it is the amount of seconds left till the end of wait period
// Note:
//    in case of no counter with ID <CounterID>, -1 is returned
//
int timer_GetType(int CounterID);
// request for a counter with ID <CounterID>
// Parameters:
//    <CounterID> - counter identifier
// Return:
//     1 - counter of the "stopwatch" type
//    -1 - counter of the "wait timer" type
//     0 - no counter with such ID is found
//
int timer_GetCounterIDbyPOS(int CounterPOS);
// request for a counter ID by its position among the activated ones
// Parameters:
//    <CounterPOS> - the number of the counter position among the activated ones
// Return:
//    counter identifier or "0" if a nonexisting position is specified
// Note:
//    <CounterPOS> can take the values ranging from 0 up to the amount of the activated counters,
//    returned by function timer_NumberUsed(). Otherwise, it returns 0
//
#import

The differences between using the Sleep() function call and the alternative realization of a pause making entries in a log is clearly demonstrated by a small Expert Advisor the text of which is given below. The alternative pause realization is set by the input parameter of Use_Sleep - "true" to use Sleep(), or by "false" to refuse using Sleep().

#include "include/ExLib_Timer.mqh"
// --
//---- input parameters
extern bool Use_Sleep = false;
// =true  - using function "Sleep()"
// =false - using a timer
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{
   timer_Init();
   if ( Use_Sleep )
      Print("init(). * Using function Sleep() *");
   else
      Print("init(). * Using a timer *");
   timer_StartWaitable(101, 10);
   timer_StartStopwatch(102);
   return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
{
   timer_DeleteAll();
   Comment("");
   return(0);
}
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
   Comment("Waitable:",timer_GetCounter(101)," / Stopwatch:",timer_GetCounter(102));
   Print("start() - initial block -");
// --
   if ( Use_Sleep )
   {
      //  - using function "Sleep()" -
      Sleep( 10000 ); // sleep for 10 seconds
      // program block to be executed after the pause ends
      Print("start() - block to be executed after the pause -");
   }
   else
   {
      //  - using a timer -
      if ( timer_GetType(101) == 0 )
         timer_StartWaitable(101, 10); // sleep for 10 seconds
         
      if ( timer_GetCounter(101) == 0 )
      {
         // program block to be executed after the pause ends
         timer_Delete(101);
         Print("start() - block to be executed after the pause -");
      }
   }
// --
   Print("start() - end block -");
   return(0);
}

Besides the EA given above, there are two more demo EAs attached to this article:
Ex_Timer_OrderLimits_TrailByTime.mq4 demonstrates how to realize trailing SL and TP by time, and
Ex_Timer_OrderSend_wMinTimeLimit.mq4 demonstrates how to place order not oftener than once within the period set on the current chart.

Minor Disadvantages, or "A Spoon of Tar"?

Unfortunately, the given library cannot fully replace Sleep(). There are two downsides here that must be brought to the reader's notice.

First, at using Sleep(), the program execution continues immediately after the pause is over. In case of using a timer, it only continues at the first tick arrived after the pause. This may result in that the delayed block is executed too late, or, as it was mentioned above, is not executed at all (for example, in case of a critical error in the link channel).

Second, function Sleep() allows you to set a pause with a step of 0.1 s, whereas the timer does only 1 s.

Conclusions

As often happens, the advantages of the alternative pause realization described above over the use of Sleep() are not infallible, there are some finer points. Everything has its strengths and weaknesses, this significance depending on the purposes pursued. To Sleep(), or not to Sleep(), what to sacrifice and what to prefer - all this must not be decided in general, but within the framework of the project to be realized.

Attachments:

Function Library:

  • ExLib_Timer.mqh - header file;
  • ExLib_Timer.mq4 - source text.

Examples of use:

  • Ex_Timer_SleepOrNot.mq4 - an Expert Advisor that clearly demonstrates the differences between the alternatives of the pause realization;
  • Ex_Timer_OrderLimits_TrailByTime.mq4 - an EA that demonstrates trailing SL and TP by time;
  • Ex_Timer_OrderSend_wMinTimeLimit.mq4 - an EA that demonstrates placing orders not oftener than once within the period set on the current chart.

Important! The EAs of "Ex_Timer_OrderLimits_TrailByTime" and "Ex_Timer_OrderSend_wMinTimeLimit" are considered for being used on demo accounts only!


Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/1558

Last comments | Go to discussion (4)
Ray
Ray | 13 Dec 2008 at 23:30

Nice little library...

Time Loop without Sleep() gives 100% CPU while processing the waiting time

Sleep() suspends program execution (CPU performs other tasks) until the Sleep() timer is expired.

MQL4 Comments
MQL4 Comments | 18 May 2009 at 11:32

Very useful! Thank You! Thank You!

MQL4 Comments
MQL4 Comments | 6 May 2010 at 02:10
Awesome stuff, thanks.

Best Forex Robot


Forex Robot
MQL4 Comments
MQL4 Comments | 6 May 2011 at 16:36
phy:

Nice little library...

Time Loop without Sleep() gives 100% CPU while processing the waiting time

Sleep() suspends program execution (CPU performs other tasks) until the Sleep() timer is expired.


Yes indeed, so far the best way I've seen to crash you platform! Think before you write... :)
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.