
了解如何在MQL5中处理日期和时间
概述
金融市场领域的任何人都不会隐瞒时间的重要性,以及它如何影响交易决策和结果。MQL5(MetaQuotes Language 5)提供了一个有效处理日期和时间的惊人解决方案,这是我们将在本文中学习的内容,因为在理解了MQL5编程语言中该主题的最重要方面后,我们将看到如何通过许多可作为交易系统一部分进行编码的应用程序来处理这一重要主题。
以下主题是我们将在本文中介绍的内容:
通过阅读这些主题,您将能够使用mql5中的日期时间类型的数据,将其作为您创建的任何软件的一部分,因为正如我们所说,日期时间主题是交易中非常重要的主题,您必须了解作为交易者或交易系统开发人员如何处理。所以,我希望你能发现这篇文章很有用,并对它的主题或至少任何相关主题有深入的了解。datetime 类型
mql5中的datetime变量类型用于对Unix时间中的日期和时间数据进行排序。Unix时间是自1970年1月1日以来经过的秒数。为了更好地理解,如果我们想将2023年1月1日00:00:00 GMT的日期转换为Unix时间,我们可以发现它是1672531200秒。相应地,时间戳就是距离 Unix 时代,即1970年1月1日这个特别日子的秒数。我们还可以看到,与已知可读时间相比,以秒为单位的时间如下:已知可读时间 | 秒 |
---|---|
一年 | 31556926秒 |
一个月 | 2629743秒 |
一周 | 604800秒 |
一天 | 86400秒 |
1 Hour | 3600秒 |
这里有关于Unix时间主题的有趣信息,如果你想阅读更多,可以通过维基百科上的Unix时间文章来阅读。
我们可能会发现自己需要确定一个特定的日期和时间,这很容易通过使用日期时间常数来实现,该常数以D字符开头,日期和时间位于单引号之间,格式如下:
D'yyyy.mm.dd hh:mm:ss'
或者:
D'dd.mm.yyyy hh:mm:ss'
以下来自mql5参考的示例在这种情况下可能是有用的指南:
datetime NY=D'2015.01.01 00:00'; // Time of beginning of year 2015 datetime d1=D'1980.07.19 12:30:27'; // Year Month Day Hours Minutes Seconds datetime d2=D'19.07.1980 12:30:27'; // Equal to D'1980.07.19 12:30:27'; datetime d3=D'19.07.1980 12'; // Equal to D'1980.07.19 12:00:00' datetime d4=D'01.01.2004'; // Equal to D'01.01.2004 00:00:00' datetime compilation_date=__DATE__; // Compilation date datetime compilation_date_time=__DATETIME__; // Compilation date and time datetime compilation_time=__DATETIME__-__DATE__;// Compilation time //--- Examples of declarations after which compiler warnings will be returned datetime warning1=D'12:30:27'; // Equal to D'[date of compilation] 12:30:27' datetime warning2=D''; // Equal to __DATETIME__
以下是编译后的打印结果:
与我们迄今为止所理解的一样,datetime类型保存以秒为单位的日期和时间值。这使得比较或操作日期和时间值变得容易。以下是我们在处理日期和时间值时可以做些什么的示例:
- 比较日期和时间的值。
- 增加或减去日期和时间值。
- 将时间转换为字符串或将字符串转换为用于其他目的的时间。
比较日期和时间的值:
如果我们有两个时间和日期值,并且出于其他目的需要对它们进行比较,那么我们可以在MQL5中通过为每个值创建一个变量,然后将它们相互比较,以根据结果采取行动来实现这一点。以下是这种情况的示例:
如果用户输入了两个日期和时间值,其中一个比另一个早,并且我们想要获得特定的结果,例如,如果输入的一个值大于输入的二个值,则我们需要收到一条消息 “Input one is a recent time”(第一个输入是最近的时间),但如果输入一个值小于输入二的值,则需要收到一个消息“Input two is a recent time”(第二个输入是最近的时间)。
以下是执行此操作的代码:
//+------------------------------------------------------------------+ //| dateTimeCompare.mq5 | //+------------------------------------------------------------------+ input datetime inpDate1 = D'2023.09.01 00:00'; input datetime inpDate2 = D'2023.09.30 00:00'; //+------------------------------------------------------------------+ void OnTick() { if(inpDate1>inpDate2) { Print("Input one is a recent time"); } else if(inpDate1<inpDate2) { Print("Input two is a recent time"); } } //+------------------------------------------------------------------+
编译此代码后,如果我们将日期和时间输入值设置为如下所示:
正如我们所看到的,我们的input1比第二个更早。因此,我们可以找到与以下相同的结果:
如果我们将第一个输入改为比第二个输入更近,则如下所示:
根据之前输入的设置,我们必须找到与以下相同的结果:
前面的代码示例是我们如何处理日期和时间作为比较的示例。
增加或减小日期和时间值:
我们还可以对日期和时间值进行加减运算,假设我们有两个日期和时间的值,如下所示:
D'2023.09.01 00:00'
D'2023.09.30 00:00'
现在,我们需要在第一个值上加一天,在第二个值上减一天,以秒为单位的一天等于86400。因此,在这两个计算之后,我们可以找到与以下相同的新值:
D'2023.09.02 00:00'
D'2023.09.29 00:00'
以下代码是关于如何对这些计算进行编码的:
//+------------------------------------------------------------------+ //| dateTimeManipulate.mq5 | //+------------------------------------------------------------------+ input datetime oldDate = D'2023.09.01 00:00'; input datetime newDate = D'2023.09.30 00:00'; //+------------------------------------------------------------------+ void OnTick() { datetime addToDate = oldDate+86400; Print("Result of date addition - ",addToDate); datetime subtractFromDate = newDate-86400; Print("Result of date subtraction - ",subtractFromDate); } //+------------------------------------------------------------------+
执行该程序的结果将与我们提到的相同,如下所示:
将时间转换为字符串或将字符串转换为时间用于其他目的:
我们可以发现自己需要转换日期和时间值的类型,此时,我们有两个函数可以处理字符串和日期时间之间的转换。无论是TimeToString还是StringToTime,我们现在将在这一部分中看到一个如何处理类似情况的示例,如下代码所示:
//+------------------------------------------------------------------+ //| dateTimeConvert.mq5 | //+------------------------------------------------------------------+ input datetime oldDate = D'2023.09.01 00:00'; input string newDate = "2023.09.30 00:00"; //+------------------------------------------------------------------+ void OnTick() { string newOldDate = TimeToString(oldDate); datetime newNewDate = StringToTime(newDate); Print("Time To String - ",newOldDate); Print("String To Time - ",newNewDate); } //+------------------------------------------------------------------+
通过在交易终端中执行这段代码,我们可以找到每个函数的返回值,第一个用于从TimeToString转换,第二个用于从StringToTime转换,如下所示:
一般来说,如果我们想了解和理解MQL5中关于日期时间类型的所有操作,
- currentTime: 返回当前时间。
- 值之间的比较:从逻辑上讲,我们使用此操作来检查时间比较。
- 对日期和时间数据的处理:比如增加或减小。
- 格式化日期和时间。
MqlDateTime 结构
在本部分中,我们将学习MqlDateTime结构。它包含可以保存日期和时间数据或日期时间值的变量。以下是与我们可以找到MQL5参考中相同的结构:
struct MqlDateTime { int year; // Year int mon; // Month int day; // Day int hour; // Hour int min; // Minutes int sec; // Seconds int day_of_week; // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) int day_of_year; // Day number of the year (January 1st is assigned the number value of zero) };
正如我们所看到的,它包含八个int类型变量的字段。
我们将(TimeToStruct)函数与此结构一起用于将datetime值转换为MqlDateTime的变量,此函数的参数为:
- dt: 日期和时间
- dt_struct:用于采用数值的结构
此函数返回一个布尔值。
我们还可以使用(StructToTime)函数将MqlDateTime结构的变量转换为日期时间值。其参数仅为dt_struct。它返回包含秒数的datetime类型。
我们可以看到这个MqlDateTime结构的一个示例,如下所示:
如果我们有D'2023.10.10 07:07:07'的日期时间或任何其他输入值,我们需要使用MqlDateTime。我们可以通过以下代码做到这一点:
//+------------------------------------------------------------------+ //| MqlDateTime.mq5 | //+------------------------------------------------------------------+ input datetime dtData = D'2023.10.10 07:07:07'; void OnTick() { MqlDateTime timeStruct; TimeToStruct(dtData,timeStruct); int year = timeStruct.year; Print("year: ",year); Print("====================="); int month = timeStruct.mon; Print("month: ",month); Print("====================="); int day = timeStruct.day; Print("day: ",day); Print("====================="); int hour = timeStruct.hour; Print("hour: ",hour); Print("====================="); int minute = timeStruct.min; Print("minute: ",minute); Print("====================="); int second = timeStruct.sec; Print("second: ",second); Print("====================="); int dayofWeek = timeStruct.day_of_week; Print("dayofWeek: ",dayofWeek); Print("====================="); int dayofYear = timeStruct.day_of_year; Print("dayofYear: ",dayofYear); } //+------------------------------------------------------------------+
通过执行此EA,我们可以找到与以下相同的打印消息:
这里值得一提的是,如果使用小于1的输入,则月份或日期的默认值为01,如果可用输入大于该最大值,则月份的最大默认值为12,日期为31。默认值为小时最小值为0或最大值为23,分钟最小值为零或最大值59。
OnTimer() 事件
在这一部分中,我们将了解什么是OnTimer()事件处理程序以及如何使用它。当您需要每隔指定的秒数执行代码或交易策略时,可以使用它。因此,如果您希望代码在特定的时间间隔执行特定的操作,您可以将代码放在OnTimer()事件处理程序中。
OnTimer()事件处理程序是一个没有参数的void类型,如果需要,可以将其添加到代码中。要使用它,您必须在OnInit()部分通过使用带有seconds参数的EventSetTimer()函数来设置计时器间隔。与以下示例相同:
int OnInit() { EventSetTimer(60); }
上一个例子意味着OnTimer()事件处理程序中的代码将每60秒执行一次。
现在,如果我们需要停止在OnTime()事件中运行代码,我们可以通过在OnDeinit()事件处理程序或类析构函数中使用void EventKillTimer()类型函数来完成。
NewBar 应用程序
在这个应用程序中,我们将创建一个包含文件,其中包含可以在其他文件(如EA)中使用的类和函数,以执行我们的目标,即通过打印“A new bar painted”(绘制了一个新柱)的消息来报告我们是否有新柱生成。
首先,以下步骤用于创建包含文件。在这里创建include文件的主要思想是学习和适应可以在许多项目中使用的构建文件,如果您是初学者,则可以作为开发人员进行专业工作。
我们将声明CNewBar类,它有两个私有变量(time[]用于存储当前柱的时间,另一个变量lastTime用于存储最近柱的时间。我们将包括一个带有两个参数的布尔型 newBarCheck 公有函数:
- symbol 字符串类型
- timeFrame ENUM_TIMEFRAMES 类型
class CNewBar { private: datetime time[], lastTime; public: void CNewBar(); bool newBarCheck(string symbol, ENUM_TIMEFRAMES timeFrame); };
将 time 数组设置为序列
void CNewBar::CNewBar(void) { ArraySetAsSeries(time,true); }
创建带有两个参数(symbol和timeFrame)的 newBarCheck 函数来检查是否有新的柱,该函数将返回 bool 类型的值
bool CNewBar::newBarCheck(string symbol, ENUM_TIMEFRAMES timeFrame)
函数的主体检查是否有一个新的柱,为 firstCheck 和 newBar 创建两个bool变量,并使用默认的false值。
bool firstCheck = false; bool newBar = false;
使用CopyTime函数和第一个位置调用的变体,以获取指定数量的指定交易品种-周期对的柱形开启时间的 time_array 历史数据。它的参数是:
- symbol_name:指定将作为交易品种的交易品种名称,作为函数中定义的字符串之一。
- timeframe:将时间框架指定为函数中定义的ENUM_TIMEFRAMES的 timeFrame。
- start_pos: 指定起始位置,将为0。
- count:指定要复制的数据量,将为2。
- time_array[]:从中复制开启时间的数组,该数组将成为时间数组。
CopyTime(symbol,timeFrame,0,2,time);我们将检查lastTime类变量,如果这是第一次检查,那么lastTime变量的值将等于0,那么firstCheck变量将为true
if(lastTime == 0) { firstCheck = true; }
检查time[0]是否大于lastTime值,然后检查firstCheck值是否等于false,因此newBar将为true,然后lastTime将与time[0]相同
if(time[0] > lastTime) { if(firstCheck == false) { newBar = true; } lastTime = time[0]; }
返回的 newBar 值是一个布尔型值。
return(newBar);
以下是该包含文件在一个代码块中的完整代码:
//+------------------------------------------------------------------+ //| newBar.mqh | //+------------------------------------------------------------------+ class CNewBar { private: datetime time[], lastTime; public: void CNewBar(); bool newBarCheck(string symbol, ENUM_TIMEFRAMES timeFrame); }; //+------------------------------------------------------------------+ void CNewBar::CNewBar(void) { ArraySetAsSeries(time,true); } //+------------------------------------------------------------------+ bool CNewBar::newBarCheck(string symbol, ENUM_TIMEFRAMES timeFrame) { bool firstCheck = false; bool newBar = false; CopyTime(symbol,timeFrame,0,2,time); if(lastTime == 0) { firstCheck = true; } if(time[0] > lastTime) { if(firstCheck == false) { newBar = true; } lastTime = time[0]; } return(newBar); } //+------------------------------------------------------------------+
如果你想了解更多关于MQL5中面向对象编程(OOP)和类的信息,你可以阅读我之前的理解MQL5面向对象编程的文章,你可能会发现它很有用。
现在,在创建包含的文件后,我们将创建我们的EA,它将检测新的柱,并打印一条消息“A new bar painted, you can trade”(绘制了一个新柱,您可以交易了)。首先,我们将使用预处理器#include将创建的include文件包含在EA代码中
#include <dateTime.mqh>
声明CNewBar类中的对象
CNewBar NewBar;
创建一个bool变量作为输入,以选择在绘制新柱时交易(true)还是不交易(false)
bool newBarTrade=true;
之后,在OnTick()部分,我们将创建一个具有默认true值的newBar的bool变量和另一个具有缺省0值的整数barShift变量
bool newBar=true; int barShift=0;
检查newBarTrade是否为true,则newBar将等于Newbarobject.newBarCheck使用参数检查当前交易品种和当前时间框架,并且barShift将等于1。
if(newBarTrade==true) { newBar = NewBar.newBarCheck(Symbol(),Period()); barShift=1; }
检查newBar是否等于true,这意味着生成了一个新的柱,因此我们需要得到“A new bar painted, you can trade”的消息
if(newBar==true) { Print("A new bar painted, you can trade"); }
以下是一个代码块中此应用程序的完整代码
//+------------------------------------------------------------------+ //| newBar.mq5 | //+------------------------------------------------------------------+ #include <dateTime.mqh> CNewBar NewBar; bool newBarTrade=true; //+------------------------------------------------------------------+ void OnTick() { bool newBar=true; int barShift=0; if(newBarTrade==true) { newBar = NewBar.newBarCheck(Symbol(),Period()); barShift=1; } if(newBar==true) { Print("A new bar painted, you can trade"); } } //+------------------------------------------------------------------+
执行此EA后,一旦生成新的柱,我们可以找到以下结果
当根据您的策略绘制新柱时,可以开发此代码来改变操作,例如打开交易或其他什么,但我们在本文中分享了简单的代码,以了解代码的主要思想,您可以根据需要进行开发。
时间过滤器应用程序
在这个应用程序中,我们需要创建一个过滤器应用程序,该应用程序可以用于允许或阻止基于时间过滤器或指定时间段的交易。我们将在创建的include文件中添加一个名为CTimeFilter的新类,并创建一个带有三个参数(startTime、endTime和localTime,默认值为false)的公有的 bool timeCheck 函数。
class CTimeFilter { public: bool timeCheck(datetime startTime, datetime endTime, bool localTime = false); };
创建timeCheck函数的主体,方法是通过获取startTime大于或等于endTime来检查数据输入是否错误,然后返回错误警报“error:Invalid Time input”并返回false
if(startTime >= endTime) { Alert("Error: Invalid Time input"); return(false); }
为currentTime创建一个日期时间变量,然后检查localTime是否为true,则TimeLocal()将分配给currentTime或TimeCurrent()将被分配给currentTime
datetime currentTime; if(localTime == true) { currentTime = TimeLocal(); } else currentTime = TimeCurrent();
创建一个名为timeFilterActive的布尔变量,该变量具有默认的false值,然后检查currentTime是否大于或等于startTime,同时小于endTime,然后为timeFilterActive返回true。返回timeFilterActive值
bool timeFilterActive = false; if(currentTime >= startTime && currentTime < endTime) { timeFilterActive = true; } return(timeFilterActive);
因此,以下是一个块中包含文件中的完整代码
class CTimeFilter { public: bool timeCheck(datetime startTime, datetime endTime, bool localTime = false); }; bool CTimeFilter::timeCheck(datetime startTime, datetime endTime, bool localTime = false) { if(startTime >= endTime) { Alert("Error: Invalid Time input"); return(false); } datetime currentTime; if(localTime == true) { currentTime = TimeLocal(); } else currentTime = TimeCurrent(); bool timeFilterActive = false; if(currentTime >= startTime && currentTime < endTime) { timeFilterActive = true; } return(timeFilterActive); }
创建包含文件后,我们将通过以下步骤创建timeFilterApp EA:
包括dateTime文件以使用其内容
#include <dateTime.mqh>
创建两个 datetime 型变量作为名为StartTime和EndTime的输入参数值
input datetime StartTime = D'2023.10.10 10:00'; input datetime EndTime = D'2023.10.10 17:00';
在全局作用域中创建一个名为filter的对象
CTimeFilter filter;
在OnTick()部分中,创建timeFilterActive并分配给其筛选器。timeCheck及其参数
bool timeFilterActive = filter.timeCheck(StartTime,EndTime,false);
要检查timeFilterActive是否为真,我们需要获得消息“Trading is active based on time filter(基于时间过滤器的交易处于活动状态)”或消息“Trading is inactive based on time filter(基于日期过滤器的交易不在活动状态)”
if(timeFilterActive == true) { Print("Trading is active based on time filter"); } else Print("Trading is inactive based on time filter");
以下是一个块中的完整代码
//+------------------------------------------------------------------+ //| timeFilterApp.mq5 | //+------------------------------------------------------------------+ #include <dateTime.mqh> input datetime StartTime = D'2023.10.10 10:00'; input datetime EndTime = D'2023.10.10 17:00'; CTimeFilter filter; void OnTick() { bool timeFilterActive = filter.timeCheck(StartTime,EndTime,false); if(timeFilterActive == true) { Print("Trading is active based on time filter"); } else Print("Trading is inactive based on time filter"); }
执行此应用程序后,我们可以根据时间输入找到三个结果之一。
在活动时间的情况下:
在非活动时间的情况下:
如果输入参数错误:
还有另一种方法,我们可以在不包含文件的情况下使用,下面是实现这一点的步骤。
全局为开始小时和分钟以及结束小时和分钟创建四个输入int参数
input int TimeStartHour = 10 ; input int TimeStartMin = 0; input int TimeEndHour = 17; input int TimeEndMin = 0 ;
在OnTick()部分中,我们将使用包含日期和时间数据的MqlDateTime函数创建structTime变量
MqlDateTime structTime;
将创建的structTime变量传递给TimeCurrent函数
TimeCurrent(structTime);
将秒设置为0、小时和分钟作为开始时间
structTime.sec = 0;
structTime.hour = TimeStartHour;
structTime.min = TimeStartMin;
为timeStart创建一个datetime变量,并将structTime传递给StructToTime函数,该函数将结构变量MqlDateTime转换为datetime类型的值,然后将其分配给timeStart变量
datetime timeStart = StructToTime(structTime);
设置结束时间的小时和分钟
structTime.hour = TimeEndHour; structTime.min = TimeEndMin;
创建timeEnd变量与创建timeStart变量相同
datetime timeEnd = StructToTime(structTime);
创建一个布尔型isTime变量以确定允许的交易时间
bool isTime = TimeCurrent() >= timeStart && TimeCurrent() < timeEnd;
在错误输入参数的情况下创建打印错误消息
if(TimeStartHour >= TimeEndHour) { Print("Error: Invalid Time input"); }
设置交易时间的条件
if(isTime==true) { Print("Trading is active based on time filter"); } else Print("Trading is inactive based on time filter");
以下是一个块中的完整代码:
//+------------------------------------------------------------------+ //| timeFilterApp2.mq5 | //+------------------------------------------------------------------+ input int TimeStartHour = 10; input int TimeStartMin = 0; input int TimeEndHour = 17; input int TimeEndMin = 0; //+------------------------------------------------------------------+ void OnTick() { MqlDateTime structTime; TimeCurrent(structTime); structTime.sec = 0; structTime.hour = TimeStartHour; structTime.min = TimeStartMin; datetime timeStart = StructToTime(structTime); structTime.hour = TimeEndHour; structTime.min = TimeEndMin; datetime timeEnd = StructToTime(structTime); bool isTime = TimeCurrent() >= timeStart && TimeCurrent() < timeEnd; if(TimeStartHour >= TimeEndHour) { Print("Error: Invalid Time input"); } if(isTime==true) { Print("Trading is active based on time filter"); } else Print("Trading is inactive based on time filter"); } //+------------------------------------------------------------------+
通过执行此代码,我们可以找到与以下相同的结果:
在活动的情况下:
如果不在活跃的交易时间
如果输入参数错误
tradeAtTime 应用程序
比方说,我们需要设定一个特定的时间,我们只需要在这个时间进行交易,我们需要将这个时间设定为允许的交易时间,以下是实现这一点的步骤。
在全局范围创建默认值为“10:00”的openTime变量的输入字符串类型
input string openTime="10:00";
为 lastTime 创建 datetime 型变量
datetime lastTime;
在OnTick()部分中,我们将创建一个datatime dtOpenTime变量,为其分配openTime的StringToTime值
datetime dtOpenTime=StringToTime(openTime);
检查允许时间的条件,如果最后允许的时间不等于dtOpenTime,同时timeCurrent()大于dtOpenTime时,我们需要用dtOpenTime更新lastTime的值,并打印消息“Now is the allowed time to trade”(现在是允许交易的时间)。此打印消息将仅在指定时间打印一次
if(lastTime !=dtOpenTime && TimeCurrent()>dtOpenTime) { lastTime=dtOpenTime; Print("Now is the allowed time to trade"); }
以下是该程序的完整代码:
//+------------------------------------------------------------------+ //| tradeAtTime.mq5 | //+------------------------------------------------------------------+ input string openTime="10:00"; datetime lastTime; //+------------------------------------------------------------------+ void OnTick() { datetime dtOpenTime=StringToTime(openTime); if(lastTime !=dtOpenTime && TimeCurrent()>dtOpenTime) { lastTime=dtOpenTime; Print("Now is the allowed time to trade"); } } //+------------------------------------------------------------------+
执行代码后,我们可以发现其信号如下:
结论
在本文的最后,我希望您了解什么是 datetime,以及我们如何在MQL5中处理日期和时间,以便基于时间考虑进行定制。我们在MQL5中确定了 datetime,以及如何使用它。此外,我们将其应用于简单的应用程序,这些应用程序必须增强我们对这一重要主题的理解。这些应用程序可以是任何有效处理日期和时间的软件的一部分。因此,我们应该理解以下主题
- datetime 类型
- MqlDateTime 结构
- OnTimer 事件
- NewBar 应用程序
- 时间过滤器应用程序
- tradeAtTime 应用程序
在我们的交易中,有许多应用程序可以在日期和时间方面使用,我们无法一一提及,但通过了解主题的基本知识,我们可以将它们应用于我们需要的任何应用程序。
我希望你发现这篇文章很有用,并在学习MQL5编程语言的过程中对你有所帮助,如果你觉得这篇文章有用,并且你需要阅读更多的文章,你可以查看我的出版物,你可以找到关于如何基于最流行的技术指标创建交易系统的文章,以及在学习MQL5编程语言的背景下的其他文章,我希望你也会发现它们很有用。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13466



