测试程序中的断点:这是可能的!

Christo Tsvetanov | 24 二月, 2016

简介

MQL4 中唯一让我怀念的是 Expert Advisor 的一个普通调试程序。每个人都有人类的情感,所以我们会犯错。正常编程时,我们设置断点、运行程序,当程序运行到断点时,将会停止。我们就可以查看所用变量的内容。

现在由于 Print、Comment 等函数的使用,显示调试数据已成为可能。但有时可能想在某时某处暂时停止程序,以对状况进行评估。这里有一些微妙之处。通常运行该程序,以在演示账户或真实账户上交易。这意味着我们只能在几个月后看到结果… 因此,调试模式仅在其处于 Expert Advisor 的测试模式(在测试程序)方为合理。


工作原理

由于是测试程序中的“视觉测试模式”,使得在程序快速通过测试程序时追踪 EA 的响应变为可能。如果要暂时停止执行,可以按键盘上的“Pause”键或在测试程序工具栏内的相同名称的按钮上单击鼠标。终端的开发者提供了名称为 WinUser32.mqh 的库,内有一些非常有趣的函数。其中之一是 keybd_event。它允许我们按下任意想要的键。

这里提出的想法是 - 我们可以编写一个函数,能够通过程序执行暂停并打印必要的调试信息。由于 Expert Advisor 使用 DLL,我们应先将其在 EA 中启用。按 Ctrl+O 并选择/取消选择复选框:



然后必须在代码开始的某处声明使用 WinUser32:

#include <WinUser32.mqh>

紧跟该操作的是函数 BreakPoint 自身的声明。这里有一些微妙之处,但最简单的理解是假设没有传递/返回参数:

void BreakPoint()

函数必须在视觉测试模式下触发,所以我们插入检验标记:如果测试程序未处于视觉测试模式,则有:

if (!IsVisualMode()) return(0);

然后将一些数据可视化。依我之见,最具描写性的是使用 Comment()。假设我们只需买入和卖出。

string Comm="";
Comm=Comm+"Bid="+Bid+"\n";
Comm=Comm+"Ask="+Ask+"\n";
   
Comment(Comm);

此处的“\n”指的是后面的数据将显示在下一个字符串。最后,让我们按下“Pause”。

keybd_event(19,0,0,0);
Sleep(10);
keybd_event(19,0,2,0);

第一个字符串将键按下,最后一个将其释放。“Sleep”也很有必要,因为过快按下/释放可能不被处理。19 是暂停的虚拟代码,最后字符串中的 2 表示必须模拟释放。

我们需要做的只是在 Expert Advisor 代码中替换断点,比如在 Expert Advisor Sample 文章的示例中刚建立多头头寸之后。

ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
BreakPoint();

以下是要插入的全部代码:

//We will use a function, described in header file
#include 
 
//Breakpoint neither receive nor send back any parameters
void BreakPoint()
{
   //It is expecting, that this function should work
   //only in tester
   if (!IsVisualMode()) return(0);
   
   //Preparing a data for printing
   //Comment() function is used as 
   //it give quite clear visualisation
   string Comm="";
   Comm=Comm+"Bid="+Bid+"\n";
   Comm=Comm+"Ask="+Ask+"\n";
   
   Comment(Comm);
   
   //Press/release Pause button
   //19 is a Virtual Key code of "Pause" button
   //Sleep() is needed, because of the probability
   //to misprocess too quick pressing/releasing
   //of the button
   keybd_event(19,0,0,0);
   Sleep(10);
   keybd_event(19,0,2,0);
}


观察局部变量,我们要做些什么?

问题在于这种变量在其声明之外的“不可见性”。本例中,数据应该传递。假设我们要观察来自同一片文章的变量MacdCurrent。为此,我们将如下改动函数:

void BreakPoint(double MacdCurrent)
{
   if (!IsVisualMode()) return(0);
   
   Comment("MacdCurrent = ",MacdCurrent);


可选的断点

我们有时可能希望程序在达到特定字符串时并不总是停止,而要同时符合一定的额外要求时才停止。这通常出现在循环中。例如,在计数器达到预定义数值时,我们要中断执行。为此,必须通过额外的要求:

void BreakPoint(double MacdCurrent, bool Condition)
{
   if (!IsVisualMode() || (!Condition)) return(0);
   //Or - which is the same:
   //if (!(IsVisualMode()&&Condition)) return(0);
   
   Comment("MacdCurrent = ",MacdCurrent);

我们将调用如下:

for(cnt=0;cnt<total;cnt++)     
   {
      OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
      BreakPoint(MacdCurrent, cnt==1);


总结

为什么不干脆创建一个连接程序的库并使用呢?问题在于有很多种类,最好修改函数 BreakPoint。固然,以上所述仅仅是大致的想法。可以有多种不同的实施方式。

最后,我要感谢Klot,他是第一个考虑如何通过程序实现暂停的人。