English Русский Deutsch 日本語
preview
使用MQL5经济日历进行交易(第一部分):精通MQL5经济日历的功能

使用MQL5经济日历进行交易(第一部分):精通MQL5经济日历的功能

MetaTrader 5交易 | 17 六月 2025, 14:27
54 0
Allan Munene Mutiiria
Allan Munene Mutiiria

概述

在本文中,我们探讨了MetaQuotes语言5(MQL5)经济日历的强大功能以及如何将它们整合到算法交易中。集成在交易终端MetaTrader 5中的经济日历是交易者的一个关键工具,它提供了可能对市场走势产生重大影响的基本新闻和数据。通过了解如何检索和解析这些信息,我们可以在预测市场对经济事件的反应方面获得优势,并相应地调整我们的交易策略。

我们将从对MQL5经济日历的概述开始,涵盖其关键组成部分以及运作方式。接下来,我们将专注于在MQL5中的实现,展示如何通过编程访问并在图表上显示新闻事件。最后,我们将进行经验总结并阐述将经济日历整合到交易系统中的好处。以下是构成全文的主要内容:

  1. MQL5经济日历概述
  2. 在MQL5中的实现
  3. 结论

阅读完本文后,您将掌握相关知识,能够开发一款MQL5的EA,可以在您的交易策略中有效运用MQL5经济日历。那么,让我们开始吧。


MQL5经济日历概述

MQL5 经济日历是一款出色的工具,能够为交易者提供关于可能对金融市场产生重大影响的关键经济事件的最新、连贯信息。这款工具非常实用,而且由于它直接嵌入在 MetaTrader 5 平台中,使用起来更加便捷。

经济日历为交易者提供了对各种可能影响金融市场的即将发生事件的全面概览。它涵盖了从利率决策日期、关于通货膨胀的报告、国内生产总值(GDP)数据到就业统计等方方面面。由于这些事件都可能对市场产生重大影响——尤其是在货币、大宗商品和股票市场——因此,这款日历对于短期和长期交易者来说都是必不可少的工具。

要打开日历,请导航到任务栏,选择“视图”,然后选择“工具箱”。以下是打开工具箱的示意图。

打开工具箱

打开工具箱窗口后,导航至“日历”(Calendar)选项卡并点击它。这样将打开日历窗口,该窗口应显示如下内容。

打开日历

值得一提的是,MQL5经济日历会以有序的方式列出与市场相关的即将发生的事件,并根据这些事件预期产生的影响进行排序。它用“低”、“中”或“高”这样清晰、明确的标签来表征每个事件可能产生的影响程度。因此,交易者可以很容易地从日历中快速了解哪些即将发生的事件具有高度重要性,哪些则不然。该日历还能很好地根据事件与货币的相关性进行筛选。尽管事件是按照相关性进行组织和颜色编码的,但数量并不多,交易者不会因试图考虑所有事件而感到应接不暇。因此,筛选机制确保交易者不会被无关的数据所淹没,使他们能够专注于可能直接影响其未平仓头寸或交易策略的新闻。

MQL5经济日历的核心优势之一在于它与MQL5的集成,这使得交易者能够以编程方式访问经济数据,并将其整合到他们的EA或自动化交易系统中。利用其预定义的函数,我们可以检索事件名称、预定时间、国家以及预测值或实际值等数据。这一功能使交易者能够开发出能够自动对重大新闻事件做出反应的系统,无论是关闭头寸以避免波动,还是根据预测进行交易,或是调整止损和止盈水平。这种程度的自动化确保了交易策略始终能够对最新的经济动态做出响应,而无需人工干预。为了总结MQL5经济日历的关键功能,以下是一个详细的可视化展示。

日历数据

从图中我们可以看到,数据展示有8列。第一列包含事件发生的时间,第二列包含货币符号,第三列包含事件名称,第四列包含新闻数据的优先级或重要性级别,第五列包含数据周期,而第六、七、八列则分别包含实际值、预测值和先前值或修正值。

当然,并非所有数据对交易者来说都至关重要,因此交易者可以通过四种方式筛选掉不需要的数据。第一种是按时间筛选,例如,如果交易者对已发布的数据不感兴趣。第二种是按货币筛选,第三种是按国家筛选。比如,如果交易者正在交易“澳元/美元”(AUDUSD)货币对,那么对这对货币产生重大影响的新闻就是直接涉及澳大利亚或美国的新闻。因此,中国的新闻对该货币对没有显著影响。

最后一种是重要性或优先级筛选,它有助于按影响程度对新闻进行分类。要应用任何筛选条件,只需在日历字段内右键单击,并相应地应用筛选即可。以下再次进行了说明。

筛选应用

因此,通过使用MQL5经济日历,交易者能够在具有影响力的市场事件发生之前更好地制定交易计划。可以通过两种方式来实现这一点:一是手动在MetaTrader 5平台上查看日历,二是通过基于日历数据的自动化交易策略。这种设置方式为交易者清晰理解MQL5经济日历以及掌握如何在MetaTrader 5交易系统中运用该日历奠定了基础,从而助力交易者制定更清晰的交易计划和执行更明确的交易操作。


在MQL5中的实现

要创建一个EA,在您的MetaTrader 5终端上,点击工具(Tools)选项卡并检查MetaQuotes语言编辑器,或者直接按键盘上的F4键。另外,您也可以点击工具栏上的IDE(集成开发环境)图标。这样就会打开MetaQuotes语言编辑器环境,该环境允许用户编写自动交易、技术指标、脚本和函数库。

打开MetaEditor

打开MetaEditor后,在工具栏上,点击“文件”选项卡,然后勾选“新建文件”,或者直接按CTRL + N键,以创建一个新文档。或者,您也可以点击工具栏选项卡上的“新建”图标。这将弹出一个MQL向导窗口。

新建EA

在弹出的向导中,选中EA(模板),然后单击下一步。

MQL 向导

在EA的一般属性中,在名称部分,提供您的文件名称。请注意,如果要指定或创建一个不存在的文件夹,您需要在EA名称前使用反斜杠。例如,这里我们默认有“Experts\”。这意味着我们的EA将被创建在Experts文件夹中,我们可以去那里找。其他部分相对直观,但您可以按照向导底部的链接了解如何精准地执行该过程。

新的EA名称

在输入您希望的EA文件名后,依次点击“下一步”、再“下一步”,然后点击“完成”。在完成上述所有操作后,我们现在可以开始编写和实现我们的策略了。

首先,我们从定义一些关于EA的基础数据开始。包括EA的名称、版权信息以及指向MetaQuotes网站的链接。我们还指定了EA的版本号,设置为“1.00”。

//+------------------------------------------------------------------+
//|                                    MQL5 NEWS CALENDAR PART 1.mq5 |
//|      Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader. |
//|                                     https://forexalgo-trader.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader"
#property link      "https://forexalgo-trader.com"
#property description "MQL5 NEWS CALENDAR PART 1"
#property version   "1.00"

到目前为止,我们首先需要做的就是了解用于处理经济日历的数据结构。这些结构共有三种:MqlCalendarCountry用于设置描述国家的信息,MqlCalendarEvent用于设置描述事件的信息,以及MqlCalendarValue用于设置事件值的信息。它们的结构方法如下所述。

日历国家结构:

struct MqlCalendarCountry 
  { 
   ulong                               id;                    // country ID (ISO 3166-1) 
   string                              name;                  // country text name (in the current terminal encoding) 
   string                              code;                  // country code name (ISO 3166-1 alpha-2) 
   string                              currency;              // country currency code 
   string                              currency_symbol;       // country currency symbol 
   string                              url_name;              // country name used in the mql5.com website URL 
  };

日历事件结构:

struct MqlCalendarEvent 
  { 
   ulong                               id;                    // event ID 
   ENUM_CALENDAR_EVENT_TYPE            type;                  // event type from the ENUM_CALENDAR_EVENT_TYPE enumeration 
   ENUM_CALENDAR_EVENT_SECTOR          sector;                // sector an event is related to 
   ENUM_CALENDAR_EVENT_FREQUENCY       frequency;             // event frequency 
   ENUM_CALENDAR_EVENT_TIMEMODE        time_mode;             // event time mode 
   ulong                               country_id;            // country ID 
   ENUM_CALENDAR_EVENT_UNIT            unit;                  // economic indicator value's unit of measure 
   ENUM_CALENDAR_EVENT_IMPORTANCE      importance;            // event importance 
   ENUM_CALENDAR_EVENT_MULTIPLIER      multiplier;            // economic indicator value multiplier 
   uint                                digits;                // number of decimal places 
   string                              source_url;            // URL of a source where an event is published 
   string                              event_code;            // event code 
   string                              name;                  // event text name in the terminal language (in the current terminal encoding) 
  };

日历数值结构:

struct MqlCalendarValue 
  { 
   ulong                               id;                    // value ID 
   ulong                               event_id;              // event ID 
   datetime                            time;                  // event date and time 
   datetime                            period;                // event reporting period 
   int                                 revision;              // revision of the published indicator relative to the reporting period 
   long                                actual_value;          // actual value multiplied by 10^6 or LONG_MIN if the value is not set 
   long                                prev_value;            // previous value multiplied by 10^6 or LONG_MIN if the value is not set 
   long                                revised_prev_value;    // revised previous value multiplied by 10^6 or LONG_MIN if the value is not set 
   long                                forecast_value;        // forecast value multiplied by 10^6 or LONG_MIN if the value is not set 
   ENUM_CALENDAR_EVENT_IMPACT          impact_type;           // potential impact on the currency rate 
  //--- functions checking the values 
   bool                         HasActualValue(void) const;   // returns true if actual_value is set 
   bool                         HasPreviousValue(void) const; // returns true if prev_value is set 
   bool                         HasRevisedValue(void) const;  // returns true if revised_prev_value is set 
   bool                         HasForecastValue(void) const; // returns true if forecast_value is set 
  //--- functions receiving the values 
   double                       GetActualValue(void) const;   // returns actual_value or nan if the value is no set 
   double                       GetPreviousValue(void) const; // returns prev_value or nan if the value is no set 
   double                       GetRevisedValue(void) const;  // returns revised_prev_value or nan if the value is no set 
   double                       GetForecastValue(void) const; // returns forecast_value or nan if the value is no set 
  };

现在,我们需要做的第一件事是收集在所选时间范围内的所有可用值,并按照我们之前看到的(共八个)特定值进行排序,同时应用筛选条件。为了获取这些值,我们采用以下逻辑。

MqlCalendarValue values[];
datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_D1);
datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_D1);

int valuesTotal = CalendarValueHistory(values, startTime, endTime, NULL, NULL);

Print("TOTAL VALUES = ", valuesTotal, " || Array size = ", ArraySize(values));

在此,我们声明了一个名为“values”的数组,其类型为MqlCalendarValue结构体,该数组将用于存储从MQL5经济日历中检索到的日历数据。接着,我们设置了两个datetime类型的变量,即“startTime”(开始时间)和“endTime”(结束时间),用于定义数据提取的时间范围。我们使用TimeTradeServer函数获取当前服务器时间,并通过PeriodSeconds函数创建一个24小时的偏移量(以秒为单位),从而计算出开始时间,即当前服务器时间前一天的时间点。通过使用“PERIOD_D1”常量,我们实现了以一天为单位的时间逻辑。类似地,“endTime”被设置为当前服务器时间后一天的时间点,这样我们就能捕获到当前时刻前后两天内发生的经济事件。

接下来,我们使用CalendarValueHistory函数,将指定时间范围内的经济事件填充到values数组中。该函数返回事件的总数,我们将这个总数存储在“valuesTotal”变量中。CalendarValueHistory函数的参数包括“values”数组、“startTime”(开始时间)和“endTime”(结束时间),以及两个用于国家和货币类型的NULL过滤器(这里我们将其设为 NULL,以便检索所有事件)。最后,我们使用Print函数,结合 ArraySize函数,打印出事件的总数和数组的大小,以此确认获取到的事件总数,并验证数组中是否包含了预期的数据,以便在后续的交易逻辑中使用。当我们编译这段代码时,会得到以下输出。

数值确认

接下来,我们可以将这些值打印到日志中,以便确认我们有什么。

if (valuesTotal >=0 ){
   Print("Calendar values as they are: ");
   ArrayPrint(values);
}

在此,我们首先检查事件总数是否大于或等于0,这表明CalendarValueHistory函数已成功检索到一些经济日历事件,或者在没有错误的情况下返回了0。如果满足此条件,我们使用Print函数输出一条消息:“当前日历数值如下:”("Calendar values as they are:"),以此作为标题,告知我们接下来将显示这些数值。随后,我们调用 ArrayPrint函数,该函数会将“values”数组的全部内容打印到日志中。运行后,我们会得到以下数据。

事件日志

由上图可见,我们记录了所选时间范围内所有可用的经济事件。然而,我们注意到,事件特征仅通过数字来表示,这些数字是特定的特征标识符,但它们并没有为我们提供太多有用的信息。因此,我们需要选择一个特定的值,然后从该值出发,以结构化的方式获取每个事件的具体特征。这意味着我们需要遍历每一个选定的值。

for (int i = 0; i < valuesTotal; i++){

//---

}

在此,我们创建了一个for循环,用于遍历“values”数组中的每一个元素,该数组存储着经济日历数据。循环将一个整数变量“i”初始化为零,代表起始索引,并且只要“i”小于事件总数(即 CalendarValueHistory 函数检索到的事件总数),循环就会继续运行。在每次迭代中,我们将“i”增加1,这样我们就可以按顺序访问“values”数组中的每一个经济事件。现在,在循环内部,我们可以处理或分析每个事件的数据,这为诸如筛选事件、根据事件详情应用特定的交易逻辑,或打印单个事件信息等任务提供了灵活性。以下是我们需要使用的逻辑。

MqlCalendarEvent event;
CalendarEventById(values[i].event_id,event);

我们声明了一个类型为 MqlCalendarEvent的变量“event”,该变量将用于存储特定经济事件的详细信息。接着,我们调用CalendarEventById函数,并传入两个参数。第一个参数从“values”数组中获取当前经济事件的唯一标识符(基于当前的循环索引 i),而第二个参数“event”则作为一个容器,用于存储该事件的完整详情。通过使用CalendarEventById函数,我们可以获取每个特定事件的全面数据,例如事件名称、国家、预测值和实际值等,这些数据随后可用于进一步的分析或交易决策。为了确认这一点,我们可以按照以下方式打印事件标识符:

Print("Event ID ",values[i].event_id);

然而,这样会将过多数据打印到日志中,因此我们将选择的时间范围缩小到当前时间前后各一小时。我们只需要修改时间范围部分即可。为了便于参考,我们已经用黄色高亮标出了这部分内容。

datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H1);
datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H1);

当运行程序时,我们得到以下输出。

事件ID

现在我们可以继续获取所选特定事件的实际值,而不仅仅是事件值的数字。为了实现这一点,我们只需要输入事件的关键字,并使用“点运算符”来访问该类或结构的所有方法和对象。您应该得到如下结果。

点运算符

使用同样的方法,我们可以检索在创建的“event”结构中填写并存储的所有信息。

Print("Event ID ",values[i].event_id);
Print("Event Name = ",event.name);
Print("Event Code = ",event.event_code);
Print("Event Type = ",event.type);
Print("Event Sector = ",event.sector);
Print("Event Frequency = ",event.frequency);
Print("Event Release Mode = ",event.time_mode);
Print("Event Importance = ",EnumToString(event.importance));
Print("Event Time = ",values[i].time);
Print("Event URL = ",event.source_url);

Comment("Event ID ",values[i].event_id,
        "\nEvent Name = ",event.name,
        "\nEvent Code = ",event.event_code,
        "\nEvent Type = ",event.type,
        "\nEvent Sector = ",event.sector,
        "\nEvent Frequency = ",event.frequency,
        "\nEvent Release Mode = ",event.time_mode,
        "\nEvent Importance = ",EnumToString(event.importance),
        "\nEvent Time = ",values[i].time,
        "\nEvent URL = ",event.source_url);
}

这里,我们使用Print函数输出values数组中每个经济事件的详细信息,并将这些信息显示在Experts标签中,以便于查看。我们首先打印values[i]中的事件ID,它是该事件的唯一地标识。接下来,我们从“event”变量中检索并打印有关事件的具体细节,包括其名称、事件代码、类型、部门、频率、时间模式(指示发布时间模式)、重要性(使用EnumToString函数转换为可读字符串),以及实际安排的时间。最后,我们打印源Uniform Resource Locator(URL),它提供了一个链接,用于获取更多信息。

除了这些打印语句外,我们还使用Comment函数在图表上显示相同的详细信息,以便随时用来参考。Comment函数在图表的注释区域输出每一行,使交易者能够直接在图表上查看实时更新。程序运行后,我们得到了以下输出。

按ID显示的事件数据

非常成功。为了获取国家和货币数据,我们引入了另一个处理国家值的结构体。我们需要采用的逻辑与之前使用的其他逻辑相同。

MqlCalendarCountry country;
CalendarCountryById(event.country_id,country);

这里,我们声明了一个类型为MqlCalendarCountry的变量"country",我们打算用它来存储与经济事件相关的特定国家的信息。然后,我们通过传递两个参数来调用CalendarCountryById函数,以检索国家详细信息。

第一个参数提供了与当前经济事件相关联的国家的唯一标识符,而第二个参数"country"则用作存储该国家数据的容器。通过调用CalendarCountryById函数,我们用相关国家的信息填充变量"country",例如国家的名称、代码和其他特征,使我们能够使用这些数据进行更有针对性的分析,并在每个经济事件旁边显示特定国家的详细信息。

为了显示这些信息,我们再次打印国家信息。

Print("Country ID ",country.id);
Print("Country Name ",country.name);
Print("Country Code ",country.code);
Print("Country Currency ",country.currency);
Print("Country Currency Symbol ",country.currency_symbol);
Print("Country URL ",country.url_name);

这是我们运行程序后得到的结果。

国家数据

非常成功。现在,我们已经对MQL5中用于访问日历数据的必要函数做出了完整的介绍。接下来,我们可以继续对我们的代码块应用过滤器,以便通常能够针对特定国家的数据、优先级数据、货币数据以及特定时间的新闻进行交易。为了动态地实现这一目标,我们可以将逻辑转移到一个函数中,以便满足上述目的。

//+------------------------------------------------------------------+
//|       FUNCTION TO GET NEWS EVENTS                                |
//+------------------------------------------------------------------+
bool isNewsEvent(){
   int totalNews = 0;
   bool isNews = false;
   
   //---
   
   return (isNews);
}

我们定义了一个名为“isNewsEvent”的布尔(boolean)函数,该函数用于判断是否存在任何可用的经济新闻事件。该函数返回一个布尔值,用于表示是否存在新闻事件(存在则返回 true,不存在则返回 false)。在函数内部,我们声明了一个名为“totalNews”的整型变量,并将其初始化为0。我们打算使用该变量来存储相关新闻事件的总数,稍后将在函数中检索该数值。我们还声明了一个布尔变量“isNews”,并将其设置为false。该变量将作为一个标识,如果在函数执行过程中检测到任何相关新闻事件,则将其切换为 true。

目前,该函数仅返回“isNews”的值,由于函数内部尚未处理任何新闻事件,因此该值默认为 false。此函数结构为后续实现检索和检查新闻事件的逻辑提供了基础,如果检测到新闻事件,则将“isNews”设置为 true。我们将之前在OnInit事件处理器中已经定义好的逻辑添加到此函数中。不过,我们在事件处理器上调用该函数,以此影响函数的逻辑。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
//---   
   if (isNewsEvent()){
      Print("______ ALERT: WE HAVE NEWS EVENT ___");
   }
   else if (isNewsEvent()==false){
      Print("______ ALERT: WE DON'T ANY HAVE NEWS EVENT ___");
   }
//---
   return(INIT_SUCCEEDED);
}

在OnInit事件处理器中,我们首先调用“isNewsEvent”函数。如果该函数返回 true,表示存在新闻事件,则 if 代码块将被触发,我们会打印消息“______ 警报:我们检测到新闻事件 ___”。该条消息提醒我们检测到了经济新闻事件,如果有需要,可以据此调整交易策略。

如果该函数返回 false,表示不存在新闻事件,那么 else if 代码块将被触发,我们会打印“______ 警报:我们没有检测到任何新闻事件 ___”。这条消息提醒我们当前没有识别到新闻事件,这可能意味着交易可以按预期进行,不会受到新闻的干扰。最后,该函数返回INIT_SUCCEEDED,这是一个常量,表示EA成功初始化。现在,在调用完这个事件处理器后,我们可以继续对所关心的新闻类型应用过滤器。当前,我们的程序已附加到“AUDUSD”(澳元兑美元)货币对上。接下来,我们开发一个逻辑,以确保只考虑美国的新闻。

string currency_filter = "USD";
string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);
string currency_quote = StringSubstr(_Symbol,3,3);
if (currency_base != currency_filter && currency_quote != currency_filter){
   Print("Currency (",currency_base," | ",currency_quote,
         ") is not equal equal to ",currency_filter);
   return false;
}

我们首先定义两个字符串变量:“currency_filter”(货币过滤器)和“currency_base”(基础货币),以便检查当前货币对是否与指定的货币过滤器“USD”(美元)相关。变量“currency_filter”被初始化为“USD”,代表我们感兴趣的目标监测货币。然后,我们使用SymbolInfoString函数获取当前货币对的基础货币(例如,在AUDUSD货币对中是AUD),并将其存储在“currency_base”中。

接下来,我们定义“currency_quote”(报价货币),通过使用StringSubstr函数从当前货币对中提取报价货币(由于没有直接访问报价货币的方法,我们取货币对字符串的最后三个字符)。例如,在AUDUSD货币对中,StringSubstr函数将提取出“USD”,即报价货币。

然后,我们检查基础货币和报价货币是否都与过滤器货币不同。如果它们与目标货币过滤器(本例中为USD)不匹配,我们会打印一条消息,指示该货币对的货币与货币过滤器不匹配。然后,函数返回false,如果该货币对与指定的货币无关,则有效终止进一步的处理。如果在不同的货币对上运行此代码,我们会得到以下输出。

货币过滤器

由可视化结果可见,当我们将程序加载到“AUDUSD”(澳元兑美元)和“USDJPY”(美元兑日元)图表上时,没有出现任何错误。但是,当我们将其加载到“EURJPY”(欧元兑日元)图表上时,出现了错误,这表明该货币对的两种货币都不符合我们预先定义的货币过滤器。现在,我们可以继续根据事件的重要性和时间动态地应用多个过滤器。

if (StringFind(_Symbol,country.currency) >= 0){
   if (event.importance == CALENDAR_IMPORTANCE_MODERATE){
      if (values[i].time <= TimeTradeServer() && values[i].time >= timeBefore){
         Print(event.name," > ", country.currency," > ", EnumToString(event.importance)," Time= ",values[i].time," (ALREADY RELEASED)");
         totalNews++;
      }
            
      if (values[i].time >= TimeTradeServer() && values[i].time <= timeAfter){
         Print(event.name," > ", country.currency," > ", EnumToString(event.importance)," Time= ",values[i].time," (NOT YET RELEASED)");
         totalNews++;
      }
   }
}

我们实现了一系列嵌套条件,以识别与特定货币相关的新闻事件,重点关注那些被归类为“中等重要性”且发生在特定时间范围内的新闻事件。首先,我们检查当前交易品种的名称(由_Symbol表示)是否包含与新闻事件相关的货币。我们通过使用StringFind函数来实现这一点,该函数确保我们只考虑与交易品种货币相关的新闻事件。

这与我们之前使用的其他逻辑相似,只不过它是动态的,因为它会自动检查所选货币对中是否包含相关货币。如果这一检查通过,我们会进入下一个条件,以确认事件的重要性级别是否与CALENDAR_IMPORTANCE_MODERATE(中等重要性)相匹配,这意味着我们只针对具有中等影响的事件,而排除那些低重要性或高重要性的事件。

一旦识别出一个中等重要性的新闻事件,我们会使用两个独立的检查来评估该事件相对于服务器当前时间(TimeTradeServer)的时间点。在第一项检查中,我们确定事件的时间是否早于或等于当前时间,但晚于“timeBefore”(某个之前设定的时间点)。如果是这样,意味着该事件已经在我们指定的过去时间段内发布。然后,我们使用Print函数记录该事件的详细信息,包括其名称、相关货币、重要性和时间,并标记为“(已发布)”。同时,我们还会递增“totalNews”变量,以跟踪符合标准的事件数量。在第二项检查中,我们确定事件的时间是否晚于或等于当前时间,但早于或等于“timeAfter”(某个之后设定的时间点),这表明该事件即将发生,但仍然在我们指定的未来时间段内。

同样,我们使用Print函数记录类似的事件详细信息,并标记为“(尚未发布)”以指示其待处理状态,同时为这个额外的相关事件递增“totalNews”变量。您可能已经注意到,我们在逻辑中使用了一些不同的时间变量。这些变量用于确保事件处于我们采取行动的时间范围内,以下是逻辑说明。

datetime timeRange = PeriodSeconds(PERIOD_D1);
datetime timeBefore = TimeTradeServer() - timeRange;
datetime timeAfter = TimeTradeServer() + timeRange;
   
Print("FURTHEST TIME LOOK BACK = ",timeBefore," >>> CURRENT = ",TimeTradeServer());

我们在for循环之前声明这些逻辑,以在当前服务器时间周围建立一个时间范围,从而定义我们希望考虑新闻事件的过去和未来时间跨度。首先,我们声明一个datetime类型的变量“timeRange”,并使用PeriodSeconds函数将其赋值为一天的时间长度。这使得我们能够使用一个标准化的24小时时间框架,不过,您可以根据需要将其调整为期望的范围,比如新闻发布前后的15分钟。接下来,我们定义另外两个datetime类型的变量:“timeBefore”和“timeAfter”。

我们通过从当前服务器时间中减去时间范围来计算变量“timeBefore”,这样一来给出了我们希望回溯的最远时间点。类似地,“timeAfter”是通过将“timeRange”加到当前服务器时间上来确定的,这就提供了我们希望在未来考虑的最晚时间点。“timeBefore”和“timeAfter”共同创建了一个以当前时间为中心的24小时窗口,这有助于我们捕捉刚刚发生或即将发生的事件。因此,我们可以通过扩大时间范围或增加事件搜索范围(在此情况下,可将其扩展为一天)来增加搜索力度。图示如下:

时间的回溯与前瞻

由图可见,当我们考虑的是为期一天的扫描时,当前时间(或更准确地说,是当前日期)为25日,那么回溯的日期是24日,前瞻的日期是26日,这正好是当前日期的前一天和后一天。为了清晰起见,我们已经用蓝色将它们高亮显示。最后,我们可以分析从循环中记录的新闻事件数量,并返回相应的布尔标识。我们采用以下逻辑来实现这一点。

if (totalNews > 0){
   isNews = true;
   Print(">>>>>>> (FOUND NEWS) TOTAL NEWS = ",totalNews,"/",ArraySize(values));
}
else if (totalNews <= 0){
   isNews = false;
   Print(">>>>>>> (NOT FOUND NEWS) TOTAL NEWS = ",totalNews,"/",ArraySize(values));
}

这里,我们根据记录的新闻总数来评估是否发现了任何相关的新闻事件。如果新闻总数大于0,这表明至少找到了一个符合我们标准的新闻事件。在此情况下,我们将“isNews”变量设置为 true,并打印一条消息,显示匹配的新闻事件总数和“values”数组的大小。这条标记为“(发现新闻)”的消息还包括了已识别的新闻总数和最初考虑的所有新闻数量,从而给出了在检索到的所有事件中发现的相关新闻事件的总数量。

相反,如果新闻总数等于0或小于0,这意味着没有找到相关的新闻事件。在这种情况下,我们将“isNews”设置为 false,并打印一条标记为“(未发现新闻)”的消息,显示新闻总数为0以及“values”数组的大小。该结构有助于我们跟踪是否找到了符合标准的新闻事件,并提供了结果日志,这对于验证新闻检查过程的成果非常有用。编译后,我们会得到以下输出。

未发现新闻事件

由图可见,目前没有相关的新闻事件。因此,我们可以通过扩大时间范围或增加事件搜索范围(在此情况下,可将其扩展为一天)来增加搜索力度。

datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_D1);
datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_D1);

如果我们将新闻搜索范围调整回一天,我们会得到以下输出。

发现5条新闻事件

由图可见,在81条新闻中,有5条是相关的新闻事件。我们打印出这些相关新闻事件的信息,这些信息可以用来做出交易决策,即决定是进入市场还是退出市场。这部分内容就讲解到这里,以下是负责从MQL5日历中识别和过滤新闻的完整函数代码段:

//+------------------------------------------------------------------+
//|       FUNCTION TO GET NEWS EVENTS                                |
//+------------------------------------------------------------------+
bool isNewsEvent(){
   int totalNews = 0;
   bool isNews = false;
   
   MqlCalendarValue values[];
   
   datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_D1);
   datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_D1);
   
   //string currency_filter = "USD";
   //string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);
   //string currency_quote = StringSubstr(_Symbol,3,3);
   //if (currency_base != currency_filter && currency_quote != currency_filter){
   //   Print("Currency (",currency_base," | ",currency_quote,
   //         ") is not equal equal to ",currency_filter);
   //   return false;
   //}
   
   int valuesTotal = CalendarValueHistory(values,startTime,endTime,NULL,NULL);
   
   Print("TOTAL VALUES = ",valuesTotal," || Array size = ",ArraySize(values));
   
   //if (valuesTotal >=0 ){
   //   Print("Calendar values as they are: ");
   //   ArrayPrint(values);
   //}
   
   datetime timeRange = PeriodSeconds(PERIOD_D1);
   datetime timeBefore = TimeTradeServer() - timeRange;
   datetime timeAfter = TimeTradeServer() + timeRange;
   
   Print("Current time = ",TimeTradeServer());
   Print("FURTHEST TIME LOOK BACK = ",timeBefore," >>> LOOK FORE = ",timeAfter);
   
   for (int i = 0; i < valuesTotal; i++){
      MqlCalendarEvent event;
      CalendarEventById(values[i].event_id,event);
      
      
      MqlCalendarCountry country;
      CalendarCountryById(event.country_id,country);
      
      if (StringFind(_Symbol,country.currency) >= 0){
         if (event.importance == CALENDAR_IMPORTANCE_MODERATE){
            if (values[i].time <= TimeTradeServer() && values[i].time >= timeBefore){
               Print(event.name," > ", country.currency," > ", EnumToString(event.importance)," Time= ",values[i].time," (ALREADY RELEASED)");
               totalNews++;
            }
            
            if (values[i].time >= TimeTradeServer() && values[i].time <= timeAfter){
               Print(event.name," > ", country.currency," > ", EnumToString(event.importance)," Time= ",values[i].time," (NOT YET RELEASED)");
               totalNews++;
            }
         }
      }
      
   }
   
   if (totalNews > 0){
      isNews = true;
      Print(">>>>>>> (FOUND NEWS) TOTAL NEWS = ",totalNews,"/",ArraySize(values));
   }
   else if (totalNews <= 0){
      isNews = false;
      Print(">>>>>>> (NOT FOUND NEWS) TOTAL NEWS = ",totalNews,"/",ArraySize(values));
   }
   
   return (isNews);
}


结论

总而言之,我们已经为探索MQL5经济日历奠定了初步基础。主要涉及到提取信息,并根据货币的重要性和事件的显著性来对其进行判断。我们开发了一个结构化的流程,能够从经济日历中提取数据,根据货币和事件重要性等重要标准进行过滤,并识别事件是即将发生还是已经发生。该方法对于自动化交易系统至关重要,系统能够针对具有影响力的经济动态做出响应,从而在应对市场波动时为我们提供优势。

在本系列的下一部分中,我们将扩展功能,在图表窗口上直接显示过滤后的经济数据,从而提高其在实时交易决策中的可见性。我们还将改进EA,使其能够利用这些信息,根据重大经济事件来开仓。随着这些功能的集成,我们将从经济新闻的分析过渡到实际利用这些新闻进行交易。我们将通过围绕实时框架进行设计,并利用从经济日历事件中学到的信息来在这些事件发生后触发交易,从而使系统更加灵活。请您持续关注。

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

附加的文件 |
交易策略 交易策略
各种交易策略的分类都是任意的,下面这种分类强调从交易的基本概念上分类。
您应当知道的 MQL5 向导技术(第 40 部分):抛物线止损和反转(PSAR) 您应当知道的 MQL5 向导技术(第 40 部分):抛物线止损和反转(PSAR)
抛物线止损和反转(PSAR) 是趋势确认、和趋势终结点的指标。因为它在识别趋势方面滞后,所以它的主要目的是为持仓定位尾随止损。然而,我们要探索它是否真的可以当作智能系统的交易信号,这要归功于由向导汇编智能系统的自定义信号类。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
从基础到中级:SWITCH 语句 从基础到中级:SWITCH 语句
在本文中,我们将学习如何以最简单、最基本的形式使用 SWITCH 语句。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。