English Русский Español Deutsch 日本語 Português
preview
从新手到专家:MQL5中的协作式调试指南

从新手到专家:MQL5中的协作式调试指南

MetaTrader 5示例 |
453 8
Clemence Benjamin
Clemence Benjamin

核心内容 


概述:

通常,每位作者都试图在文章中自始至终围绕某个具体问题展开探讨。今天的标题虽为“调试”,但请勿轻视其深度——我们将超越基础流程,深入挖掘实战精髓。我将分享多年来在多个成功项目中被验证有效的调试策略。在软件编程的所有领域中,调试都是不可回避的核心议题。我注意到,许多作者习惯展示完美无瑕的最终成果,却鲜少提及代码从混乱到稳定所经历的挣扎。

无论是神经网络系统、人工智能(AI)还是其他相关软件领域,调试都是关键能力。我相信,最有价值的技能是即时解决问题的能力,这种能力会演变为您的本能,助您从容应对类似的挑战。因此,系统性的解决问题能构建强大的技能体系,让新手逐渐蜕变为专家。

协作式调试

协作式调试

在撰写本文时,我正参与一个项目,期间编译代码时屡遇错误。有时调试器会抛出多达50行错误提示,令人应接不暇。但通过反复试错,我总结出一套高效的排错流程,如今能在数秒至数分钟内定位并解决问题,让程序顺畅运行。

我的目标是向您揭示加速调试进程的技术工具。根据我的经验,主动参与调试项目是快速掌握MQL5的捷径。我特意保留了一份带漏洞的示例程序,我们将共同逐步优化。

一个长期存在的挑战是:许多人难以坚持读完免费提供的MQL5长篇教程,但这并不意味着它们缺乏价值。这些书籍系统涵盖了从基础到进阶的MQL5编程知识。为了取得最佳的效果,明智的做法是在参与现有项目的同时,参考这些书籍中与您正在进行的工作相关的特定主题。这样一来,当您完成这些项目后,您将自然而然地掌握了大量的相关知识。

我们的错误报告通常按照下表中的四列结构呈现。

列(名称) 解释说明
描述 这一列提供了代码中遇到的错误或警告的简要说明。它可以用来描述问题的性质,例如语法错误、未声明的变量、类型不匹配或其他编程错误。理解描述有助于程序员快速识别问题。
文件 这里指出了发生错误的文件名称(例如D1 PriceMarker.mq5)。在一个项目中有多个文件的情况下,知道哪个文件有问题对于有效的调试和修正至关重要。
具体说明在指定文件中找到错误的确切行号。这种精确定位允许程序员快速导航到需要关注的代码的具体部分,减少了定位问题所需的时间。
指出了通过逐行检测找到错误的具体列号。尽管它不如前面的列常用,但在识别复杂语句中的错误或当前行包含的许多元素(如多个函数调用或参数)时,它可能特别有用。

它们通常从左到右排列,如下所示。我们还拿到了完整的错误报告,该报告包含20个错误和4个警告(见代码片段底部),这是我们将在本次讨论中调试程序的错误报告,位于表格下方。您会发现报告的特征很容易识别。如果您读到了最后,我们将探讨如何解决以下代码段中呈现的错误。

描述 文件

'D1 PriceMarker.mq5'                    1
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      70      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      70      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      71      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      71      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      72      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      72      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      73      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      73      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      74      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      74      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      75      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      75      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      76      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      76      5
'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      77      5
   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      77      5
'ObjectCreate' - wrong parameters count D1 PriceMarker.mq5      15      9
   built-in: bool ObjectCreate(long,const string,ENUM_OBJECT,int,datetime,double,...)   D1 PriceMarker.mq5      15      9
'ObjectCreate' - wrong parameters count D1 PriceMarker.mq5      27      9
   built-in: bool ObjectCreate(long,const string,ENUM_OBJECT,int,datetime,double,...)   D1 PriceMarker.mq5      27      9
'ObjectSetText' - undeclared identifier D1 PriceMarker.mq5      37      5
',' - unexpected token  D1 PriceMarker.mq5      37      28
'labelName' - some operator expected    D1 PriceMarker.mq5      37      19
'+' - illegal operation use     D1 PriceMarker.mq5      37      36
',' - unexpected token  D1 PriceMarker.mq5      37      69
result of expression not used   D1 PriceMarker.mq5      37      43
',' - unexpected token  D1 PriceMarker.mq5      37      73
expression has no effect        D1 PriceMarker.mq5      37      71
',' - unexpected token  D1 PriceMarker.mq5      37      82
expression has no effect        D1 PriceMarker.mq5      37      76
')' - unexpected token  D1 PriceMarker.mq5      37      87
expression has no effect        D1 PriceMarker.mq5      37      84
'OBJPROP_Y' - undeclared identifier     D1 PriceMarker.mq5      41      36
'ObjectSetInteger' - no one of the overloads can be applied to the function call        D1 PriceMarker.mq5      41      5
could be one of 2 function(s)   D1 PriceMarker.mq5      41      5
   built-in: bool ObjectSetInteger(long,const string,ENUM_OBJECT_PROPERTY_INTEGER,long) D1 PriceMarker.mq5      41      5
   built-in: bool ObjectSetInteger(long,const string,ENUM_OBJECT_PROPERTY_INTEGER,int,long)     D1 PriceMarker.mq5      41      5
20 errors, 4 warnings           21      5

在探讨该主题时需要了解的一些常见术语:

  • 语法(Syntax):指的是规定程序(如交易算法、指标和脚本)必须如何编写,以便MetaTrader 5平台能够正确理解和执行的一组特定规则。
  • 错误(Error):指的是代码中的一个错误或问题,它会阻止代码正确执行。错误可以分为几种类型,如语法错误、运行时错误、逻辑错误、类型错误和编译错误。


什么是调试?

我相信大多数人都熟悉这个广为人知的术语,但对于初学者来说,详细理解这个概念至关重要。现在,我将定义一个“漏洞(bug)”,以便你们能更好地理解调试的过程。

  • 漏洞:

计算机程序或系统中的一个错误或缺陷,导致其行为出乎意料或不正确。 

  • 调试:

调试是识别、隔离并修复计算机程序或系统中的错误或“bug”的过程,目的是确保程序按预期运行,并在各种条件下产生正确的输出。

MQL5编程中的调试

以下是与MQL5相关调试的一些方面:

 1. MQL5中常见的错误类型:

  • 语法错误(Syntax Errors):MQL5代码结构中的错误,导致脚本无法编译。

该示例展示了一个简单的语法错误,其中Print()函数缺少闭合括号。因此,脚本将无法编译,这就突显了仔细检查代码结构的必要性。

void OnStart()
{
    Print("Hello, World!" // Missing closing parenthesis
}
  • 逻辑错误(Logical Errors):代码可以编译和运行,但产生错误的交易信号或行为出乎意料。

这里是一个逻辑错误,条件错误地触发了一个买入订单。程序员原本打算利用更复杂的逻辑,这取决于市场条件,但当前的实现导致了不必要的交易。

void OnTick()
{
    double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double targetPrice = currentPrice + 100; // Intended to use a different logic

    // Mistakenly placing a buy when conditions aren't right
    if (currentPrice < targetPrice) // Logic Error
    {
        OrderSend(_Symbol, OP_BUY, 0.1, currentPrice, 3, 0, 0, "Buy order", 0, 0, clrGreen);
    }
}
  • 运行时错误(Runtime Errors):在执行过程中发生的错误,例如尝试访问数组的越界元素或未正确处理交易订单。
在该示例中,尝试访问一个只有五个有效索引的数组的第六个元素。这将导致运行时错误,说明在处理数组时进行边界检查的重要性。
void OnStart()
{
    double prices[5];
    
    // Attempt to access out-of-range index (e.g., index 5)
    double value = prices[5]; // Runtime error: Array out of range
}

2. MQL5中的调试工具:

  • MetaEditor:这是MQL5的集成开发环境(IDE)。它提供了语法高亮、代码补全和错误指示,可以帮助快速识别语法错误。
以下代码允许在result变量计算处设置断点。在MetaEditor中调试时,您可以在此处检查变量,以确认它们是否按预期设置,便于逐行执行分析程序的运行过程。
void OnStart()
{
    double startValue = 10.0;

    // Set a breakpoint here to inspect value
    double result = startValue * 2;

    Print("Result: ", result); // Check the value of result
}
  • 打印语句(Print Statements):使用Print()函数将变量值和执行流程日志输出到“Experts”日志。这是一种直接追踪问题的方法。
在下面的代码片段中,我们使用Print()函数记录当前的买入价和卖出价。这种方法有助于在执行过程中追踪变量值,并且可以通过比较预期值和实际值来帮助诊断逻辑错误。
void OnTick()
{
    double bidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    
    // Logging prices for debugging
    Print("Bid: ", bidPrice, ", Ask: ", askPrice);
}
  • 注释掉代码(Commenting Out Code):临时禁用代码的某些部分以隔离问题区域。
在该示例中,注释掉一行代码以专注于替代逻辑。在无需完全移除的情况下,这种技术对于隔离错误和测试代码的不同部分非常有用。
void OnTick()
{
    // double result = SomeFunction(); // Temporarily disabling this line
    
    // Run alternate logic while debugging
    Print("Running alternative logic.");
}
3. 在MetaEditor中使用调试器:

MetaEditor包含一个内置的调试器,允许:
  • 断点(Breakpoints):在代码中设置断点可以让您在特定行暂停执行并检查变量值。
  • 逐步执行(Step Through Execution):您可以逐行逐步执行代码,观察变量如何变化以及逻辑流程如何展开。
  • 监视窗口(Watch Expressions):在代码执行过程中监控特定变量的值。
 4. 测试和优化:
  • 策略测试器(Strategy Tester):MQL5提供了一个策略测试器,允许您使用历史数据对交易策略进行回测。它可以帮助模拟交易并分析性能,这样可能会揭示出隐藏的错误。
  • 优化(Optimization):您可以优化参数以提高交易算法的性能,这也可能会突出逻辑或执行中的潜在错误。
 5. 错误处理:
  • 处理MQL5特定的错误代码,例如检查与交易操作相关的函数的返回值。常见的函数包括订单执行函数(OrderSendOrderClose等),其中适当的错误处理可以防止崩溃或不期望的结果。

MQL5调试的最佳实例:

1. 代码模块化(Code Modularity):将代码分解为函数,编写模块化代码。这使得测试和调试各个组件变得更加容易。2. 代码注释(Documenting Code):在代码中添加注释以解释每个部分的作用,这在一段时间后重新查看代码时会很有帮助。3. 定期测试(Regular Testing):在开发过程中频繁测试代码,而不是等到一切都完成后再测试。这有助于尽早发现错误。


在MQL5中实现调试

不久前,我和我的朋友创建了一个脚本的草稿,用于绘制标记关键价格水平(如最高价、最低价、开盘价和收盘价)的前一日(D1)K线图的价格线。这是我为本文保留的版本。 

通常,当编写EA代码时,MetaEditor提供了模板和参数建议,以帮助开发人员并减少错误的可能性,前提是按照正确的语言语法输入详细信息。然而,有时您可能会得到论坛上的代码,这些代码包含错误。以下是带有错误的脚本程序的代码段:

//+------------------------------------------------------------------+
//|                                               D1 PriceMarker.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.00"
#property strict

// Function to create a price line with a label
void CreatePriceLine(string name, double price, color clr, string label)
{
    // Create a horizontal line
    if(!ObjectCreate(name, OBJ_HLINE, 0, 0, price))
    {
        Print("Failed to create line: ", GetLastError());
        return;
    }
    
    // Set line properties
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);

    // Create a label for the price
    string labelName = name + "_label";
    if(!ObjectCreate(labelName, OBJ_LABEL, 0, 0, 0))
    {
        Print("Failed to create label: ", GetLastError());
        return;
    }

    // Set label properties
    ObjectSetInteger(0, labelName, OBJPROP_XSIZE, 70);
    ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
    ObjectSetInteger(0, labelName, OBJPROP_COLOR, clr);
    ObjectSetText(labelName, label + ": " + DoubleToString(price, 2), 10, "Arial", clr);

    // Position the label
    double yPos = price; // Positioning along the price axis
    ObjectSetInteger(0, labelName, OBJPROP_Y, yPos);
    ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 5);
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
{
    // Get the previous D1 candle's open, high, low, and close prices
    datetime prevCandleTime = iTime(NULL, PERIOD_D1, 1);
    double openPrice = iOpen(NULL, PERIOD_D1, 1);
    double highPrice = iHigh(NULL, PERIOD_D1, 1);
    double lowPrice = iLow(NULL, PERIOD_D1, 1);
    double closePrice = iClose(NULL, PERIOD_D1, 1);

    // Draw labeled price lines for open, high, low, and close prices
    CreatePriceLine("PrevCandle_Open", openPrice, clrBlue, "Open");
    CreatePriceLine("PrevCandle_High", highPrice, clrRed, "High");
    CreatePriceLine("PrevCandle_Low", lowPrice, clrGreen, "Low");
    CreatePriceLine("PrevCandle_Close", closePrice, clrOrange, "Close");
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Remove the drawn price lines and labels upon indicator deinitialization
    ObjectDelete("PrevCandle_Open");
    ObjectDelete("PrevCandle_High");
    ObjectDelete("PrevCandle_Low");
    ObjectDelete("PrevCandle_Close");
    ObjectDelete("PrevCandle_Open_label");
    ObjectDelete("PrevCandle_High_label");
    ObjectDelete("PrevCandle_Low_label");
    ObjectDelete("PrevCandle_Close_label");
}

//+------------------------------------------------------------------+                                                                                                            

(1)查找漏洞(bug)

我发现当尝试编译一个有错误的程序时,MetaEditor内置的调试器相当好用。大多数错误在您点击MetaEditor中的“编译”或“实时分析”按钮时被捕获。然而,有些性能错误并不会阻止程序编译;这些错误只能在您尝试运行程序时在平台或测试器上被识别出。

开发人员可以从头开始编写整个程序,然后进行调试,也可以简单地调试一个已有的程序。在下图中,我们利用MetaEditor内置的编译器和调试器来获取错误摘要(见工具箱窗口底部)。双击错误行,光标将移动到并突出显示包含错误的相应代码行。我很少在点击“编译”按钮之前注意到代码中的错误,此时调试器会报告出现的任何问题。

定位错误

在MetaEditor中定位错误

有时程序会成功编译,但可能不会产生期望的结果。在这种情况下,通过检查MetaTrader 5平台工具箱窗口中可用的“专家日志”和“日志记录”来识别潜在错误是明智之举。这些日志可以提供关于执行过程中可能出现问题的宝贵见解,帮助您有效地进行故障排除和解决问题。

(2)三种调试技术

错误通常源于人为输入错误或术语的误用,调试器通常会提供解释说明和部分解决提示。如果您能理解这些信息,通常可以直接返回代码中并进行必要的修正。然而,有些更复杂的情况可能难以诊断,或许需要参考文献或依靠社区专家。

在我的经验中,我在调试复杂问题时通常使用几种策略。我将这些步骤分为三种主要方法,尽管这个列表并不全面;它是为了本次讨论的目的而特别设计的。让我详细地解释每一步流程。

1. 重新查阅文档:

通过提供关于语法、函数使用和错误消息的清晰说明,有助于调试MQL5程序。提供了详细的解释和示例,帮助识别错误的实现并增强对内置函数的理解。此外,文档通常包括有关常见错误、最佳实践和替代解决方案的信息,使开发人员能够有效地解决故障并优化代码以获得更好的性能。对于该项目,查看以下图片,看看我们如何轻松地访问文档。

重新查阅文档

重新查阅文档

错误报告:

'ObjectDelete' - wrong parameters count D1 PriceMarker.mq5      70      5

解决方案提示:

   built-in: bool ObjectDelete(long,const string)       D1 PriceMarker.mq5      70      5

当前情况:

   ObjectDelete("PrevCandle_Open");

从解决方案提示中可以看出,ObjectDelete函数期望包含两个参数,但目前我们只提供了一个。这种差异表明了问题所在,我们可以相应地解决。根据文档,MQL5中的ObjectDelete函数需要两个参数:

  1. 名称:要删除的对象名称,以字符串形式指定。
  2. 图表ID:要从中删除对象的图表的ID,指定为整数形式。如果省略,函数默认为当前图表。

为了解决此问题,我们需要确保在调用ObjectDelete时传递两个参数。通过这样做,我们使函数调用与预期的语法一致,从而消除了错误。

    解决方案:

    ObjectDelete(0, "PrevCandle_Open");

    现在,查看主代码,我们注意到ObjectDelete函数有多个实例。利用从研究中收集到的信息,我们可以对它们进行必要的调整。通过确保我们为每个实例提供两个所需的参数,我们可以有效地解决这些错误。

    参考以下图片,看看我们如何通过减少10个错误总数来取得显著进展。这表明我们在调试脚本方面取得了显著进展,并展示了基于适当的文档进行彻底检查和修正的重要性。

    ObjectDelete函数已正常

    ObjectDelete函数错误已解决

    完成!当您遇到报错时,每次尝试解决后点击“编译”按钮是一个好习惯。这样允许您检查错误是否仍然存在,并帮助识别其他余下的问题。

    现在,让我们继续来查看下一个报错。

    错误报告:

    'ObjectSetText' - undeclared identifier D1 PriceMarker.mq5      29      5
    'ObjectSetText' - undeclared identifier D1 PriceMarker.mq5      29      5
    ',' - unexpected token  D1 PriceMarker.mq5      29      28
    'labelName' - some operator expected    D1 PriceMarker.mq5      29      19
    '+' - illegal operation use     D1 PriceMarker.mq5      29      36
    ',' - unexpected token  D1 PriceMarker.mq5      29      69
    result of expression not used   D1 PriceMarker.mq5      29      43
    ',' - unexpected token  D1 PriceMarker.mq5      29      73
    expression has no effect        D1 PriceMarker.mq5      29      71
    ',' - unexpected token  D1 PriceMarker.mq5      29      82
    expression has no effect        D1 PriceMarker.mq5      29      76
    ')' - unexpected token  D1 PriceMarker.mq5      29      87
    expression has no effect        D1 PriceMarker.mq5      29      84

    这一次,我收集了多个报告,因为如果您来查看,会发现错误发生在同一行。这通常表明有一个共同的问题需要在代码的那部分解决。让我们更仔细地查看相关行,以定位潜在问题并明确必要的修正方法。

    解决方案提示:

    在这种情况下,我们遇到了以下错误:(未声明的标识符、期望的操作符、表达式无效以及意外的标记)。

    当前情况:

    ObjectSetText(labelName, label + ": " + DoubleToString(price, 2), 10, "Arial", clr);
                
    

    我查阅了文档,但没有找到有关ObjectSetText的任何引用。然而,当我开始输入ObjectSet...时,出现了几个建议,其中ObjectSetString是最合适的选择。最终证明该函数是正确的,并且按照预期工作。

    您可以通过以下内容看到我们付出的努力。

    ObjectSetText在文档中不可用

    ObjectSetText在文档中不可用


    ObjectSetString

    ObjectSetString

    根据文档,ObjectSetString函数的参数如下:

    bool  ObjectSetString(
       long                            chart_id,          // chart identifier
       string                          name,              // object name
       ENUM_OBJECT_PROPERTY_STRING     prop_id,           // property
       int                             prop_modifier,     // modifier
       string                          prop_value         // value
       );

    解决方案:

    通过进行必要的调整,就像我们应对第一个错误的方式,我们几乎能够解决所有问题。现在,只剩下几行需要修正。让我们专注于这些最后的调整,以完全清除剩余的错误。

     ObjectSetString(0, labelName, OBJPROP_TEXT, label + ": " + DoubleToString(price, 2));

    只剩下几个漏洞需要解决:

    还剩2个错误

    还剩2个错误

    最后,修复剩余的错误:

    ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, price); // Position label at the price level

    应该是OBJPROP_YDISTANCE,而不是OBJPROP_Y

    错误已清除,现在可以编译程序

    参考文档成功完成调试

    我们最终清理完成后的代码如下:

    //+------------------------------------------------------------------+
    //|                                               D1 PriceMarker.mq5 |
    //|                                Copyright 2024, Clemence Benjamin |
    //|             https://www.mql5.com/en/users/billionaire2024/seller |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2024, Clemence Benjamin"
    #property link      "https://www.mql5.com/en/users/billionaire2024/seller"
    #property version   "1.00"
    #property strict
    
    
    // Function to create a price line with a label
    void CreatePriceLine(string name, double price, color clr, string label)
    {
        // Create a horizontal line
        if(!ObjectCreate(0, name, OBJ_HLINE, 0, 0, price))
        {
            Print("Failed to create line: ", GetLastError());
            return;
        }
        
        // Set line properties
        ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
        ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
    
        // Create a label for the price
        string labelName = name + "_label";
        if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
        {
            Print("Failed to create label: ", GetLastError());
            return;
        }
    
        // Set label properties
        ObjectSetInteger(0, labelName, OBJPROP_XSIZE, 70);
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, clr);
        ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, price); // Position label at the price level
        ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 5);
        
        // Set the text of the label
        ObjectSetString(0, labelName, OBJPROP_TEXT, label + ": " + DoubleToString(price, 2));
    }
    
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    void OnInit()
    {
        // Get the previous D1 candle's open, high, low, and close prices
        datetime prevCandleTime = iTime(NULL, PERIOD_D1, 1);
        double openPrice = iOpen(NULL, PERIOD_D1, 1);
        double highPrice = iHigh(NULL, PERIOD_D1, 1);
        double lowPrice = iLow(NULL, PERIOD_D1, 1);
        double closePrice = iClose(NULL, PERIOD_D1, 1);
    
        // Draw labeled price lines for open, high, low, and close prices
        CreatePriceLine("PrevCandle_Open", openPrice, clrBlue, "Open");
        CreatePriceLine("PrevCandle_High", highPrice, clrRed, "High");
        CreatePriceLine("PrevCandle_Low", lowPrice, clrGreen, "Low");
        CreatePriceLine("PrevCandle_Close", closePrice, clrOrange, "Close");
    }
    
    //+------------------------------------------------------------------+
    //| Custom indicator deinitialization function                       |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
        // Remove the drawn price lines and labels upon indicator deinitialization
        ObjectDelete(0, "PrevCandle_Open");
        ObjectDelete(0, "PrevCandle_High");
        ObjectDelete(0, "PrevCandle_Low");
        ObjectDelete(0, "PrevCandle_Close");
        ObjectDelete(0, "PrevCandle_Open_label");
        ObjectDelete(0, "PrevCandle_High_label");
        ObjectDelete(0, "PrevCandle_Low_label");
        ObjectDelete(0, "PrevCandle_Close_label");
    }
    
    //+------------------------------------------------------------------+

    D1 PriceMarker是一个我们设计的MQL5脚本程序,用于通过在前一日(D1)K线图的最高价、最低价、开盘价和收盘价处绘制水平线来强化交易图表上的技术分析。在初始化时,脚本检索这四个价格点,并使用辅助函数以不同颜色(开盘价为蓝色,最高价为红色,最低价为绿色,收盘价为橙色)创建相应的价格线,每条线都附带一个标记文本,指示价格值。当脚本从图表中移除时,它会自动清理,删除所有绘制的线条和标签,确保为交易者提供一个整洁的界面。

    2. 使用AI进行调试:

    我知道每个人都渴望了解我们如何利用现有的AI模型,所以我会简要提及这个话题,以便之后进行更深入地探讨。幸运的是,这些模型中的大多数都能比我们更快、更准确地理解语法。然而,需要注意的是,它们有时会产生意想不到的结果,并且可能需要更密切地人工监控来指导它们的下一步行动。尽管如此,当合理地使用AI模型时,它们仍然是出色的工具,我通过审慎参考它们的建议解决了许多编码挑战。

    本质上,在编写AI提示时,您应该首先提供想要调试的程序。然后,通过利用从MetaEditor报告中复制错误行的能力,您可以将这些行粘贴到提示中,让AI协助调试过程。虽然这种方法可以取得成功的结果,但它也可能导致进一步的复杂情况,因此至关重要的一点是要批判性地斟酌,并在AI建议的基础上更进一步。我们之前取得的结果与有效使用其他调试工具应该产生的结果是一致的。

    复制错误

    将错误复制到AI提示中

    3. 访问论坛:

    与我们上述做法类似,论坛成员需要信息才能提供有价值的调试见解。就编码挑战进行探讨是向其他专家学习的绝佳方式。通过提交您的程序,您可以邀请其他人分享他们的明智建议和解决方案。这种协作方法不仅能帮助您解决问题,还能扩展每个人的知识和技能。

    在MetaTrader 5上的程序:

    D1 PriceMarker

    D1 PriceMarker脚本 


    结论

    在创建D1 PriceMarker脚本的过程中,团队合作在MQL5编程中被证明是开发稳健交易算法的一个重要实践。调试过程严重依赖于错误报告和全面的MQL5参考文档,使我们能够系统地识别和解决开发过程中遇到的问题。

    这种方法有助于解决具体错误,并促进了对编程环境更深入地理解。此外,我们简要提及了利用AI作为未来调试辅助工具的潜力,强调了其提供见解和建议的能力,这样可以简化调试过程。

    社区支持,特别是MQL5论坛,发挥了不可估量的作用,使我们能够向其他经验丰富的开发人员寻求建议并分享知识。通过采用这些协作和资源驱动的方法,我们可以显著提高调试工作的效率,最终在竞争激烈的算法交易领域中开发出更可靠、更具创新性的交易解决方案。交易顺利,朋友们!

    本文由MetaQuotes Ltd译自英文
    原文地址: https://www.mql5.com/en/articles/15325

    附加的文件 |
    D1_PriceMarker.mq5 (3.31 KB)
    最近评论 | 前往讨论 (8)
    Clemence Benjamin
    Clemence Benjamin | 13 10月 2024 在 04:52
    Alain Verleyen #:

    对初学者来说是篇好文章,但我认为有点啰嗦。一些评论 :

    • 您应该明确指出,当您有一个错误列表时,您总是需要从列表顶端的第一个错误开始检查。这是处理错误和避免在次要错误上浪费时间的最有效方法。
    • 您应该提供 "D1_PriceMarker.mq5 "的 "错误 "版本,这样人们就可以根据您的文章进行练习。

    • 这显然是一个逻辑错误,"Y_DISTANCE "是一个整数值(屏幕上的像素位置),而您提供的是一个双 "价格"。
    • 我几乎没有看到您在论坛上活跃,无论是提问还是回答问题。
    • 我不明白。合作部分 "在哪里?我错过了什么吗?

    感谢您的反馈,Alain Verleyen!我真心感谢您的真知灼见,尤其是作为一名版主,我同意清晰是至关重要的,尤其是对于初学者。

    1. 你说解决列表中第一个错误的重要性是完全正确的。
    2. 这是一个非常好的建议!虽然我没有在文章中包含源文件,但读者可以通过这个链接 找到并复制 " D1_PriceMarker.mq5 " " 错误 "版本。
    3. 感谢您指出有关Y_DISTANCE 的逻辑错误。
    4. 关于合作方面,我的目的是鼓励读者在评论或论坛上相互交流,分享自己的调试经验和解决方案。我会努力提高自己在论坛上的知名度。

    我非常重视你们的建设性批评和参与!这样的讨论对我们的集体成长和进步至关重要。

    Gerard William G J B M Dinh Sy
    Gerard William G J B M Dinh Sy | 13 10月 2024 在 08:52

    早上好

    算法交易者可能没有意识到。

    那就是,他们很少会花时间去编写神奇的指标或利润丰厚的 EA。

    现实情况是,他们主要花时间修正、改进代码,从而进行调试。

    如果没有强大的工具,他们很快就会花费数天时间来查找代码错误,而这些时间本来就已经很充裕了。

    Bazarbay83 Jumaev
    Bazarbay83 Jumaev | 16 5月 2025 在 10:05
    大家好,你们认为什么工具功能强大?
    Vitaly Muzichenko
    Vitaly Muzichenko | 16 5月 2025 在 11:16
    Bazarbay83 Jumaev #:
    大家好,你们认为什么乐器功能强大?

    鼓组。

    Vinicius Pereira De Oliveira
    Vinicius Pereira De Oliveira | 18 8月 2025 在 12:15
    Bazarbay83 Jumaev # 大家好,您认为哪个工具功能强大?

    您好,欢迎来到 MQL5 论坛!关于您的问题,不允许推荐市场产品。您必须自己进行研究。祝您好运😊

    使用经典机器学习方法预测汇率:逻辑回归(logit)模型和概率回归(probit)模型 使用经典机器学习方法预测汇率:逻辑回归(logit)模型和概率回归(probit)模型
    本文尝试构建一款用于预测汇率报价的EA。该算法以经典分类模型——逻辑回归与概率回归为基础。并利用似然比检验作为交易信号的筛选器。
    交易中的神经网络:运用形态变换器进行市场分析 交易中的神经网络:运用形态变换器进行市场分析
    当我们用模型分析市场形势时,我们主要关注蜡烛条。然而,人们早就知道烛条形态能有助于预测未来的价格走势。在本文中,我们将领略一种能将这两种方法集成的方式。
    交易中的神经网络:对比形态变换器 交易中的神经网络:对比形态变换器
    对比变换器在设计上基于单根烛条水平和整个形态来分析行情。这有助于提升行情趋势建模的品质。甚至,运用对比学习来统调烛条和形态的表示、促进自我调节,并提升预测的准确性。
    精通日志记录(第一部分):MQL5中的基础概念与入门步骤 精通日志记录(第一部分):MQL5中的基础概念与入门步骤
    欢迎开启另一段探索之旅!本文是一个特别系列的开篇之作,我们将逐步创建一个专为MQL5语言开发者量身定制的日志操作库。