
是睡,还是不睡?
简介
在运行时,Expert Advisor 有时会出现在运行中间必须暂停的情形。 产生这种情况的原因可能是为了符合向交易服务器重复请求必须保持一定间隔的要求(在出现执行错误时)以及等待特定的事件(例如,重新连接服务器或自由的交易环境等)。
用 Sleep(),还是不用 Sleep()?
为了实现暂停,MQL4 有一个名为 Sleep() 的函数,它将以毫秒数表示的时间间隔值作为参数。 Sleep() 函数停止执行程序代码,只有在超过给定的时间间隔后才允许继续执行。
在我看来,该函数的使用有两个缺点。 第一,机器时间的利用不够实际:在一种类型的操作暂停时,程序可以执行跟上一个类型无关的其他类型的操作(例如,在交易暂停时,程序可以进行一些计算、监视即将产生的价格变动等)。 第二,也是更关键的一点,Sleep() 函数无法从自定义指标调用(请查看文档)。 但是,编程必然需要一种编程语言。
我们来探讨如何在一个 MQL4 程序中实现十秒钟的暂停。 下面给出了一个使用 Sleep() 函数的示例:
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 // ...
作为替代,我们考虑一个使用附加变量的程序代码。 如果你在执行操作前需要暂停,则暂停需要在其结束的局部时间值之前进行初始化。 将局部时间值跟该变量的值进行对比,你可以确认暂停结束的事实。 为了利用该替代方法的优点,你应该在等待暂停结束期间对程序代码循环执行,因为没有必要对它设置暂停。 你可以使用循环(例如,while)和 start() 函数的相应实现来完成。start() 函数可以根据价格变动出现的周期由循环调用。 前面的替代方法适用于自定义脚本,后者适用于 EA 和指标。 下面给出了两种方法的代码。
替代方法 1(使用 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 // ...
替代方法 2(使用 start() 函数调用的循环)。
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 // ...
这两种替代方法的主要区别是:替代方法 1 确保程序块在暂停期间停止执行,但替代方法 2 则不然。 这跟实际情况有关,价格变动流可能在没有重启的情况下出于某些原因被中断。 此外,在替代方法 2 中,_time_waiting 变量被描述为静态,在 start() 函数调用之间维持它的值,而在替代方法 1 中则不需要。
跟通过 Sleep() 函数调用实现暂停的方法相比,替代方法中的部分代码冗余是消除停机时间的代价。 但是,没有停机时间会产生一个问题,即在程序代码中反复使用 _time_waiting 变量,因为你无法在上一个暂停结束前用新值将其初始化。 这个问题可以用至少两种方法解决,选择哪种方法将取决于程序员的风格和偏好:
1) 你可以为每组条件使用自己的变量来存储相应暂停的到期时间;或者
2) 你可以将 _time_waiting 描述为数组。
我认为可以创建一个实现定时器功能的小型库。 该定时器必须包含足够数量的计数器变量并具有初始化、添加和删除受控提取的功能。 此外,你可以实现倒计时(等待定时器)和升计数器(秒表)。 本文随附了一个实现这种库的示例(文件名为 ExLib_Timer.mq4,包含源文本)。 下面给出了一个包含已实现功能原型的头文件:
#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
使用 Sleep() 函数调用和记录在日志中实现暂停的替代方法的不同可以通过一个小型 Expert Advisor 清楚的进行展示,其文本如下。 替代暂停实现方法由 Use_Sleep 输入参数设置 - “true”为使用 Sleep(),“false”为拒绝使用 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); }
除了上面给出的 EA,本文附件还给出了两个模拟 EA:
Ex_Timer_OrderLimits_TrailByTime.mq4 展示了如何根据时间实现跟踪止损和获利,
Ex_Timer_OrderSend_wMinTimeLimit.mq4 展示了如何在当前图表设置的周期内下单不超过一次。
是小缺点还是“毒瘤”?
不幸的是,所给的库不能完全替代 Sleep()。 这里有两个缺点,读者必须予以注意。
第一,使用 Sleep() 时,程序在暂停结束后立即执行。 在使用定时器时,它只在暂停之后出现第一个价格变动时继续。 这可能导致延迟的块执行过迟,或如上所述,根本不执行(例如,在链接通道出现严重错误时)。
第二,Sleep() 函数允许设置步长为 0.1 秒的暂停,但定时器只能以 1 秒为步长。
总结
一般来说,上述实现暂停的替代方法相对于使用 Sleep() 并非全是优点,也有一些弱点。 万物都有其强项和弱项,看重哪一个将取决于所追求的目的。 用 Sleep()还是不用 Sleep()?什么该取什么该舍? - 所有这些不能泛泛而论,而是在要实现的项目范围内。
附件:
功能库:
- ExLib_Timer.mqh - 头文件;
- ExLib_Timer.mq4 - 源文本。
应用示例:
- Ex_Timer_SleepOrNot.mq4 - 是一个 Expert Advisor,清楚的展示了实现暂停的替代方法之间的区别;
- Ex_Timer_OrderLimits_TrailByTime.mq4 - 一个展示了按时间跟踪止损和获利的 EA;
- Ex_Timer_OrderSend_wMinTimeLimit.mq4 - 一个展示了在当前图表上设置的周期内下单不超过一次的 EA。
重要须知! 我们认为“Ex_Timer_OrderLimits_TrailByTime”和“Ex_Timer_OrderSend_wMinTimeLimit”这两个 EA 仅适用于模拟账户!
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1558

