MQL5 编程基础:时间·进阶篇
(2/3)· 多数 EA 在周末与夏令时切换时莫名错单,根因往往出在时间的取法上
图表上跳空的 M1 柱、周日假柱、服务器与本地三套时区——把时间当普通变量读,回测漂亮实盘翻车。本文接上篇概念,继续把 MQL5 里时间的真实脾气拆开看。
◍ GMT 与 UTC 的差从哪来
早期 MT5 里很多旧 EA 把 GMT 当基准时区,但地球自转有微小不均匀,GMT 本质还是太阳日近似。现在全球主标准是 UTC,由原子钟维持,且 UTC 本身不受夏令时约束,只是各地区显示时再叠加夏令偏移。 基于太阳日的 GMT 和基于原子钟的 UTC 并不同步,实测大约每 500 天会累积出 1 秒左右偏差。闰秒补偿固定在 6 月 30 日或 12 月 31 日插入 1 秒。 做跨时区贵金属或外汇回测时,若历史数据时间戳标的是 GMT 而平台用 UTC,长周期可能错位几根 K 线。开 MT5 对着 2020—2024 的日线,手动比对 GMT/UTC 切换点,确认你的数据源标注口径。
「MT5里日期时间字符串怎么写才不踩坑」
不同地区的日期书写习惯差异很大,俄罗斯常用 01.12.2012 表示 2012年12月1日,美国则是 12/01/2012 的月/日/年结构,分隔符还有圆点、斜杠、破折号三种。时间部分相对统一,用冒号隔开时分秒,例如 12:15:30。 在 MQL5 中描述格式有一套简写规则:dd.mm.yyyy 要求日、月都补零到两位、年份四位;d-m-yy 则允许日和月是一位数、年份两位,如 1-12-12 仍指代 2012年12月1日。时间用 h/m/s 代表时分秒,hh:mi:ss 强制小时和分钟补零。 从文件管理角度看,yyyy.mm.dd hh:mi:ss 是最稳妥的标记法。若你每天把日志写进同一文件夹,用这个格式命名文件,系统按名称排序就直接等于时间先后排序,不用额外解析。 外汇与贵金属行情受时点影响明显,这类时间戳在高波动时段可能错位,实操前请在 MT5 用脚本打印几次本地与服务器时间验证。
datetime 背后的秒数刻度
MQL5 里的时间不是日历概念,而是从 1970 年 1 月 1 日 00:00 起累计的秒数,也就是 Unix 时间戳。做回测或跨周期比对时,直接用秒数相减就能拿到间隔,比肉眼读字符串可靠。 存储时间要用 datetime 类型。它的下界是 0,正好对应时间戳起点;上界为 32 535 244 799,等于 3000 年 12 月 31 日 23:59:59。写 EA 时若手动赋极值做占位,记住这个上限,别拿 2^31-1 去套,否则在 MT5 里会溢出或类型告警。 开 MT5 敲一行 Print((datetime)32535244799); 就能看到 3000.12.31 23:59:59 的回显,验证你对 datetime 边界的理解没跑偏。外汇与贵金属杠杆高,时间函数用错可能导致订单时间过滤失效,实盘前务必在策略测试器跑一遍边界值。
◍ 抓准 MT5 里的服务器时间
在 MQL5 里拿当前服务器时间,最直接的是 TimeCurrent()。它返回的是终端已知最新的服务器时间,这个值跟图表上每根 K 线的时间戳同源。 需要注意,TimeCurrent() 并不是墙上时钟的镜像。它取决于 Market Watch 里任一品种的最后一次报价变动。只要窗口里挂着若干个活跃品种,基本就等于实时服务器时间;但周末报价静止,函数吐出来的时间会和真实服务器时间差出几十个小时。 如果只关心当前品种的最后报价时刻,用 SymbolInfoInteger(_Symbol, SYMBOL_TIME) 更干净,不受其他品种干扰。外汇与贵金属杠杆高、跳空频繁,用错时间源可能在跨周逻辑上踩坑。 下面两段代码分别演示两种取法,Alert 会弹出具体时间戳,开 MT5 新建脚本粘进去就能跑:
class="type">class="kw">datetime tm=TimeCurrent(); class=class="str">"cmt">//--- output result Alert(tm); class="type">class="kw">datetime tm=(class="type">class="kw">datetime)SymbolInfoInteger(_Symbol,SYMBOL_TIME); class=class="str">"cmt">//--- output result Alert(tm);
「本地时间只在少数场景才需要手动取」
MT5 里读用户 PC 时钟用的是 TimeLocal(),返回的是 datetime 类型。实际写 EA 或指标时,绝大多数逻辑都该用服务器时间,避免本地时钟被改导致信号错位。 本地时间真正的价值在提醒和日志可读性上——你看到一条 Alert,本能会拿它和右下角系统时间比对,判断这条消息是几分钟前触发的。但终端对 Alert() 和 Print() 的输出会自动带时间戳,所以手动调 TimeLocal() 的场景其实很少。 只有在你想自定义日志格式、或者做跨时区提醒文案时,才值得专门取一次本地时间。外汇和贵金属波动受多地会话影响,时区错配可能让策略在错误窗口成交,这类细节高风险,验证前先确认终端时区设置。
class="type">class="kw">datetime tm=TimeLocal(); class=class="str">"cmt">//--- output result Alert(tm);
时间变量打印时的格式陷阱
在 MT5 里用 Alert() 把 datetime 变量直接丢进去,看到的是 "2012.12.05 22:31:57" 这种可读格式,本质是函数内部把入参强转成了 string。Print()、Comment() 以及写 txt/csv 文本时同理,类型转换由系统代劳。 但一旦你打算自己拼文本消息,datetime 怎么变成字串就得自己管。要格式化时间就直接 (string) 强转;要拿到秒数之类的数值,得先 (long) 再 (string),否则出来的不是你要的数字。 long 和 ulong 的取值范围覆盖 datetime,所以也能存时间。只是存成 long 后想看格式化时间,必须绕一圈:(datetime) 转回再去 (string);只要数值的话,(string) 一步就行。下面两段代码在 MT5 里跑一下,Alert 弹窗会同时给你两种形态,对照着看就懂差异。 外汇与贵金属行情受杠杆与跳空影响,时间解析出错可能导致信号误判,实盘前务必在策略测试器里验证输出。
class="type">class="kw">datetime tm=TimeCurrent(); class=class="str">"cmt">//--- output result Alert("Formatted: "+(class="type">class="kw">string)tm+", in seconds: "+(class="type">class="kw">string)(class="type">long)tm); class="type">long tm=TimeCurrent(); class=class="str">"cmt">//--- output result Alert("Formatted: "+(class="type">class="kw">string)(class="type">class="kw">datetime)tm+", in seconds: "+(class="type">class="kw">string)tm);