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 ( )
Sleep(10000);
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 ( ... )
_time_waiting = TimeLocal() + 10;
while ( TimeLocal() < _time_waiting )
{
}
Alternative 2 (using the cycle of function start() calls).
static int _time_waiting=0;
if ( ... )
_time_waiting = TimeLocal() + 10;
if ( TimeLocal() >= _time_waiting )
{
}
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"
void timer_Init();
int timer_NumberTotal();
int timer_NumberUsed();
int timer_NumberFree();
void timer_ResetAll();
bool timer_Reset(int CounterID);
ted
void timer_DeleteAll();
bool timer_Delete(int CounterID);
bool timer_StartWaitable(int CounterID, int timeslice);
bool timer_StartStopwatch(int CounterID);
int timer_GetCounter(int CounterID);
is returned
int timer_GetType(int CounterID);
int timer_GetCounterIDbyPOS(int CounterPOS);
#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"
extern bool Use_Sleep = false;
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);
}
int deinit()
{
timer_DeleteAll();
Comment("");
return(0);
}
int start()
{
Comment("Waitable:",timer_GetCounter(101)," / Stopwatch:",timer_GetCounter(102));
Print("start() - initial block -");
if ( Use_Sleep )
{
Sleep( 10000 );
Print("start() - block to be executed after the pause -");
}
else
{
if ( timer_GetType(101) == 0 )
timer_StartWaitable(101, 10);
if ( timer_GetCounter(101) == 0 )
{
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: http://articles.mql4.com/ru/742