Русский 中文 Español Deutsch 日本語 Português
To Sleep, or Not to Sleep?

To Sleep, or Not to Sleep?

MetaTrader 4Examples | 10 December 2008, 14:58
20 038 4
Sergey Gridnev
Sergey Gridnev

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 Ltd.
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.

[Deleted] | 18 May 2009 at 11:32

Very useful! Thank You! Thank You!

[Deleted] | 6 May 2010 at 02:10
Awesome stuff, thanks.

Best Forex Robot


Forex Robot
[Deleted] | 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... :)
Visual Testing of the Profitability of Indicators and Alerts Visual Testing of the Profitability of Indicators and Alerts
What indicator of trading alerts or just the methods of their calculating to use is usually decided when testing EAs using these alerts. However, it is not always possible/necessary/reasonable to write an EA for each indicator. You can promptly calculate the profitability of trading on the alerts from other indicators, using a special indicator that collects their alerts itself and draws a picture of ideal trading with them. It can help you both make a visual estimate of the results obtained and quickly choose most optimal parameters.
View of Technical Analysis in the Context of Automatic Control Systems (ACS), or "Reverse View" View of Technical Analysis in the Context of Automatic Control Systems (ACS), or "Reverse View"
The article demonstrates an alternative view of technical analysis, which is based on the principles of both the modern automatic control theory and technical analysis itself. It is an introductory article representing the theory with some practical applications of it.
Program Folder of MetaTrader 4 Client Terminal Program Folder of MetaTrader 4 Client Terminal
The article describes the contents of the program folder of MetaTrader 4 Client Terminal. The article will be useful for those who have already started to grasp into the details of the client terminal operation.
Idleness is the Stimulus to Progress. Semiautomatic Marking a Template Idleness is the Stimulus to Progress. Semiautomatic Marking a Template
Among the dozens of examples of how to work with charts, there is a method of manual marking a template. Trend lines, channels, support/resistance levels, etc. are imposed in a chart. Surely, there are some special programs for this kind of work. Everyone decides on his/her own which method to use. In this article, I offer you for your consideration the methods of manual marking with subsequent automating some elements of the repeated routine actions.