English Русский Deutsch 日本語
preview
使用MQL5经济日历进行交易(第二部分):创建新闻交易面板

使用MQL5经济日历进行交易(第二部分):创建新闻交易面板

MetaTrader 5交易 | 1 七月 2025, 13:42
183 0
Allan Munene Mutiiria
Allan Munene Mutiiria

引言

本文将在前文第一部分基于MQL5的经济日历的基础上进行展开,当时我们专注于掌握检索和分析经济新闻事件所必需的函数。现在,我们将通过创建一个新闻面板来迈出下一步,该面板为交易者提供了一个便捷的界面,用于实时访问关键经济数据。这个面板将通过突出显示可能影响市场走势的相关新闻事件,帮助简化决策过程。我们将涵盖的主题包括:

  1. 设计面板
  2. 在MQL5中设置面板
  3. 结论

本文中,我们旨在通过为MQL5中的经济新闻提供一个有效的实时监控工具,来增强交易体验。


设计面板

面板的设计是使用MQL5经济日历来作为监控经济新闻事件之工具的关键步骤。我们的目标是创建一个用户友好且视觉上吸引人的界面,清晰简洁地呈现重要信息。一个结构良好的面板将使我们能够快速评估经济事件同我们交易策略的相关性和影响。

在设计面板时,我们需要首先确定需要显示的关键组件。这些组件通常包括事件名称、预定时间、受影响的货币、重要性级别以及事件的简要描述。为了提高可用性,我们将这些信息以表格形式组织,每一行代表一个不同的经济事件。我们打算使表格易于阅读,使用不同重要性级别的对比颜色,以便快速识别高影响力事件。

为了使面板更具吸引力,我们将选择视觉元素,如边框、背景和字体,以创建一个清爽、专业的外观。布局将便于导航,确保我们能够迅速找到我们需要的信息,而不会被过多的细节所淹没。通过保持设计直观和简单,我们将使交易者能够专注于根据面板中显示的经济事件做出明智的决策。面板将按上述方式排列,并具有以下所示的组件:

面板组件

在明确了目标之后,让我们深入了解自动化过程。在上一篇文章中,我们专注于掌握MQL5经济日历的函数,以有效地检索和分析经济新闻事件。如果您还没有,请参考该内容,以确保您在我们继续创建新闻面板时做好了充分的准备。让我们开始吧!


在MQL5中设置面板

在本节中,我们将专注于通过使用MQL5创建必要的面板元素来设置面板。首先,我们需要创建三个元素所需的函数:矩形标签、按钮和文本标签。这种方法将带来极大的好处,因为它允许我们在创建类似功能时重用相同的函数,消除了为每个新对象重复整个过程的麻烦。通过这样做,我们节省了时间和空间,使过程变得快速、简单,并保持代码片段简洁。

为了创建矩形标签,我们将创建一个接受十个参数的函数。这个函数将定义矩形的属性,例如它的位置、大小、颜色和样式,使我们能够根据面板设计要求定制标签的视觉外观。

//+------------------------------------------------------------------+
//|     Function to create rectangle label                           |
//+------------------------------------------------------------------+

bool createRecLabel(string objName, int xD, int yD, int xS, int yS,
                    color clrBg, int widthBorder, color clrBorder = clrNONE,
                    ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {

...
}

函数的标题说明了一切。这是一个名为“createRecLabel”的布尔函数,意味着它将返回两个布尔标志,在成功或失败的情况下分别返回true或false。为了便于理解其参数,让我们在下面逐一概述并解释它们。

  • “objName:” 这个参数代表矩形标签对象的唯一名称。它作为正在创建的图形元素的标识符。
  • “xD和yD:"这些参数决定了矩形标签将被定位的角落的X和Y距离。可以将它们视为定义矩形左上角相对于图表的坐标的参数。
  • “xS和yS:” 这些参数指定矩形的宽度和高度。“xS”值决定了矩形的水平宽度,而“yS”控制其垂直高度。

距离和大小

  • “clrBg:” “clrBg”参数代表矩形标签的背景颜色。选择一种与图表背景对比鲜明或与其他元素相辅相成的颜色。
  • “widthBorder:” 此参数定义矩形周围的边框宽度。如果需要边框,请设置正值;否则,使用零表示无边框。
  • “clrBorder:” 边框颜色的可选参数。如果需要边框,请指定颜色(例如,“clrNONE”表示无边框颜色)。
  • “borderType:” 指定矩形的边框类型。选项包括平面、浮雕或其他样式。对于简单的平面边框,请使用BORDER_FLAT
  • “borderStyle:” 如果选择平面边框,此参数确定线条样式(例如,实线、虚线)。使用STYLE_SOLID 表示连续线。

在函数签名中,您可能已经注意到某些参数已经初始化为某个值。初始化值表示在函数调用期间忽略该参数时将分配给该参数的默认值。例如,我们的默认边框颜色为无,这意味着如果在函数调用期间未指定颜色值,则不会为我们的矩形标签的边框应用颜色。 

在函数体内部,由大括号({})包围,我们定义我们的对象创建过程。

// Create a rectangle label object
if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) {
  Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
  return (false); // Return false if object creation fails
}

我们首先使用if语句检查对象是否未创建。使用ObjectCreate函数,这是一个返回类型为布尔值的函数,接受6个参数。此函数在指定的图表子窗口中创建具有指定名称、类型和初始坐标的对象。首先,我们指定图表窗口,0表示对象将在主窗口上创建。然后,我们提供对象名称。这是将唯一分配给特定对象的名称。我们想要创建的对象类型是OBJ_RECTANGLE_LABEL,表示用于创建和设计自定义图形界面的对象。然后,我们继续设定子窗口,0表示当前子窗口。最后,我们设定时间为零(0)和价格值为零(0),因为我们不会将它们附加到图表上,而是附加到图表窗口坐标上。使用像素设置映射。

如果对象创建失败,最终ObjectCreate函数返回false,显然没有继续进行的必要,我们返回错误。在这种情况下,我们报错信息和错误代码一并打印到日志中,并返回false。可能存在之前的报错信息,因此为了获取最新的报错信息,我们需要清除之前的报错。这是通过在我们的对象创建逻辑之前调用“ResetLastError”函数来实现的,这是MQL5的一个内置函数。

ResetLastError(); // Reset any previous error codes

该函数的目的是将预定义变量_LastError的值设置为零,该变量存储遇到错误的最后一个操作的错误代码。通过调用它,我们确保在进行下一步操作之前清除任何之前的错误代码。这一步是必要的,因为它允许我们独立处理新的错误,而不受之前错误状态的干扰。

如果到目前为止还没有返回,这意味着我们成功创建了对象,因此可以继续更新对象的属性。内置函数“ObjectSet...”设置相应对象属性的值。对象属性必须是datetime、整数、颜色、布尔值或字符类型。

// Set properties for the rectangle label
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle
ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color
ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type
ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object
ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

让我们聚焦到第一个属性设置的代码。

ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner

在这里,我们使用内置的ObjectSetInteger函数,并分别传递参数。 参数如下所述。

  • 图表ID:这是图表标识符。“0”表示当前图表(图表ID)。我们正在调整此图表中对象的属性。
  • 名称:这是对象的名称。“objName”表示分配给矩形标签对象的唯一名称。
  • 属性ID:这是对象属性的ID,其值可以是ENUM_OBJECT_PROPERTY_INTEGER枚举的值之一。OBJPROP_XDISTANCE指定我们正在修改X距离属性。
  • 属性值:这是属性的值。分配给“xD”的值决定了我们的矩形标签的左上角将从图表的左边缘水平向右(或如果为负数则向左)定位多远。

同样,我们使用相同的格式设置其他属性。OBJPROP_YDISTANCE配置矩形标签的Y距离属性。“yD”值决定了矩形标签的左上角将从图表的上边缘垂直定位多远。换句话说,它控制标签在图表区域内的垂直位置。这设置了从角落的Y距离。“OBJPROP_XSIZE”和“OBJPROP_YSIZE”分别设置矩形的宽度和高度。 

为了定位我们的对象,我们使用OBJPROP_CORNER属性来确定我们希望对象在图表窗口上的角落。

ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner

属性只能是4种类型之一:

  • CORNER_LEFT_UPPER:坐标中心位于图表的左上角。
  • CORNER_LEFT_LOWER:坐标中心位于图表的左下角。
  • CORNER_RIGHT_LOWER:坐标中心位于图表的右下角。
  • CORNER_RIGHT_UPPER:坐标中心位于图表的右上角。

在图形表达中,这就是我们能够得到的。

角落

其余属性都很简单。我们为它们添加了注释以便于理解。然后,我们只需使用ChartRedraw函数重新绘制图表,使更改自动生效,而无需等待价格报价或图表事件的变化。

ChartRedraw(0); // Redraw the chart

最后,我们返回true,表示对象的创建和属性更新成功。

return (true); // Return true if object creation and property settings are successful

负责在图表窗口上创建矩形对象的完整函数代码如下。

bool createRecLabel(string objName, int xD, int yD, int xS, int yS,
                    color clrBg, int widthBorder, color clrBorder = clrNONE,
                    ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {
    ResetLastError(); // Reset any previous error codes
    
    // Create a rectangle label object
    if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) {
        Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
        return (false); // Return false if object creation fails
    }
    
    // Set properties for the rectangle label
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle
    ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color
    ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type
    ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected
    
    ChartRedraw(0); // Redraw the chart
    
    return (true); // Return true if object creation and property settings are successful
}

创建按钮对象的方式也类似。创建按钮函数的代码如下所示。

//+------------------------------------------------------------------+
//|     Function to create button                                    |
//+------------------------------------------------------------------+

bool createButton(string objName, int xD, int yD, int xS, int yS,
                  string txt = "", color clrTxt = clrBlack, int fontSize = 12,
                  color clrBg = clrNONE, color clrBorder = clrNONE,
                  string font = "Arial Rounded MT Bold") {
    // Reset any previous errors
    ResetLastError();

    // Attempt to create the button object
    if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
        // Print an error message if creation fails
        Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError);
        return (false);
    }

    // Set properties for the button
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the button
    ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the button
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the button
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color
    ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size
    ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name
    ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color
    ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Button state (not pressed)
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

    // Redraw the chart to display the button
    ChartRedraw(0);

    return (true); // Button creation successful
}

代码的不同之处在于,矩形对象不能包含文本,但如果有需要,按钮对象确实可以包含描述按钮功能的文本。因此,对于输入参数,考虑文本属性,在我们的情况下是文本值、颜色、字体大小和字体名称。按钮的边框类型是静态的,因此我们去掉它的属性,只保留边框颜色。 

我们创建的对象类型是OBJ_BUTTON,表示创建一个按钮图形对象。它的锚点以像素设置。我们保留的边框属性是边框颜色,并用文本输入属性替换其余部分。

最后,我们需要最后一个元素的函数,即文本标签。文本标签消除了对背景对象的需求,因此它的实现比其他函数要简单得多。我们只需要文本,因此专注于文本属性。它的代码如下。

//+------------------------------------------------------------------+
//|     Function to create text label                                |
//+------------------------------------------------------------------+

bool createLabel(string objName, int xD, int yD,
                 string txt, color clrTxt = clrBlack, int fontSize = 12,
                 string font = "Arial Rounded MT Bold") {
    // Reset any previous errors
    ResetLastError();

    // Attempt to create the label object
    if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) {
        // Print an error message if creation fails
        Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError);
        return (false);
    }

    // Set properties for the label
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color
    ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size
    ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Label state (not active)
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

    // Redraw the chart to display the label
    ChartRedraw(0);

    return (true); // Label creation successful
}

这段代码结构与按钮函数的主要区别在于对象大小和边框属性。在函数签名中,我们去掉了对象大小以及边框属性。我们定义对象类型为OBJ_LABEL,表示我们根据定义的标签坐标在图表窗口上绘制标签。最后,我们去掉了大小和边框参数,仅此而已。就是这么简单。

现在我们已经拥有了创建图形用户界面(GUI)所需的函数,让我们使用它们来创建面板。我们需要对象的名称,为了便于管理对象名称的交互,定义宏会更加容易。 

#define MAIN_REC "MAIN_REC"

我们使用#define关键字定义一个名为"MAIN_REC"的宏,其值为"MAIN_REC",以便轻松存储我们的主矩形基础名称,而无需在每次创建级别时重复输入名称,这大大节省了我们的时间,并减少了错误提供名称的可能性。因此,基本上,宏在编译期间用于文本替换。

我们的代码将主要基于EA初始化函数,因为我们希望在初始化实例时创建面板。因此OnInit事件处理程序将包含大部分代码。  

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit(){

   ...
   
   return(INIT_SUCCEEDED);
}

OnInit函数是一个事件处理程序,它在EA初始化实例时被调用,用于执行必要的初始化操作。 

然后我们通过输入其名称并提供其参数来调用创建矩形标签的函数。

//--- Create main rectangle label for the dashboard panel
createRecLabel(MAIN_REC,50,50,740,410,clrSeaGreen,1);

在这里,我们的矩形名称是"MAIN_REC",正如宏定义中所定义的那样。沿着x轴(时间与日期刻度)从图表窗口的左上角的距离是50像素,沿着y轴(价格刻度)的距离是50像素。宽度为740像素,高度为410像素。我们选择背景颜色为海洋绿,边框宽度为1,其余参数默认。为了大致获取像素范围,你可以将图表缩放到0,两个十字准线坐标之间的柱数等于水平刻度上的像素数。举个例子,这就是我们所说的。

CROSS-HAIR

其他参数已被省略,这意味着将自动应用默认值。也就是说,边框类型将是平面的,线条样式将是连续的实线。编译后,我们目前有如下结果。

主面板

为了创建子框架,我们再次明确地声明相应的宏。

#define SUB_REC1 "SUB_REC1"
#define SUB_REC2 "SUB_REC2"

然后我们调用相同的函数来创建子框架。我们希望这些框架位于基础面板框架内,因此需要使用略微不同的颜色。为了实现这一点,我们使用了白色和绿色,并设置了3和5像素的边距。

//--- Create sub-rectangle labels within the main panel for different sections
createRecLabel(SUB_REC1,50+3,50+30,740-3-3,410-30-3,clrWhite,1);
createRecLabel(SUB_REC2,50+3+5,50+30+50+27,740-3-3-5-5,410-30-3-50-27-10,clrGreen,1);

此处,我们在主面板内设置了两个子面板,“SUB_REC1”和“SUB_REC2”,从视觉上组织和分隔内容。使用“createRecLabel”函数,我们通过在主矩形“MAIN_REC”的左侧和右侧添加3像素的偏移量,以及在顶部添加30像素的偏移量,来定位“SUB_REC1”——有效地在主面板内创建了一个带框的区域。我们将其宽度定义为“740-3-3”,以适应减少的边距,其高度定义为“410-30-3”,以在上方和下方留出空间,使其能够整齐地嵌套在主矩形内。这个子面板设置为白色,与主面板的海洋绿色形成对比,以增强视觉清晰度。

接下来,我们使用“createRecLabel”添加“SUB_REC2”,这是“SUB_REC1”内的一个额外部分,并通过更精细的偏移量来实现有组织的分层布局。为了实现这一点,我们将起始X坐标设置为“50+3+5”,将其进一步定位在“SUB_REC1”内,以在视觉上将其定义为这个子模块内的一个独立区域。我们将Y坐标设置为“50+30+50+27”,以考虑主矩形和第一个子矩形的垂直偏移量。宽度“740-3-3-5-5”使“SUB_REC2”精确地适应剩余的水平空间,而高度“410-30-3-50-27-10”允许一个平衡且分离的区域。将“SUB_REC2”设置为绿色,增加了强烈的对比度,表明它是一个将显示关键数据的区域。这种对矩形的仔细分层对于建立一个结构化且视觉上清晰简洁的面板至关重要。编译后,我们得到以下结果:

面板框架

到目前为止,我们已经完成了面板的框架、边距和边界的设置。然后我们继续添加其他面板工具、它们的属性和效果。首先,让我们给面板一个标题。

#define HEADER_LABEL "HEADER_LABEL"

//---

//--- Create the header label with text "MQL5 Economic Calendar"
createLabel(HEADER_LABEL,50+3+5,50+5,"MQL5 Economic Calendar",clrWhite,15);

在这里,我们定义标签标识符“HEADER_LABEL”,其值为“HEADER_LABEL”,以便在代码中一致且方便地引用这个特定的标签。这个标签将作为我们面板的标题,突出显示标题“MQL5经济日历”。

然后,使用“createLabel”函数,我们在指定位置创建标题标签。我们将X坐标设置为“50+3+5”,将其略微定位在主面板边缘的右侧,以确保它对齐在“SUB_REC1”矩形内,并且不与任何边距重叠。Y坐标“50+5”将其放置在主矩形顶部边缘下方几像素处,确保可读性。为了提高可见性,我们将文本颜色设置为白色,字体大小为“15”,创建一个醒目且显眼的标题,标明面板的用途。这个标题将锚定面板的视觉设计,立即向用户传达其用途。这就是我们得到的。

主题

成功了。我们现在可以继续创建面板标题了。为此,我们将使用最简单的方法,即定义标题名称,并将它们放置在一个数组中,然后使用循环动态放置它们,因为它们只在一行中。然而,我们还将不得不分别定义按钮的大小,因为它们将因各个标题的长度而有不同的宽度。以下是我们将使用的逻辑。

string array_calendar[] = {"Date","Time","Cur.","Imp.","Event","Actual","Forecast","Previous"};
int buttons[] = {80,50,50,40,281,60,70,70};

我们定义了两个数组,用于在面板中组织标签和按钮的尺寸。第一个数组“array_calendar”包含了我们将会显示的每个列标题的字符串,指定了信息的类型:“Date”(日期)、“Time”(时间)、“Cur.”(货币)、“Imp.”(影响/重要性)、“Event”(事件)、“Actual”(实际值)、“Forecast”(预测值)和“Previous”(前值)。每个字符串代表一个数据类别的标签,帮助我们理解面板的每个部分将显示什么内容。

第二个数组“buttons”包含了整数,表示与“array_calendar”中相应列相关联的每个按钮的宽度(以像素为单位)。这些宽度是根据每种数据类型量身定制的,以确保布局保持对齐并且视觉上井然有序。例如,“Date”列的“80”像素可以容纳较长的日期格式,而“Time”和“Cur.”列的宽度则设置为“50”像素,因为它们需要的空间较少。这两个数组共同帮助简化了面板列标题和按钮的创建过程,为后续的用户界面(UI)元素奠定了结构化的基础。从这里开始,我们就可以使用循环动态创建标题了。

#define ARRAY_CALENDAR "ARRAY_CALENDAR"

//---

//--- Initialize starting x-coordinate for button positioning
int startX = 59;
   
//--- Loop through the array_calendar elements to create buttons
for (int i=0; i<ArraySize(array_calendar); i++){
   //--- Create each button for calendar categories
   createButton(ARRAY_CALENDAR+IntegerToString(i),startX,132,buttons[i],25,array_calendar[i],clrWhite,13,clrGreen,clrNONE,"Calibri Bold");
   startX += buttons[i]+3; //--- Update x-coordinate for the next button
}

在这里,我们根据“array_calendar”中的元素初始化并定位按钮,以标记我们面板中的每个类别。首先,我们为日历按钮系列定义了标识符“ARRAY_CALENDAR”。然后,我们将“startX”设置为“59”,作为初始x坐标,这将把第一个按钮水平定位在面板上。

接下来,我们使用for循环遍历 “array_calendar”中的每个项目以创建一个按钮。在每次迭代中,我们调用“createButton”函数,并通过将循环索引附加到“ARRAY_CALENDAR”来为每个按钮传递一个唯一的ID。这确保了每个按钮ID都是唯一的,引用了诸如“Date”(日期)、“Time”(时间)、“Cur.”(货币)等类别。我们指定“startX”位置,并使用“buttons”中的值来定义每个按钮的宽度,确保与相应数据类别对齐。每个按钮还接收样式属性,包括字体颜色(白色)、字体大小(“13”)以及背景颜色(按钮为绿色,边框无色),并设置为“Calibri Bold”字体。在创建每个按钮后,我们通过加上当前按钮的宽度加上“3”像素的边距来调整“startX”,以便在下一次迭代中均匀地间隔开按钮。编译后,我们得到了以下输出。

标题

创建完标题后,我们现在需要创建另一个子模块,用于显示时间、已识别新闻事件的数量以及影响级别。我们将首先从系列的前一部分获取新闻事件。

//--- Declare variables for tracking news events and status
int totalNews = 0;
bool isNews = false;
MqlCalendarValue values[]; //--- Array to store calendar values

//--- Define start and end time for calendar event retrieval
datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H12);
datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H12);

//--- Set a specific country code filter (e.g., "US" for USD)
string country_code = "US";
string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);

//--- Retrieve historical calendar values within the specified time range
int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL);

//--- Print the total number of values retrieved and the array size
Print("TOTAL VALUES = ",allValues," || Array size = ",ArraySize(values));

这使我们能够获取从MQL5经济日历检索到的历史事件值,因此我们不仅可以打印它们,还可以将它们显示在面板上。这是我们使用的代码逻辑。

#define TIME_LABEL "TIME_LABEL"

//---

//--- Create label displaying server time and total number of news events found
createLabel(TIME_LABEL,70,85,"Server Time: "+TimeToString(TimeCurrent(),
           TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
           IntegerToString(allValues),clrBlack,14,"Times new roman bold");

在这里,我们创建一个标签来显示当前服务器时间以及检索到的新闻事件总数。首先,我们定义标识符“TIME_LABEL”,以便在我们的面板内唯一引用此标签。

接下来,我们调用“createLabel”函数来生成标签本身。我们通过提供坐标“70”和“85”来指定标签的位置,这些坐标决定了标签在面板上的显示位置。标签的文本是通过使用TimeToString函数动态构建的,该函数将TimeCurrent函数检索到的当前服务器时间以日期和秒的格式进行格式化。我们将这个格式化的时间与字符串“||| Total News: ”连接起来,并使用IntegerToString函数将包含新闻事件数量的变量“allValues”转换为字符串。这创建了一个全面的标签,显示了服务器时间和找到的新闻事件总数。我们将标签样式设置为黑色,字体大小为14,并使用“Times New Roman bold”字体以确保清晰可见。通过相同的逻辑,我们还创建了影响标签。

#define IMPACT_LABEL "IMPACT_LABEL"

//---

//--- Create label for displaying "Impact" category header
createLabel(IMPACT_LABEL,70,105,"Impact: ",clrBlack,14,"Times new roman bold");

编译后,我们得到以下输出。

值和影响标签

成功了。我们现在需要进一步显示各自的影响按钮及其对应的标签和颜色,以便用户能够了解每个影响级别所代表的含义。 

//--- Define labels for impact levels and size of impact display areas
string impact_labels[] = {"None", "Low", "Medium", "High"};
int impact_size = 100;

//--- Loop through impact levels to create buttons for each level
for (int i=0; i<ArraySize(impact_labels); i++){
   color impact_color = clrBlack, label_color = clrBlack; //--- Default colors for label and button

   //--- Assign color based on impact level
   if (impact_labels[i] == "None"){label_color = clrWhite;}
   else if (impact_labels[i] == "Low"){impact_color = clrYellow;}
   else if (impact_labels[i] == "Medium"){impact_color = clrOrange;}
   else if (impact_labels[i] == "High"){impact_color = clrRed;}

   //--- Create button for each impact level
   createButton(IMPACT_LABEL+string(i),140+impact_size*i,105,impact_size,25,impact_labels[i],label_color,12,impact_color,clrBlack);
}

在这里,我们定义了与经济事件相关联的不同影响级别的标签以及这些影响指示器的显示区域大小。首先,我们声明了一个名为“impact_labels”的数组,其中包含代表各种影响级别的字符串:“None”(无)、“Low”(低)、“Medium”(中)和“High”(高)。此外,我们初始化了一个整数变量“impact_size”,其值为100,它决定了将为每个影响级别创建的按钮的宽度。

接下来,我们进入一个循环,该循环使用ArraySize函数来确定影响级别的总数,从而遍历“impact_labels”数组。在这个循环中,我们首先使用黑色设置按钮和标签的默认颜色。然后,使用条件语句根据当前的影响级别分配特定的颜色。 如果影响级别是“None”,我们将“label_color”更改为白色。如果级别是“Low”,我们将“impact_color”设置为黄色。对于“Medium”,我们分配“impact_color”为橙色,而对于“High”,我们指定“impact_color”为红色。最后,我们调用“createButton”函数为每个影响级别生成一个按钮,使用“140 + impact_size * i”计算的动态x坐标进行定位,保持固定的y坐标105,并相应地提供尺寸和颜色。以下是当前的里程碑。

IMPACT BUTTONS

成功了。我们现在可以继续将实际的日历数据添加到面板中。然而,在此之前,我们需要对第二个子面板进行分区,以便为面板塑造更专业化的视觉效果,而不是仅仅在其中放置数据。我们通过以下代码来实现这一点。

//--- Limit the total number of values to display
int valuesTotal = (allValues <= 11) ? allValues : 11;

//--- Initialize starting y-coordinate for displaying news data
int startY = 162;

//--- Loop through each calendar value up to the maximum defined total
for (int i = 0; i < valuesTotal; i++){

   //--- Set alternating colors for each data row holder
   color holder_color = (i % 2 == 0) ? C'213,227,207' : clrWhite;

   //--- Create rectangle label for each data row holder
   createRecLabel(DATA_HOLDERS+string(i),62,startY-1,716,26,holder_color,1,clrBlack);

   //--- Increment y-coordinate for the next row of data
   startY += 25;
   Print(startY); //--- Print current y-coordinate for debugging
}

我们通过定义一个名为“valuesTotal”的整数变量来限制在面板中显示的值的总数。我们使用一个条件(ternary)运算符来检查“allValues”是否小于或等于“11”。如果是,我们将“valuesTotal”设置为“allValues”;否则,我们将其设置为“11”。这种方法将确保我们不会尝试显示超过“11”个新闻事件,使我们的面板保持整洁且易于管理。

接下来,我们初始化一个整数变量“startY”,其值为“162”,这将作为在面板上定位新闻数据的起始y坐标。然后,进入一个从“0”到“valuesTotal”的循环,有效地处理我们打算显示的每个日历值。在这个循环中,我们根据当前索引“i”使用交替模式定义每个行容器的颜色。如果“i”是偶数,我们将“holder_color”设置为浅灰色,用“C'213,227,207'”表示;如果“i”是奇数,我们将其设置为白色。在确定颜色后,我们调用“createRecLabel”函数为每个数据行容器生成一个矩形标签,定位在x轴的“62”处,y轴的“startY - 1”处,宽度为“716”,高度为“26”,边框颜色为黑色。最后,我们将“startY”增加“25”,以调整下一行数据的y坐标,确保每个条目依次显示。为了调试目的,打印当前的“startY”值,使我们能够跟踪每个数据行在创建时的垂直位置。以下是当前的里程碑。

数据容器

你可能已经注意到,在创建数据容器子框架时,我们使用了一个宏变量“DATA_HOLDERS”。以下是我们的定义方式。

#define DATA_HOLDERS "DATA_HOLDERS"
#define ARRAY_NEWS "ARRAY_NEWS"

我们还定义了“ARRAY_NEWS”宏,以便于在数据容器中映射与列标题相关的特定数据。为了填充数据,我们需要为每个选定的容器,遍历特定事件值的所有数据并获取其数据,这些数据将被显示。因此,这将在第一个循环中完成,代码逻辑如下。

//--- Initialize starting x-coordinate for each data entry
int startX = 65;

//--- Loop through calendar data columns
for (int k=0; k<ArraySize(array_calendar); k++){

   MqlCalendarEvent event; //--- Declare event structure
   CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID

   MqlCalendarCountry country; //--- Declare country structure
   CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID

   //--- Print event details for debugging
   Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time);

   //--- Skip event if currency does not match the selected country code
   // if (StringFind(_Symbol,country.currency) < 0) continue;

   //--- Prepare news data array with time, country, and other event details
   string news_data[ArraySize(array_calendar)];
   news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date
   news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time
   news_data[2] = country.currency; //--- Event country currency

   //--- Determine importance color based on event impact
   color importance_color = clrBlack;
   if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;}
   else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;}
   else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;}

   //--- Set importance symbol for the event
   news_data[3] = ShortToString(0x25CF);

   //--- Set event name in the data array
   news_data[4] = event.name;

   MqlCalendarValue value; //--- Declare calendar value structure
   CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values

   //--- Populate actual, forecast, and previous values in the news data array
   news_data[5] = DoubleToString(value.GetActualValue(),3);
   news_data[6] = DoubleToString(value.GetForecastValue(),3);
   news_data[7] = DoubleToString(value.GetPreviousValue(),3);

   //--- Create label for each news data item
   if (k == 3){
      createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri");
   }
   else {
      createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri");
   }

   //--- Increment x-coordinate for the next column
   startX += buttons[k]+3;
}

在这里,我们初始化一个名为“startX”的整数变量,其值为65,这将作为定位与日历事件相关的每个数据条目的起始x坐标。然后,我们进入一个循环,使用索引“k”遍历“array_calendar”中的每一列。在这个循环中,我们声明一个类型为MqlCalendarEvent的结构变量“event”,它将用于保存特定日历事件的详细信息。我们通过调用CalendarEventById函数并传递“values”数组中的事件ID来检索事件详细信息,并将结果存储在“event”中。

接下来,我们声明另一个类型为MqlCalendarCountry的结构变量“country”,用于保存与日历事件相关的国家信息。我们使用CalendarCountryById函数根据事件的国家ID填充“country”中的详细信息。为了调试目的,我们打印出关键事件详细信息,如事件名称、其重要性级别(使用EnumToString函数转换为字符串)、国家名称以及存储在“values[i].time”中的事件时间。

然后,准备一个名为“news_data”的字符串数组,其大小与“array_calendar”相同,用于存储与事件相关的信息。“news_data”的第一个元素设置为事件日期,使用带有“TIME_DATE”标志的TimeToString函数将其格式化为字符串。第二个元素捕获事件时间,使用“TIME_MINUTES”标志进行格式化。第三个元素存储事件国家的货币。

接下来,我们根据事件的影响级别确定事件的重要性颜色,初始化一个变量“importance_color”为黑色。我们检查“event.importance”的值,并根据其值(低、中或高),分配适当的颜色:低为黄色,中为橙色,高为红色。

我们还将“news_data”的第四个元素设置为一个表示事件重要性级别的符号,使用“ShortToString(0x25CF)”创建一个实心圆。第五个元素分配从“event.name”检索到的事件名称。

为了获取事件的实际值、预测值和前值,我们声明另一个类型为MqlCalendarValue的结构变量“value”,并使用CalendarValueById函数根据存储在“values[i].id”中的事件ID填充此结构。“news_data”的第六、第七和第八个元素分别用实际值、预测值和前值填充,使用DoubleToString函数将其格式化为三位小数。

最后,我们使用“createLabel”函数为每个新闻数据项创建一个标签。如果“k”等于“3”,我们应用重要性颜色;否则,我们默认为黑色。每个标签的x坐标由“startX”确定,然后我们通过加上“buttons”数组中的按钮宽度来增加“startX”,确保每列数据都正确定位以便清晰显示。编译后,我们得到以下输出。

填充后面板

成功了。我们现在创建了一个MQL5经济日历面板,该面板在图表上显示新闻数据,便于参考。到目前为止,我们实现了获取所有的数据。在本系列的后续部分中,我们将通过集成过滤器、实现实时数据更新以及将新闻数据用于交易目的来改进面板。


结论

总之,至此我们成功地奠定了MQL5经济日历的基础,通过构建一个交互式面板,以用户友好的界面显示关键经济事件。 通过实现诸如日历数据检索、基于其重要性的事件视觉分类以及直观的标签等功能,我们可以了解重要的市场动态。这一初步成果不仅增强了用户体验,而且为将我们的面板提升到更高水平,进一步增强其功能,夯定了坚实的基础。

在本系列文章的后续部分,我们将为程序集成其他功能,例如新闻过滤器,以帮助我们在MQL5中专注于同策略相关性最强的信息。我们还将实现实时更新,以确保面板反映最新的经济数据。此外,我们将专注于面板的互动响应性,使其能够完美适配不同的屏幕尺寸和用户交互。最终,我们旨在利用这些数据来促进交易决策的合理性,将经济日历转变为一个强大的工具,供交易者利用市场波动获利。敬请期待。

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

附加的文件 |
交易中的神经网络:受控分段(终章) 交易中的神经网络:受控分段(终章)
我们继续上一篇文章中开启的工作,使用 MQL5 构建 RefMask3D 框架。该框架旨在全面研究点云中的多模态互动和特征分析,随后基于自然语言提供的描述进行目标对象识别。
重构经典策略(第十一部分)移动平均线的交叉(二) 重构经典策略(第十一部分)移动平均线的交叉(二)
移动平均线和随机振荡器可用于生成趋势跟踪交易信号。然而,这些信号只有在价格行为发生之后才会被观察到。我们可以有效地利用人工智能克服技术指标中这种固有的滞后性。本文将教您如何创建一个完全自主的人工智能驱动型EA,这种方式可以改进您现有的任何交易策略。即使是最古老的交易策略也可以被改进。
从基础到中级:数组和字符串(一) 从基础到中级:数组和字符串(一)
在今天的文章中,我们将开始探索一些特殊的数据类型。首先,我们将定义什么是字符串,并解释如何使用一些基本过程。这将使我们能够处理这类数据,这可能很有趣,尽管有时对初学者来说有点困惑。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
基于Python和MQL5的特征工程(第二部分):价格角度 基于Python和MQL5的特征工程(第二部分):价格角度
在MQL5论坛上,有许多帖子询问如何计算价格变化的斜率。本文将展示一种计算任意交易市场中价格变化所形成角度的可行方法。此外,我们还将探讨为这项新特征工程投入额外精力和时间是否值得。我们将研究价格斜率是否能在预测M1时间框架下的USDZAR货币对时,提高我们人工智能(AI)模型的准确性。