日期时间变量实际上是基于从1970年1月1日开始计算的秒数。比如,2009年6月15日凌晨0点会被记录为1245024000。这种日期时间格式的好处是,它使过去和将来时间的比较及数学运算变得相当简便。 例如,若想判断一个日期是在另一个日期之前还是之后,只需要进行简单的比较操作。假设开始时间为2009年6月15日下午2点,结束时间为2009年6月16日凌晨5点:
if(StartDate < EndDate) // 结果为真
if(StartDate > EndDate) // 结果为假
另一个优点是可以通过直接加减秒数来调整某个具体日期。如果你想给开始时间加上24小时,只需加上一天的秒数(86400秒):
datetime AddDay = StartDate + 86400;
如果你需要频繁处理日期时间变量的数学运算,定义一些整型常量来表示不同的时间单位会很有帮助:
#define SEC_H1 3600 // 表示一个小时的秒数
#define SEC_D1 86400 // 表示一天的秒数
不过,日期时间格式的不足之处在于它不易于人类阅读。看到如1245024000这样的数字,无法直观理解它对应的具体日期。这时就需要用到转换函数,将日期时间格式转换为更易读的形式。
日期时间常量通常按照以下格式表示:yyyy.mm.dd hh:mm
。例如,2009年6月15日凌晨0点表示为2009.06.15 00:00。虽然存在多种日期时间常量格式,但我们将使用上述格式,因为它是唯一一种可以方便转换的格式。 要将日期时间变量转换为字符串形式,可以使用TimeToStr()
函数。以下是其基本语法:
string TimeToStr(datetime Time, int Output = TIME_DATE|TIME_MINUTES);
Time
:表示自1970年1月1日以来的秒数的日期时间变量。Output
:一个可选参数,用于指定输出格式,可以是仅日期、仅时间(小时和分钟)、或者包含秒在内的完整时间信息。有效值包括:TIME_DATE
:仅输出日期,例如2009.06.15TIME_MINUTES
:输出小时和分钟,例如05:30TIME_SECONDS
:输出完整的小时、分钟和秒,例如05:30:45
如果想得到默认的yyyy.mm.dd hh:mm
格式的结果,可以省略Output
参数。根据需求选择合适的参数值,比如下面的例子中,假设StartTime
为2009年6月15日上午5点30分45秒:
TimeToStr(StartTime,TIME_DATE) // 返回 "2009.06.15"
TimeToStr(StartTime,TIME_SECONDS) // 返回 "05:30:45"
TimeToStr(StartTime,TIME_MINUTES) // 返回 "05:30"
TimeToStr(StartTime,TIME_DATE|TIME_SECONDS) // 返回 "2009.06.15 05:30:45"
TimeToStr(StartTime) // 返回 "2009.06.15 05:30"
为了构建一个日期时间常量,可以利用字符串拼接的方法,并使用StrToTime()
函数将其转换为日期时间变量。该函数的用法与TimeToStr()
相似,但不涉及Output
参数。字符串必须遵循yyyy.mm.dd hh:mm
格式以便正确转换。例如,使用如下代码可以将几个整数转换为字符串,再转换为日期时间变量:
extern int UseMonth = 6; // 使用月份
extern int UseDay = 15; // 使用日期
extern int UseHour = 5; // 使用小时
extern int UseMinute = 30; // 使用分钟
// 创建字符串常量
string DateConstant = StringConcatenate(Year(), ".", UseMonth, ".", UseDay, " ", UseHour, ":", UseMinute); // DateConstant 是 "2009.6.15 05:30"
// 转换为日期时间变量
datetime StartTime = StrToTime(DateConstant); // StartTime 是 "1245043800"
注意,在StringConcatenate()
函数中,我们使用Year()
函数返回当前年份,而不是使用外部变量。你可以使用类似Month()
、Day()
等函数插入当前的时间值。
日期和时间函数有两种获取当前时间的函数:TimeCurrent()
返回当前服务器时间,而TimeLocal()
则返回本地计算机的时间。根据需求,你可以自由选择使用哪一个。为了灵活切换这两种时间源,可以定义一个布尔类型的外部变量:
extern bool UseLocalTime = true; // 使用本地时间标志
下面是如何根据这个变量将当前时间(无论是本地时间还是服务器时间)赋值给名为CurrentTime
的变量:
if(UseLocalTime == true)
datetime CurrentTime = TimeLocal(); // 使用本地时间
else
CurrentTime = TimeCurrent(); // 使用服务器时间
有时候,你可能只需要获取当前时间的一部分信息,比如仅需知道当前的小时数或者某天的具体日期。以下是几个常用的函数,它们能够帮助你获取当前时间的相关部分。请注意,这些函数均基于服务器时间而非本地时间,其返回值均为整数类型:
Year()
– 返回当前的四位数年份,例如2009。Month()
– 返回当前月份,范围是1至12。Day()
– 返回当前月份中的具体日期,范围是1至31。DayOfWeek()
– 返回当前星期几对应的整数值。其中,周日为0,周一为1,以此类推。Hour()
– 返回当前24小时制下的小时数,范围是0至23。例如,早上3点对应的是3,下午3点则是15。Minute()
– 返回当前的分钟数,范围是0至59。
同样地,对于任意一个日期时间变量,你可以通过一系列特定的函数提取上述信息。这些函数需要一个日期时间变量作为参数,并且操作方式与之前提到的函数相似。如果你想要从TimeLocal()
的结果中获取时间值,可以直接将TimeLocal()
的输出作为下面函数的参数:
TimeYear()
– 获取指定日期时间值的四位数年份。TimeMonth()
– 获取指定日期时间值的月份,范围是1至12。TimeDay()
– 获取指定日期时间值中的具体日期,范围是1至31。TimeDayOfWeek()
– 获取指定日期时间值对应的星期几的整数值。其中,周日为0,周一为1,以此类推。TimeHour()
– 获取指定日期时间值在24小时制下的小时数,范围是0至23。TimeMinute()
– 获取指定日期时间值中的分钟数,范围是0至59。
举例来说,假定TimeLocal()
返回的是2009年6月15日上午5点30分:
datetime CurrentTime = TimeLocal();
int GetMonth = TimeMonth(CurrentTime); // 返回6
int GetHour = TimeHour(CurrentTime); // 返回5
int GetWeekday = TimeDayOfWeek(CurrentTime); // 返回1表示周一
创建简单的定时器
在MQL4编程中,利用时间和日期功能,我们可以为专家顾问程序添加一个定时器。一些交易者倾向于只在一天中最为活跃的时段,如伦敦和纽约的交易时段,进行交易。而另一些人则可能希望避开市场波动较大时期,比如经济报告发布期间或非农就业数据(NFP)公布时刻。 实现定时器的基本思路是设定一个起始时间和一个结束时间。我们可以通过外部整型变量输入这些时间参数。接着,我们需要构造一个日期时间常量字符串,并将其转换成日期时间变量。之后,我们会将这个时间段与当前时间进行对比。如果当前时间位于该时间段内,则允许交易。 首先,我们需要定义一些外部变量来配置定时器,包括启用/禁用定时器的开关,以及选择使用本地时间或服务器时间等选项。同时,我们也需要为起始时间和结束时间分别设置月份、日期、小时和分钟:
核心思路是:通过外部参数让用户设定一个允许交易的开始时间和结束时间。EA在运行时会检查当前时间是否落在这个预设的时间区间内,以此来决定是否允许开立新的交易。
我们首先需要一个布尔型外部参数(例如 UseTradingTimer
)作为总开关,来决定是否启用时间过滤功能。如果启用,还需要一系列外部参数让用户指定开始和结束的月份、日期、小时和分钟,以及选择使用本地计算机时间还是服务器时间进行比较。
// --- 假设在EA的外部参数部分已定义 ---
// extern bool UseTradingTimer = true; // 是否启用交易时段过滤
// extern int StartMonth = 1; // 开始月份 (1-12)
// extern int StartDay = 1; // 开始日期 (1-31)
// extern int StartHour = 0; // 开始小时 (0-23)
// extern int StartMinute = 0; // 开始分钟 (0-59)
// extern int EndMonth = 12; // 结束月份 (1-12)
// extern int EndDay = 31; // 结束日期 (1-31)
// extern int EndHour = 23; // 结束小时 (0-23)
// extern int EndMinute = 59; // 结束分钟 (0-59)
// extern bool UseLocalTime = false; // true: 使用本地时间; false: 使用服务器时间
// --- 在主函数内部 ---
// bool TradeAllowed; // 假设已在OnTick()函数开头声明
if (UseTradingTimer == true) // 如果启用了交易时段过滤
{
// 1. 构建并转换开始时间
string startDateTimeString = StringFormat("%04d.%02d.%02d %02d:%02d",
Year(), StartMonth, StartDay, StartHour, StartMinute);
datetime startTime = StrToTime(startDateTimeString);
// 2. 处理结束年份(考虑跨年情况)
int endYear = Year();
// 如果开始时间是12月31日,并且结束时间设置在1月份,则结束年份应为下一年
if (StartMonth == 12 && StartDay == 31 && EndMonth == 1)
{
endYear = Year() + 1;
}
// 3. 构建并转换结束时间
string endDateTimeString = StringFormat("%04d.%02d.%02d %02d:%02d",
endYear, EndMonth, EndDay, EndHour, EndMinute);
datetime endTime = StrToTime(endDateTimeString);
// 4. 获取当前时间 (本地时间或服务器时间)
datetime currentTimeToCompare;
if (UseLocalTime == true)
{
currentTimeToCompare = TimeLocal();
}
else
{
currentTimeToCompare = TimeCurrent(); // 服务器时间 (推荐用于交易逻辑)
}
// 5. 判断当前时间是否在允许的区间内
// 条件: 开始时间 <= 当前时间 < 结束时间 (结束时间通常被视为开区间上限)
if (startTime <= currentTimeToCompare && endTime > currentTimeToCompare)
{
TradeAllowed = true; // 在允许的时间段内
}
else
{
TradeAllowed = false; // 不在允许的时间段内
}
}
else // 如果未启用交易时段过滤
{
TradeAllowed = true; // 始终允许交易
}
- 代码解释:
- 首先,我们将用户设定的开始年、月、日、时、分参数通过
StringConcatenate
(或StringFormat
)组合成一个标准日期时间格式的字符串,然后使用StrToTime()
函数将其转换为 MQL 的datetime
类型变量startTime
。 - 跨年处理:一个特殊情况是当设定的时间段跨越年份时(例如,从12月31日到下一年的1月份)。代码中有一个检查:如果开始月份是12月、开始日期是31日,并且结束月份是1月,那么结束年份
endYear
会自动设置为当前年份加1。否则,结束年份使用当前年份。 - 同样地,结束时间也被转换为
datetime
变量endTime
。 - 根据
UseLocalTime
参数的选择,获取当前的比较时间currentTimeToCompare
(本地计算机时间TimeLocal()
或交易服务器时间TimeCurrent()
)。对于交易逻辑,通常推荐使用服务器时间TimeCurrent()
以避免时区问题。 - 最后,通过
if (startTime <= currentTimeToCompare && endTime > currentTimeToCompare)
判断当前时间是否位于设定的开始时间和结束时间之间。如果是,则将布尔变量TradeAllowed
设置为true
;否则设为false
。 - 如果
UseTradingTimer
本身就为false
(即不启用时间过滤),则TradeAllowed
直接设为true
。
- 首先,我们将用户设定的开始年、月、日、时、分参数通过
-
在交易逻辑中应用
TradeAllowed
确定了TradeAllowed
的状态后,我们就可以在执行实际的开仓、平仓等交易操作之前,用它来控制是否继续执行。最简单的方法是将所有核心的交易下单逻辑包裹在一个if(TradeAllowed == true)
的代码块中:// --- 在主函数中,获取交易信号并准备下单的部分 --- // 首先执行上面的时间过滤逻辑,得到 TradeAllowed 的值 if (TradeAllowed == true) // 只有当允许交易时,才执行下面的操作 { // 例如,买入条件判断 // (假设 FastMA, SlowMA, g_BuyTicket, BuyOrderCount() 等已定义) if (FastMA > SlowMA && g_BuyTicket == 0 && BuyOrderCount(Symbol(), MagicNumber) == 0) { // ... 执行买入开仓的代码(为简洁省略) ... } // 例如,卖出条件判断 // (假设 g_SellTicket, SellOrderCount() 等已定义) if (FastMA < SlowMA && g_SellTicket == 0 && SellOrderCount(Symbol(), MagicNumber) == 0) { // ... 执行卖出开仓的代码(为简洁省略) ... } } // else (如果 TradeAllowed 为 false) // { // Print("当前不在允许的交易时间段内。"); // }
上述实现只是一个基础的日期和时间范围定时器。您还可以根据具体需求创建更复杂的定时逻辑,例如:
- 仅在每周的特定几天(例如周一到周五)进行交易。
- 设置相对于当天某个特定时间点(例如,只在亚洲时段、伦敦时段或纽约时段的特定几小时内)的交易窗口。
- 组合日期范围和每日时间范围。
这些更高级的定时器逻辑,就需要您根据 MQL 提供的日期时间函数(如 DayOfWeek()
, TimeHour()
, TimeDayOfYear()
等)自行探索和构建了。