了解如何在MQL5中处理日期和时间(基础篇)
📘

了解如何在MQL5中处理日期和时间(基础篇)

第 1/3 篇

「在 MQL5 里把时间真正用起来」

MQL5 的时间系统以 1970-01-01 为纪元,用 datetime 类型以秒为单位累计,跟 Unix 时间戳一致。直接在代码里写 datetime t = D'2024.04.02 09:44'; 就能构造一个具体时刻,比用整数硬凑更不易出错。 想拿当前服务器时间用 TimeCurrent(),本地时间用 TimeLocal(),两者在跨时区回测时经常差出几个小时,不区分清楚会导致入场逻辑偏移。下面这段演示了怎么取当前时间并拆出小时数: [CODE]datetime now = TimeCurrent(); int hour = TimeHour(now); Print("Server hour: ", hour);[/CODE] TimeHour() 返回 0–23 的整型,配合 TimeDay、TimeMonth 可以做时段过滤,比如只在伦敦时段(hour>=7 && hour<16)允许交易。外汇与贵金属杠杆高、跳空频繁,时段过滤只是降低风险暴露概率,不等于规避了所有风险。 开 MT5 按 F4 建个脚本把上面三行贴进去编译运行,终端 Experts 标签里能看到服务器小时数打印,验证你账户服务器时区到底是哪。

MQL5 / C++
class="type">class="kw">datetime now = TimeCurrent();
class="type">int hour = TimeHour(now);
Print("Server hour: ", hour);

为什么时间处理是 MQL5 交易系统的地基

在 MT5 里,时间不是附属信息,而是驱动逻辑的核心变量。无论是择时开仓、限制交易窗口,还是按 K 线刷新触发计算,都绕不开对日期与时间的精确控制。很多策略回测和实盘表现脱节,根源常出在时区或定时器理解错误。 本篇会拆开讲清几个硬核组件:datetime 原始类型怎么存、MqlDateTime 结构怎么拆字段、OnTimer 事件怎么周期性跑任务,以及三个可直接抄进 EA 的实战应用——NewBar 判新柱、时间过滤器、tradeAtTime 定点交易。 外汇与贵金属杠杆高、波动快,时间错配可能让策略在错误时段成交,风险完全由使用者自担。把这套时间机制吃透,你才有能力在 MT5 里验证自己的择时假设。

◍ 用 datetime 把时间当数字算

MT5 里的 datetime 本质是 Unix 时间戳,即从 1970.01.01 00:00 GMT 起算的秒数。比如 2023.01.01 00:00:00 GMT 对应 1672531200 秒,一天固定是 86400 秒,一个月约 2629743 秒——这些常数直接决定了你做加减时的偏移量。 写时间常量用 D'yyyy.mm.dd hh:mm:ss' 或 D'dd.mm.yyyy hh:mm:ss' 都行,编译器认两种。下面这段声明值得直接贴进 MT5 看警告:D'' 会被填成编译时刻,D'12:30:27' 只剩时分秒时也会自动补上编译日期,容易埋坑。 [CODE] datetime NY=D'2015.01.01 00:00'; // 2015年起始时刻 datetime d1=D'1980.07.19 12:30:27'; // 年 月 日 时 分 秒 完整写法 datetime d2=D'19.07.1980 12:30:27'; // 与 d1 等价,日.月.年格式 datetime d3=D'19.07.1980 12'; // 等价 D'1980.07.19 12:00:00' datetime d4=D'01.01.2004'; // 等价 D'01.01.2004 00:00:00' datetime compilation_date=__DATE__; // 编译日期 datetime compilation_date_time=__DATETIME__; // 编译日期与时间 datetime compilation_time=__DATETIME__-__DATE__; // 编译时间(差值) datetime warning1=D'12:30:27'; // 等价 D'[编译日期] 12:30:27' datetime warning2=D''; // 等价 __DATETIME__ [/CODE] 比较两段输入时间,只需各自建 datetime 变量后在 OnTick 里用 > < 判断。下方代码挂上 EA,把 inpDate1 设成 2023.09.01、inpDate2 设成 2023.09.30,跑出来必打印“Input two is a recent time”;对调后则出“Input one is a recent time”。 [CODE] 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"); } } [/CODE] 加减法就是纯秒数运算:oldDate 为 D'2023.09.01 00:00' 时,oldDate+86400 得到次日同时刻,oldDate-86400 回退一天。外汇与贵金属行情受时段跳空影响,用这种偏移算历史窗口时,要留意周末和节假导致的时间断层。 转字符串用 TimeToString,反向解析用 StringToTime,两者在把日志时间转成可读格式、或把界面输入转回时间戳时高频用到。开 MT5 新建脚本跑一遍这两个函数,比看文档直观得多。

MQL5 / C++
class="type">class="kw">datetime NY=D&class="macro">#x27;class="num">2015.01.class="num">01 class="num">00:class="num">00&class="macro">#x27;;
 class="type">class="kw">datetime d1=D&class="macro">#x27;class="num">1980.07.class="num">19 class="num">12:class="num">30:class="num">27&class="macro">#x27;;
 class="type">class="kw">datetime d2=D&class="macro">#x27;class="num">19.07.class="num">1980 class="num">12:class="num">30:class="num">27&class="macro">#x27;;
 class="type">class="kw">datetime d3=D&class="macro">#x27;class="num">19.07.class="num">1980 class="num">12&class="macro">#x27;;
 class="type">class="kw">datetime d4=D&class="macro">#x27;class="num">01.01.class="num">2004&class="macro">#x27;;
 class="type">class="kw">datetime compilation_date=__DATE__;
 class="type">class="kw">datetime compilation_date_time=__DATETIME__;
 class="type">class="kw">datetime compilation_time=__DATETIME__-__DATE__;
 class="type">class="kw">datetime warning1=D&class="macro">#x27;class="num">12:class="num">30:class="num">27&class="macro">#x27;;
 class="type">class="kw">datetime warning2=D&class="macro">#x27;&class="macro">#x27;;
class="kw">input class="type">class="kw">datetime inpDate1 = D&class="macro">#x27;class="num">2023.09.class="num">01 class="num">00:class="num">00&class="macro">#x27;;
class="kw">input class="type">class="kw">datetime inpDate2 = D&class="macro">#x27;class="num">2023.09.class="num">30 class="num">00:class="num">00&class="macro">#x27;;
class="type">void OnTick()
  {
   if(inpDate1>inpDate2)
     {
      Print("Input one is a recent time");
     }
   else
     if(inpDate1<inpDate2)
       {
        Print("Input two is a recent time");
       }
  }

「用时间戳做加减与互转」

MT5 里 datetime 本质是从 1970-01-01 算起的秒数,直接加减整数就是在挪日期。下面这段在 OnTick 里给 oldDate(2023.09.01) 加 86400 秒得到次日,给 newDate(2023.09.30) 减 86400 秒得到前日,Print 出来的两个值差了 29 天整数倍,验证时间轴是连续秒计数。 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); } 另一段演示字符串与时间戳互转:oldDate 用 TimeToString 变成可读文本,newDate 这种 "2023.09.30 00:00" 字符串用 StringToTime 反向解析回 datetime。做跨周期回测或把 CSV 里的时间列喂给指标时,这套转换是必过的关卡。 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); } 外汇与贵金属行情受跳空影响,用秒级加减推演历史 K 线位置时,周末和节假日不计入报价但时间戳仍连续,回测里可能偏离实盘,属高风险盲区。

MQL5 / C++
class="kw">input class="type">class="kw">datetime newDate = D&class="macro">#x27;class="num">2023.09.class="num">30 class="num">00:class="num">00&class="macro">#x27;;
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   class="type">class="kw">datetime addToDate =  oldDate+class="num">86400;
   Print("Result of date addition - ",addToDate);
   class="type">class="kw">datetime subtractFromDate =  newDate-class="num">86400;
   Print("Result of date subtraction - ",subtractFromDate);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 dateTimeConvert.mq5 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">datetime oldDate = D&class="macro">#x27;class="num">2023.09.class="num">01 class="num">00:class="num">00&class="macro">#x27;;
class="kw">input class="type">class="kw">string newDate = "class="num">2023.09.class="num">30 class="num">00:class="num">00";
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   class="type">class="kw">string newOldDate = TimeToString(oldDate);
   class="type">class="kw">datetime newNewDate = StringToTime(newDate);
   Print("Time To String - ",newOldDate);
   Print("String To Time - ",newNewDate);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

拆开 datetime 的内部骨架

MQL5 里那个看着像黑盒的 datetime,其实可以整块拆进 MqlDateTime 结构里读。这个结构有 8 个 int 字段:年、月、日、时、分、秒,外加星期几和年内第几天(1 月 1 日记为 0)。 要把一个 datetime 值灌进去,用 TimeToStruct(dt, struct) 就行,返回布尔表示转换是否成功;反过来 StructToTime(struct) 只吃结构、吐回带秒数的 datetime。下面这段 EA 把 D'2023.10.10 07:07:07' 拆开打印,跑完能在日志看到 year: 2023、month: 10、day: 10、hour: 7、minute: 7、second: 7,day_of_week 是 2(周二),day_of_year 是 282。 边界兜底有点反直觉:月或日输入小于 1 时默认补 01,大于上限则月封顶 12、日封顶 31;小时限在 0–23,分钟限在 0–59。写时间解析逻辑时别假设用户输入都合法,靠这个默认规则可能掩盖脏数据。 外汇与贵金属行情受杠杆与跳空影响,时间解析错乱会让止损或定时触发逻辑偏移,实盘前务必在 MT5 策略测试器用非常规时间输入跑一遍上面代码验证兜底行为。

MQL5 / C++
class="kw">struct class="type">MqlDateTime
  {
   class="type">int year;            class=class="str">"cmt">// Year
   class="type">int mon;             class=class="str">"cmt">// Month
   class="type">int day;             class=class="str">"cmt">// Day
   class="type">int hour;            class=class="str">"cmt">// Hour
   class="type">int min;             class=class="str">"cmt">// Minutes
   class="type">int sec;             class=class="str">"cmt">// Seconds
   class="type">int day_of_week;     class=class="str">"cmt">// Day of week(class="num">0-Sunday, class="num">1-Monday, ... ,class="num">6-Saturday)
   class="type">int day_of_year;     class=class="str">"cmt">// Day number of the year(January 1st is assigned the number value of zero)
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                            class="type">MqlDateTime.mq5 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">datetime dtData = D&class="macro">#x27;class="num">2023.10.class="num">10 class="num">07:class="num">07:class="num">07&class="macro">#x27;;
class="type">void OnTick()
  {
   class="type">MqlDateTime timeStruct;
   TimeToStruct(dtData,timeStruct);
   class="type">int year = timeStruct.year;
   Print("year: ",year);
   Print("=====================");
   class="type">int month = timeStruct.mon;
   Print("month: ",month);
   Print("=====================");
   class="type">int day = timeStruct.day;
   Print("day: ",day);
   Print("=====================");
   class="type">int hour = timeStruct.hour;
   Print("hour: ",hour);
   Print("=====================");
   class="type">int minute = timeStruct.min;
   Print("minute: ",minute);
   Print("=====================");
   class="type">int second = timeStruct.sec;
   Print("second: ",second);
   Print("=====================");
   class="type">int dayofWeek = timeStruct.day_of_week;
   Print("dayofWeek: ",dayofWeek);
   Print("=====================");
   class="type">int dayofYear = timeStruct.day_of_year;
   Print("dayofYear: ",dayofYear);
   }

常见问题

用 TimeCurrent() 取当前时间,配合 iTime() 取当日首根K线时间,两者相减除以 PeriodSeconds() 得到差值再取整即可。
datetime 按服务器时间连续计秒,周末市场停盘不产生新K线但时间仍在走,用柱数而非时间差算时长更稳。
可以,把小布接进你的策略页,它能基于AIGC读出你时间条件里的时区与边界漏洞并标红提示。
用 TimeHour() 和 TimeMinute() 从 datetime 拆出时分,判断 Hour==8 且 Minute>=30(以经纪商服务器时区为准)。
不是存储问题,是回测用历史服务器时间、实盘用实时服务器时间,需在代码里统一用 TimeCurrent() 而非本地时间。