- 本地时间和服务器时间
- 夏令时(本地)
- 世界时
- 暂停程序
- 时间间隔计数器
夏令时(本地)
为确定本地时钟是否已切换至夏令时,MQL5 提供了 TimeDaylightSavings 函数。该函数会获取你的操作系统的设置。
确定服务器上的夏令时时间并非易事。为此,你需要对 报价、 财经日历 事件或展期交割/掉期时间(在 账户交易历史记录中)实施 MQL5 分析。在下面的示例中,我们将显示其中一种方案。
int TimeDaylightSavings()
该函数返回秒数修正(如果已应用了夏令时)。冬季时间是每个时区的标准时间,因此该期间的修正为零。条件性形式的获取修正的公式可编写如下:
TimeDaylightSavings() = TimeLocal winter() - TimeLocal summer() |
例如,如果标准时区 (winter) 等于 UTC+3(即时区时间比 UTC 早 3 小时),则在转换到夏令时 (summer) 的过程中,我们加 1 小时,得到 UTC+4。其中 TimeDaylightSavings 将返回 -3600。
该函数的一个使用示例在 TimeSummer.mq5 脚本中提供,其中还给出了一种识别服务器上适当模式的可能实证方法。
void OnStart()
|
首先,我们显示所有类型的时间以及 MQL5 提供的修正(函数 TimeGMT 和 TimeGMTOffset 将在关于 世界时间的下一节中探讨,但从前面的描述中我们应该已经大致清楚它们的含义了)。
该脚本应在交易日运行。日志中的条目将对应于你的计算机和经纪商的服务器的设置。
TimeLocal()=2021.09.09 22:06:17 / ok
|
在本例中,客户的时区与 GMT 相差 3 小时 (UTC+3),没有夏令时调整。
现在我们看看服务器的情况。基于 TimeCurrent 函数的值,我们可以确定服务器的当前时间,但不能确定其标准时区,因为该时间可能涉及夏令时转换(MQL5 不提供关于是否使用夏令时以及是否当前已启用夏令时的信息)。
为确定服务器的真实时区和夏令时,我们将利用服务器时间转换会影响报价这一事实。与大部分用于解决问题的实证方法一样,这一实证方法在某些情况下可能无法给出完全正确的结果。如果与其它来源的比较显示不一致,则应选择不同的方法。
外汇市场在 UTC 时间星期六 22:00 点开盘(这对应于亚太地区早盘开始),在星期五 22:00 点关闭(美洲交易收盘)。这表示在 UTC+2 时区(东欧)的服务器上,第一柱线将在星期一 0 时 0 分准时出现。根据中欧时间(对应于 UTC+1),交易周在星期六 23:00 点开始。
在计算了每周末暂停后第一柱线 H1 的当天偏移统计之后,我们将获得服务器时区的估计结果。当然,为此最好使用流动性最强的外汇金融工具 EURUSD。
如果在年度期间的统计中找到两个最大当日偏移,并且它们的位置彼此相邻,这就表示经纪商正在切换到夏令时,反之亦然。
请注意,夏季和冬季时间周期不相等。因此,若在三月初切换到夏季时间而在十一月初返回冬季时间时,我们得到约 8 个月的夏季时间。这将影响统计中最大值的比率。
有了两个时区之后,我们就可以轻松确定当前处于活动状态的时区,进而确定当前是否存在夏令时修正。
将时钟切换到夏令时,经纪商的时区将从 UTC+2 变更为 UTC+3,将把周开始时间从 22:00 点偏移到 21:00 点。这将影响 H1 柱线的结构:在图表上,我们将看到在星期天晚上有三个柱线,而不是两个。
EURUSD H1 图表上从冬季时间 (UTC+2) 到夏季 (UTC+3) 时间的变化
为实现这点,我们有一个单独函数 ServerTimeZone。对内置函数 CopyTime 的调用负责获取报价,或者更准确的说,获取柱线时间戳(我们将在关于 访问时间序列的章节学习该函数)。
ServerTime ServerTimeZone(const string symbol = NULL)
|
CopyTime 函数接收工作金融工具、H1 时间范围以及去年的日期范围作为参数。代替金融工具的 NULL 值表示脚本放置位置的当前图表交易品种,因此,建议选择有 EURUSD 的窗口。你可能猜到了,PERIOD_H1 常量对应于 H1。我们已经熟悉了 TimeCurrent 函数:返回当前最新的已知服务器时间。如果我们从其减去一年中的秒数(该秒数放在 year 变量中),我们将得到正好一年前的时期和时间。结果将进入 array 中。
为统计每周某跟柱线在特定小时开盘的次数,我们保留了 hours[24] 数组。将通过遍历生成的 array 来执行计算,即,按从过去到现在的柱线计算。每次迭代时,被查看的该周的开盘时间将存储在 current 变量中。当循环结束时,服务器的当前时区将保持在 current 中,因为当前周将最后处理。
// (-v-) cycle through H1 bars
|
在天数循环中,我们将使用来自头文件 MQL5Book/DateTime.mqh 的 datetime 类(参见 日期和时间)。
// (-v-) processing the i-th bar H1
|
建议的算法不是最优的,但不要求我们理解时间序列组织方式的技术细节,这些内容我们尚未学习。
有些周没有设置格式(在节假日之后开始)。如果此情况发生在最后一周,则 current 变量将包含异常偏移。这可通过统计来验证:对于得到的小时数,记录的每周“开盘”次数将非常少。在此情况下,在测试脚本中,会直接在日志中显示一条消息。在实践中,应核实前一到两周的标准开盘时间。
// (-V-) cycle through H1 bars
|
如果经纪商未切换到夏令时,则统计数据中将出现一个最大值,其将包含所有或几乎所有的周。如果经纪商实行时区更改,则在统计中将会有两个高值。
// find the most frequent time shift
|
我们需要确定第二个极高值的重要性(即区别于可能导致周初开盘时间偏移的随机假期)。为此,我们评估一年的一个季度(52 周/4)的统计数据。如果超过了该限值,则经纪商支持夏令时。
int DST = 0;
|
如果当前周的开盘偏移(存储在当前变量中)与两个极高值之一重合,则当前周正常开盘,可据此推断时区(这一保护性条件是必要的,因为我们对于非标准周没有修正,而是仅会发出一个警告)。
现在一切就绪,可以确定我们的函数回应:服务器时区和启用夏令时的标志。
current +=2 +DST;// +2 to get offset from UTC
|
由于我们需要从函数返回两个特性(current 和 DST),并且除此之外,我们可以告知调用代码,经纪商是否使用夏令时(即使现在是冬季),因此声明一个具有所有要求字段的特殊结构体 ServerTime 是合理的。
struct ServerTime
|
然后,在 ServerTimeZone 函数中,我们可以填充和返回由此得到的这样一个结构体。
ServerTime st = {};
|
如果因某些原因该函数不能获取到报价,则返回一个空结构体。
ServerTime ServerTimeZone(const string symbol = NULL)
|
我们来以实操方式检查新函数,我们为该新函数在 OnStart 中添加以下指令:
...
|
我们来看看可能的结果。
CopyTime(symbol,PERIOD_H1,TimeCurrent()-year,TimeCurrent(),array)=6207 / ok
|
根据为 H1 柱线收集的统计数据,该经纪商的周开盘严格在星期一 00:00。因此,真实时区等于 UTC+2,,并且没有夏季时间修正,即服务器时间必须匹配 EET (UTC+2)。然而在实践中,如同我们在日志第一部分中看到的,服务器上的时间与 GMT 相差 3 小时。
这里我们可以假定我们碰到了一个全年以夏季时间工作的服务器。在此情况下,函数 ServerTimeZone 便无法区分修正和“时区”中的额外小时:结果,DST 模式将等于零,而根据服务器报价计算得出的 GMT 时间将比真实时间向右偏移一小时。我们最初假设的“报价从星期天 22:00 点开始”与该服务器的运行模式不符。此类问题应通过经纪商的支持服务进行澄清。