用MQL4开发贸易专家并不是一件容易的事情。 1、任何复杂的算法 贸易体系 这已经是一个问题,因为有很多细节需要考虑,从车辆的特殊性到环境的特殊性。 MetaTrader 4 其次,即使有最详细的算法,也不能消除将开发的算法移植到MQL4编程语言时出现的困难。
编译器在编写正确的专家时提供了一些帮助。 编译开始后,MetaEditor将报告代码中的所有语法错误。 除了语法错误,你的 参赞 它还可能包含编译器无法捕获的逻辑错误。 这就是为什么我们必须自己做。 如何做到这一点-在我们今天的材料。
最常见的编译错误
如果代码中有错误,则无法编译程序。 为了完全控制所有错误,建议使用指令设置的严格编译模式:
这种模式大大简化了错误查找。 现在我们来谈谈最常见的编译错误。
标识符与保留词匹配
如果变量或函数的名称与保留的单词之一相同:
int char[]; //错误 int char1[]; // 对 int char()//错误 { return(0); }
编译器将显示错误消息:
要修复此错误,必须修复变量或函数的名称。 我建议使用以下命名系统:
所有功能都必须表示操作。 这应该是一个动词。 例如,openlongposition()或modifystoploss()。 功能总是在做什么,对吗?
此外,这些功能被称为Camelcase风格。 a cebabu case风格的变量。 这是公认的做法。
说到变量的名字。 变量是名词。 例如,my_stop_loss,day_of_week,current_month。 给一个变量起一个长名字并不可怕,更可怕的是给它起一个不清楚的名字。 什么是道琼斯指数? 不,这是一个星期的一天。 当然,今天你已经知道这是什么变量了。 但当你打开顾问的代码一个月后,一切都会变得不那么明显。 这是你用来破译过去信息的时间吗?
变量和函数名称中的特殊字符
让我们继续。 如果变量或函数的名称包含特殊字符($,@,点):
int $var1; // 错 int @var2; // 错 int var.3; // 错 void f@()//错误 { return; }
编译器将显示错误消息:
为了纠正这个错误,有必要再次调整变量或函数的名称,或者立即用人名称呼它们。 理想情况下,代码应该以这样的方式编写,即使是不懂编程的人也只需阅读它,就可以理解它到底发生了什么。
Switch语句错误
旧版本的编译器允许您在Switch语句的表达式和常量中使用任何值:
void start() { double n=3.14; switch(n) { case 3.14: Print("Pi"); break; case 2.7: Print("E"); break; } }
在新的编译器中,Switch语句的表达式和常量必须是整数,因此在使用这些结构时会出现错误:
所以当你分析经典的代码时,比如 WallStreet , Ilan 和其他非婴儿(这对自我发展非常有用),你可能会遇到这个错误。 治疗方法非常简单,例如,使用以下字符串:
switch(MathMod(day_48, 10))
这是一个很容易解决问题的方法:
switch((int)MathMod(day_48, 10))
函数值返回
除void之外的所有函数都必须返回声明类型的值。 例如:
在严格编译模式下,出现错误:
在默认编译模式下,编译器输出警告:
如果返回的函数值与播发不匹配:
然后在严格的编译模式下出现错误:
在默认编译模式下,编译器输出警告:
为了纠正这些错误,只需在函数代码中添加带有相应类型返回值的返回运算符。
函数参数数组
函数参数中的数组仅通过引用传递。 以前不是这样的,所以在老顾问身上可以发现这个错误。 举个例子:
double ArrayAverage(double a[]) { return(0); }
此代码在严格编译模式下会导致错误:
在默认编译模式下,编译器输出警告:
要纠正这些错误,必须通过在数组名称前面添加前缀来显式指定引用数组传输:
double ArrayAverage(double &a[]) { return(0); }
顺便说一句,常量数组(time[],open[],high[],low[],close[],volume[])不能通过引用传递。 例如,呼叫:
无论编译模式如何,都会导致错误:
要消除此类错误,必须从常量数组复制必要的数据:
/--用于存储打开价格值的数组 double OpenPrices[]; /--将开放价格值复制到OpenPrices数组[] ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY); /--调用函数 ArrayAverage(OpenPrices);
最常见的错误之一是失去顾问。 指示器 在这种情况下,专家的用户通常 论坛 他愤怒地说:“顾问不工作。 ”或“我把顾问在时间表上,什么都没有发生! ». 解决这个问题其实很简单。 像往常一样,只需查看终端的“日志”选项卡并发现以下条目:
2018.07.08 09:15:44.957 2016.01.04 00:51 cannot open file 'C:Users1AppDataRoamingMetaQuotesTerminal MQL4indicatorsKELTNER_F12.ex4' [2]
这告诉我们,指示器被忘记放在文件夹中,或者它的名称不同。 如果没有指示器,则必须将其添加到指示器文件夹中。 如果有,请检查顾问代码中的名称-很可能是不同的名称。
编译器警告
编译器警告是信息性的,不是错误消息,但它们指出了可能的错误来源,并更好地纠正它们。 纯代码不应包含警告。
全局和局部变量名称的交叉
如果全局和本地级别存在具有相同名称的变量:
int i; // 全局变量 void OnStart() { int i=0,j=0; // 局部变量 for (i=0; i<5; i++) { j+=i; } PrintFormat("i=%d, j=%d",i,j); }
编译器将显示警告并指定声明全局变量的行号:
为了纠正这些警告,必须调整全局变量的名称。
类型不匹配
在下面的例子中:
#property strict void OnStart() { double a=7; float b=a; int c=b; string str=c; Print(c); }
在严格的编译模式下,如果类型不匹配,编译器将显示警告:
在本例中,编译器警告在分配不同类型的数据和隐式将int类型转换为string时可能发生的精度损失。
要修复此错误,必须使用显式类型转换:
#property strict void OnStart() { double a=7; float b=(float)a; int c=(int)b; string str=(string)c; Print(c); }
未使用变量
存在程序代码中不使用的变量(多余的实体)不是一个好的色调。
void OnStart() { int i,j=10,k,l,m,n2=1; for(i=0; i<5; i++) {j+=i;} }
无论编译模式如何,都会显示有关这些变量的消息:
要修复,只需从程序代码中删除未使用的变量。
编译错误诊断
通常,在编写程序后,由于代码错误而导致编译问题。 这可能是各种各样的错误,但在任何情况下,都需要快速检测出错误的代码段。
很多时候,人们需要大量的时间和神经来寻找多余的括号。 但是,有一种基于注释的快速错误检测方法。
写一个足够大的代码,没有一个错误,真是太好了。 但不幸的是,这种情况并不经常发生。 我不考虑导致代码执行错误的错误。 这是一个错误,导致无法编译。
常见的错误-在复杂的条件下插入额外的括号,缺少括号,在变量声明中不设置冒号或逗号, 变量名称的拼写错误等等。 通常,在编译时,您可以立即看到哪个行出现了这种错误。 但也有一些情况下,发现这样的错误并不容易。 编译器和敏锐的眼睛都无法帮助我们立即发现错误。 在这种情况下,初学者通常会“绕过”整个代码,试图直观地识别错误。 一次又一次,只要你的神经。
但是,像其他编程语言一样,MQL提供了一个很好的工具——注释。 使用它,您可以禁用代码的某些部分。 注释通常用于插入注释或禁用代码中未使用的部分。 评论也可以成功地用于查找错误。
查找错误通常归结为识别错误的代码段,然后在该段中直观地显示错误。 我认为,几乎没有人会怀疑,研究5-10行代码比100-500或数千行更容易、更快。
使用注释时,任务非常简单。 首先,您需要对代码的不同部分(有时几乎是整个代码)进行注释,从而“禁用”它。 然后,依次从代码的这些部分撤回评论。 在下一次撤回评论后,进行汇编尝试。 如果编译成功-错误不在代码的这一部分。 然后打开下一段代码,依此类推。 当有问题代码段时,视觉上会搜索错误,然后消除错误。 再次尝试编译。 如果一切顺利,错误就解决了。
正确定义要评论的代码段是很重要的。 如果这是一个条件(或其他逻辑结构),则必须对其进行完整的评论。 如果对声明变量的代码段进行注释,重要的是不要打开引用变量的代码段。 换句话说,注释必须根据编程逻辑来应用。 不遵守这种方法会导致新的、误导性的编译错误。
这是一个很好的错误例子,当不清楚在哪里可以找到它时,对代码进行评论可以帮助我们。
运行时错误
代码执行过程中出现的错误称为运行时错误(runtime errors)。 这些错误通常取决于程序的状态,并与不正确的变量值相关。
例如,如果一个变量被用作数组元素的索引,它的负值将不可避免地导致数组之外的值。
超越数组(Array Out of Range)
此错误通常发生在指示器访问指示器缓冲区时。 该indicatorcounted()函数返回上次调用指示器后未更改的条数。 以前计算的条形图上的指标值不需要重新计算,因此只需处理最后几个条形图就可以加快计算速度。
使用这种计算优化方法的大多数指标都是这样的:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { /--有时至少需要n个条(例如100条)才能计算。 //如果图形上没有这样多的条形图(例如,在MN时间帧上) if (Bars<100) { return(-1); // 停止计算,提前退出。 } /--自上次调用指示器以来未更改的条形数 int counted_bars=IndicatorCounted(); /--如果出现错误,请退出 if (counted_bars<0) { return(-1); } /--循环中开始计算的条形位置 int limit=Bars-counted_bars; /--如果countedu bars=0,则循环中的初始位置应减少1, if (counted_bars==0) { limit--; //不要在countedu bars==0时超越数组 //--计算使用10巴深的偏移量 /--历史记录,因此在第一次计算时添加此偏移 limit-=10; } else { /---指标已经计算过了,countedu bars>0 //--如果调用重复,则将限制提高1,以便 //--保证更新最后一条的指示器值 limit++; } /--主要计算周期 for (int i=limit; i>0; i--) { //使用的条形图值深入历史5和10 Buff1[i]=0.5*(Open[i+5]+Close[i+10]) } }
通常情况下,对countedu bars==0的不正确处理是不正确的(初始限制位置必须减少为相对于循环变量的1+最大索引)。
请记住,在执行start()函数时,我们可以访问从0到bars()-1的指示缓冲区数组元素。 如果需要处理不是指示器缓冲区的数组,则应使用arrayresize()函数放大数组的大小。 根据指示器缓冲区的当前大小。 通过调用arraysize()并将其中一个指示器缓冲区作为参数,也可以检索要寻址的元素的最大索引。
零分(Zero Divide)
如果除法操作的除数为零,则会出现“零分”错误:
void OnStart() { int a=0, b=0,c; c=a/b; Print("c=",c); }
在“专家”选项卡上运行此脚本时,会收到一条错误消息,并导致程序关闭:
当因子值由外部数据的值定义时,通常会出现这种错误。 例如,如果分析了交易参数,则所涉交易量 保证金 如果没有开放,则等于0。 订单 另一个例子是,如果分析数据是从文件中读取的,则无法保证文件正确工作。 因此,最好考虑到这种情况并正确处理。
最简单的方法是在除法操作之前检查除法器并输出参数值不正确的消息:
void OnStart() { int a=0, b=0,c; if (b!=0) { c=a/b; Print(c); } else { Print("Error: b=0"); return; } }
不会出现严重错误,但会显示参数值不正确的消息,并结束工作:
对当前字符使用0而不是null
旧版本的编译器允许在需要指定金融工具的函数中使用0(0)作为参数。
例如,技术指标的值 Moving Average 可以按以下方式查询当前字符:
AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); //错误
在新编译器中,必须显式指定空字符以指定当前字符:
AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // 对
您还可以使用symbol()和period()函数指定当前字符和图形周期。
AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); // 对
如果您使用预定义的u symbol和u period变量,则会更快地处理它们:
AlligatorJawsBuffer[i]=iMA(_Symbol,_Period,13,8,MODE_SMMA,PRICE_MEDIAN,i); // 对
Unicod字符串及其在DLL中的使用
字符串是Unicode字符序列。 您应该记住这一点,并使用相应的Windows功能。 例如,如果使用wininet.dll库函数而不是internetopena()和internetopenurla(),则应调用internetopenw()和internetopenurlw()。 将字符串传输到DLL时,应使用mqlstring结构:
#pragma pack(push,1) struct MqlString { int size; //32位整数,包含为字符串分配的缓冲区大小 LPWSTR buffer; //包含字符串的缓冲区的32位地址 int reserved; //32位整数,保留,不使用 }; #pragma pack(pop,1)
文件共享
打开文件时,必须显式指定要共享的fileu shareu write和fileu shareu read标志。
如果没有,文件将以垄断模式打开,直到被垄断方关闭,否则不会有人打开它。
例如,脱机图形需要显式指定共享标志:
// 1-st change - add share flags ExtHandle=FileOpenHistory(c_symbol+i_period+".hst", FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);
DateTime转换功能
请注意,将datetime类型转换为字符串取决于编译模式:
datetime date=D'2014.03.05 15:46:58'; string str="mydate="+date; /--str=“mydate=1394034418”-没有#property strict指令 /---str=“mydate=2014.03.05 15:46:58”-与#property strict指令
例如,尝试使用名称包含冒号的文件将导致错误。
运行时错误处理
由于没有内置的用户功能,任何交易专家都无法做到,所以首先我们将尝试简化错误分析的生活。 返回这些函数。
一些库可以在盒子里找到,以简化顾问的写作,包括处理错误。 它们存储在MQL4/include文件夹中:
我们需要两个图书馆:
stderror.mqh-包含每个错误编号的常量;
stdlib.mqh包含一些辅助功能,包括以字符串形式返回错误描述的功能:
string ErrorDescription(int error_code)
因此,我们将这两个库连接到我们的项目中:
#include <stderror.mqh> #include <stdlib.mqh>
错误描述本身可以在mql4/library/stdlib.mql4文件中找到,并且是英文的。 因此,如果你反对外语,你总是可以用母语重写描述。
我们需要的另一个内置功能是getlasterror()。 它以整数(int)的形式返回错误代码,然后我们将处理它。 错误代码本身及其在俄语中的描述可以在 MQL4手册 从元报价。 您还可以从那里获取将stdlib.mql4文件翻译成俄语的信息。
现在,我们已经连接了必要的库,看看与交易操作直接相关的功能的结果, 因为忽略这些功能中的故障可能会导致严重的后果。
不幸的是,MQL4不能编写一个通用库来处理所有可能的错误。 在每种情况下,必须分别处理错误。 但并非一切都那么糟糕——许多错误不需要处理,在开发阶段排除它们就足够了 测试专家 但这需要及时了解它们的存在。
让我们看看MQL4专家的两个常见错误:
错误130-ERR_INVALID_STOPS
错误146-ERR_TRADE_CONTEXT_BUSY
第一个错误发生的情况之一是专家试图将 待决订单 太接近市场了。 在某些情况下,这可能会严重影响专家的表现。 例如,假设一个专家打开一个有利可图的头寸,每150次收缩利润。 项目 如果在下一次尝试中出现错误130,并且价格不可逆转地恢复到先前的止损水平,专家可能会剥夺您的合法利润。 尽管有这种可能性,但可以通过修改专家代码,使其考虑到价格与价格之间的最小允许距离,从而从根本上消除这一错误。 脚 .
第二个错误与终端的交易上下文占用有关,无法完全排除。 当多个专家在一个终端上工作时,总是有一种情况,即一个专家试图打开一个位置,而另一个专家仍在这样做。 这样的错误总是需要处理的。
因此,如果使用的任何内置函数在专家工作期间返回错误,我们应该始终保持警惕。 这可以通过使用以下简单的辅助功能来实现:
void logError(string functionName, string msg, int errorCode = -1) { Print("ERROR: in " + functionName + "()"); Print("ERROR: " + msg ); int err = GetLastError(); if (errorCode != -1) { err = errorCode; } if (err != ERR_NO_ERROR) { Print("ERROR: code=" + err + " - " + ErrorDescription( err )); } }
我们将以以下方式使用它:
void openLongTrade() { int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask + 5, 5, 0, 0); if (ticket == -1) { logError("openLongTrade", "could not open order"); } }
当然,这是一个简单的例子。 要编写更实用的打开、关闭和修改订单功能,请参阅 这一课 .
第一个参数将检测到错误的函数的名称传递给logerror()函数,在我们的示例中,它是openlongtrade()函数。 如果我们的专家在多个地方调用ordersend()函数,这将使我们能够准确地确定哪个地方发生了错误。 第二个参数传递错误描述,以便您了解在openlongtrade()函数中检测到错误的确切位置。 这可以是一个简短的错误描述,也可以是一个更详细的描述,列出了传递到内置函数的所有参数的值。
我更喜欢后一种选择,因为如果发生错误,您可以立即获得分析所需的所有信息。 例如,假设在调用orderSend()之前,当前价格已经与我们知道的最后一个价格严重偏离。 因此,在执行此示例时,将出现错误,并将出现以下行:
ERROR: in openLongTrade() ERROR: could not open order ERROR: code=138 - requote
你会立刻看到:
哪些功能出现错误;
它涉及什么(在这种情况下-试图打开一个位置);
什么是错误(错误代码和错误描述)。
现在考虑函数logerror()的第三个可选参数。 当我们希望处理特定类型的错误时,它是必要的,其余的将在专家的工作记录中报告,一如既往:
void updateStopLoss(double newStopLoss) { bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), OrderExpiration()); if (!modified) { int errorCode = GetLastError(); if (errorCode != ERR_NO_RESULT ) { logError("updateStopLoss", "failed to modify order", errorCode); } } }
在updatestoploss()函数中调用内置的ordermodify()函数。 此函数在错误处理方面与orderSend()略有不同。 如果要修改的订单的任何参数都与当前订单的参数相同,则函数将返回erru nou result错误。 如果我们的专家允许这种情况发生,我们就必须忽略这一具体错误。 为此,我们分析getlasterror()返回的值。 如果ERRu NOu RESULT代码出现错误,我们不会输出任何结果。
但是,如果发生了另一个错误,我们必须像以前一样充分报告它。 这就是为什么我们将getlasterror()函数的结果存储在中间变量中,并将第三个参数传递给logerror()函数。 事实上,内置的getlasterror()函数在调用后自动重置上次错误的代码。 如果我们没有明确地将错误代码传递到logerror()中,则协议将显示代码为0的错误,并描述为“no error”。
在处理其他错误时,也必须执行类似的操作,例如查询。 主要的想法是只处理需要处理的错误,其余的传递到logerror()函数。 如果在专家工作期间发生了意外错误,我们将始终保持警惕。 分析 日志 ,我们可以决定是否需要单独处理此错误,或者是否可以通过修改专家代码来排除此错误。 这种方法通常显著简化了生活,并减少了处理错误所需的时间。
逻辑错误诊断
专家代码中的逻辑错误可能会导致许多问题。 由于缺乏一步一步地调试专家的能力,与这些错误作斗争不是一件愉快的事情。 目前诊断这一点的主要方法是内置的print()函数。 使用它,您可以打印重要变量的当前值,并在终端期间记录专家的工作进度。 测试 在可视化测试期间调试专家时,内置的comment()函数也可以帮助您将消息输出到图表上。 通常情况下,在确定专家的工作方式与预期的不同后,必须添加对print()函数的临时调用。 并记录专家在可能发生错误的地方的内部状态。
但是,为了检测复杂的错误情况,有时需要添加几十个这样的print()调用。 在发现和解决问题后,必须删除或评论它们,以免专家的代码混乱或延迟测试。 如果专家代码中的print()函数已用于定期记录不同的状态,则情况会恶化。 然后,只需在专家代码中搜索短语“print”就无法删除print()临时调用。 必须考虑不要删除对该功能的有用调用。
例如,在记录orderSend()、orderModify()和orderClose()函数的错误时,在协议中键入当前BID和ASK变量值可能是有用的。 这有助于识别错误的原因,如ERRu INVALIDu STOPS和ERRu OFFu QUOTES。
为了在协议中突出这些诊断结果,我建议使用以下辅助功能:
void logInfo(string msg) { Print("INFO: " + msg); }
出于几个原因,这是可取的。 首先,现在在专家代码中搜索“print”时不会遇到这样的调用,因为我们将搜索Loginfo。 其次,这个功能还有另一个有用的特性,我们稍后会讨论。
添加和删除print()函数的临时诊断调用占用了我们宝贵的时间。 因此,我建议考虑另一种方法,这种方法可以有效地检测代码中的逻辑错误,并为我们节省一点时间。 考虑以下一个简单的函数:
void openLongTrade(double stopLoss) { int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) { logError("openLongTrade", "could not open order"); } }
在这种情况下,当我们开仓时,很明显, 在专家正常工作时,stoploss参数的值永远不会大于或等于当前出价。也就是说,当调用openlongtrade()函数时 总是满足stoploss<bid条件。由于我们在编写函数时就知道了这一点,因此我们可以立即使用以下方法:
void openLongTrade( double stopLoss ) { assert("openLongTrade", stopLoss < Bid, "stopLoss < Bid"); int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) { logError("openLongTrade", "could not open order"); } }
也就是说,我们使用新的辅助函数assert()在代码中记录我们的声明。 这个功能看起来很简单:
void assert(string functionName, bool assertion, string description = "") { if (!assertion) { Print("ASSERT: in " + functionName + "() - " + description); } }
函数的第一个参数是它的名称,它检查我们的条件(类似于logerror()函数)。 第二个参数传递此条件的验证结果。 第三个参数是它的描述。 因此,如果未满足预期条件,则协议将显示以下信息:
例如,可以传递条件本身作为描述,也可以输出更详细的描述,其中包含检查条件时受控制变量的值。 如果这有助于找出错误的原因。
当然,这个例子是尽可能简单的。 但是,我希望基本的想法反映得足够好。 在增强专家功能的过程中,我们知道专家应该如何工作,允许和不允许函数的状态和输入参数。 通过使用assert()函数将其固定在专家代码中,我们将获得有关专家工作逻辑被破坏的位置的宝贵信息。 此外,我们部分免除了添加或删除print()函数临时调用的需要,因为assert()函数 只有当我们在预期条件下发现不一致时,才会向协议提供诊断信息。
另一个有用的方法是在每次裂变操作之前使用此功能。 事实上,有时由于某种逻辑错误,有时会被除以零。 在这种情况下,专家的工作停止了,协议中只出现了一行悲伤的诊断:“零分裂”。 如果代码中多次使用除法操作,很难找出错误发生的确切位置。 这里是assert()函数的帮助。 在每次裂变操作之前插入适当的检查:
assert("buildChannel", distance > 0, "distance > 0"); double slope = delta / distance;
现在,在除以零的情况下,只需查看日志即可找出错误发生的确切位置。
状态处理
在专家工作期间,您的帐户中可能会出现一些并非错误的情况-所谓的专家状态。 这些状态不是错误,但仍然值得记录。 这是MQL4语言的特殊功能。
isexpertenabled()函数返回有关启动专家的可能性的信息。 如果客户端终端允许运行专家,则函数将返回true,否则返回false。 如果返回false,请通知用户启用相应的设置是有用的。 例子:
void OnStart() { if (!IsExpertEnabled() { 顾问不允许交易 Alert("Attention! Please press the "Expert Advisors" button in MT4"); } //顾问工作算法 return; }
如果专家使用外部库,则使用islibrariesallowed()函数。 如果专家可以调用库函数,则返回true,否则返回false。
如果库是DLL文件,则使用isdllsallowed()函数。 检查是否有可能使用istradeallowed()函数在专家的帮助下进行交易也是有益的。
如果你想知道账户是否 示范或真实 ,可以使用isdemo()函数。
以上所有检查都应该使用oninit()函数进行。
当然,您应该定期检查与服务器的通信。 isconnected()函数将帮助您做到这一点。
以下三项功能将有助于确定顾问处于何种模式。 如果isoptimisation()返回true,则执行 优化 ,如果iStesting(),则测试,iVisualMode()–可视化模式下的测试。 在每一种情况下,顾问可以有自己的逻辑。 例如,对于可视化模式,您可以在图表上显示某些内容(而不是在其他模式下显示以节省资源)。 在测试模式下,您可以输出调试信息,在优化模式下,您可以简化代码以节省时间。
最后一个函数是istradecontextbusy()。 如果交易流程被占用,它将返回true。 这在专家进行交易时很有用。 您可以使用Sleep功能等待某个时刻并重新尝试。
另一个有用的函数是uninitializerason()
int deinit() { switch(UninitializeReason()) { case REASON_CHARTCLOSE: case REASON_REMOVE: CleanUp(); break; 清理和释放资源。 case REASON_RECOMPILE: case REASON_CHARTCHANGE: case REASON_PARAMETERS: case REASON_ACCOUNT: StoreData(); break; //为修复做准备。 } //... }
您还可以记录顾问退出的原因。
常见错误代码及其可能的解决方案
错误编号
意义
问题
决定
4, 146
交易服务器忙
顾问同时下达了太多命令,或者没有等待服务器的回复,在执行操作时,顾问试图发送新的命令
使用错误处理功能重新启动终端或优化顾问代码
8, 141
频繁查询
以前的错误原因,在一个非常频繁的查询
相似解
129
错价
您试图开仓(买入或卖出)的价格不正确
Buy必须按Ask开启,按Bid关闭; SELL必须通过BID打开,然后通过ASK关闭
130, 145
错脚
止损,利润丰厚 或打开延迟或限制级别不正确。 脚离价格太近了。 贵 帐户 在ECN(ECN)或NDD(NDD)组中发现,不允许立即移动脚
检查您的止损损失值,获利回调,检查您的工具的最低止损水平 经纪人 当你把脚放在地板上时,保持最小的距离。 一个写得很好的顾问应该有ECN和NDD帐户的功能-这是通过在订单打开后修改订单来实现的
131
不规则体积
不正确的 测深 当交易开始时,或是更少(最大)。 批量的大小也可能不同于批量的大小。 经纪人
检查是否正确开仓,检查合同规范,阅读交易条款和条件在您的交易中心,检查最低和最高 测深 在您的DHC和您的帐户
132
市场关闭
市场周末关闭。
周末后试着联系市场。
133
禁止贸易
目前,贸易已被禁止。
根据 货币对 禁止交易-在特定时间或任何时候。 通常,经纪人在午夜有几分钟的休息时间。
134
没有足够的钱做交易。
你想打开的地块太大了,没有足够的保证金。
检查您的可用资金水平,计算您需要的资金来打开批量,并跟踪您的可用资金水平
135-138
价格变了
市场太快( 新闻 )经纪商或交易中心不允许您按申报价格下注
不要在这样的时刻交易,提高水平。 滑动 ,但请记住,这意味着打开头寸,而不是你所说的价格。 为顾问提供错误处理功能和开仓尝试次数
147
经纪人禁止使用订单到期日
您的顾问或您正在尝试设置延期订单的到期日
在顾问中,在ordersend函数中,将过期日期设置为0(0)。 不要设定订单到期日
148
未结和待决订单数量达到经纪人设定的上限
未平仓订单和头寸的最大数量已达到经纪人设定的上限
删除或关闭部分位置。 停止打开新头寸的过程
4012, 4013
零余数
你想把一个数字除以0(零)
检查顾问代码是否有错误,或者在返回0时检查marketinfo函数中的所有值,有时marketinfo(symbol(),modeu spread)不会返回价差。 0(浮动经纪商) 价差 )
4017
DLL调用不允许
您的终端不允许调用DLL
通过菜单–服务–设置–顾问–允许DLL调用
4018, 4019
无法加载库
库已损坏或调用失败,可能根本不存在
检查DLL库
4020
不允许调用外部库函数
您的终端不允许从外部专家调用函数。
允许通过菜单–服务–设置–顾问–允许调用外部专家
4103
无法打开文件
此文件不存在或被其他进程阻止
检查是否有指定的文件。 检查防病毒系统是否锁定了文件,是否允许写入/读取文件
4106
未知字符
市场评论中没有符号
在市场评论中-右键单击-显示所有字符。 检查顾问中的符号名称,并检查其是否存在于市场评论中。 一些顾问使用明确的名称而不使用后缀,经纪人故意使用后缀,例如EurusDX,其中x是后缀
4108
无效的票证号码
专家选择的订单票证不存在。 专家试图选择票证,但此订单已被另一位顾问或手动关闭。 当试图在订单上执行订单时,票证被经纪人执行并关闭
如果此错误经常出现,每分钟100-1000次,请检查顾问的功能。 禁用其他顾问或将其配置为不冲突,在专家执行操作时不要手动关闭订单。 有时,当多个顾问使用相同的magicnumber时会发生这种情况。
4109
贸易不允许
顾问被禁止交易,图表上有悲伤的表情或十字架。
在安装顾问时,在选项卡上或在“服务-设置-顾问”菜单上启用“允许顾问交易”复选框
4110, 4111
不允许长/短位置
在“顾问设置”中,在“常规”选项卡中不允许项目类型
在“常规”选项卡中,在 安装顾问 可以选择要打开的位置
结论
所考虑的支持功能和简单的技术大大简化和加快了检测和纠正贸易专家代码错误的过程, 用MQL4编程语言编写。 熟练地编写代码和函数 记录 对顾问工作的支持大大加快了起草进程。
论坛主题
尊敬的Dmitry Aka Silentspec
TradeLikeaPro.ru