交易和报价时段时间表
稍后,在接下来的章节中,我们将讨论 MQL5 API 函数,这些函数将实现交易操作自动化。但首先要研究平台的技术特性,这些特性决定了能否成功调用这些 API。特别是,金融工具的规范施加了一些限制。本章中,我们将逐步全面考虑这些金融工具的编程分析,我们将从交易时段开始。
在交易金融工具时,应考虑到许多国际市场(如证券交易所)都有预先确定的开放时间,只有在这些时间内才能获得信息和进行交易。尽管终端一直连接在经纪商的服务器上,但是在工作时间表之外进行交易的尝试将会失败。在这方面,对于每个交易品种,终端都会存储交易时段时间表,即一天中可以执行某些操作的时间段。
我们知道,有两种主要的交易时段类型:报价和交易。在报价交易时段内,终端会(可能会)接收当前报价。在交易时段内,允许终端发送交易指令和进行交易。在一天中,每种类型可能有几次交易时段,并有休息时间(例如,早上和晚上)。显然,报价交易时段的持续时间大于等于交易时段的持续时间。
在任何情况下,交易时段时间(即开盘和收盘时间)可由终端从交易所的本地时区转换为经纪商所处的时区(服务器时间)。
MQL5 API 允许你使用 SymbolInfoSessionQuote 和 SymbolInfoSessionTrade 函数来确定每种工具的报价和交易交易时段。特别是,这种重要信息允许程序在向服务器发送交易请求之前,检查市场当前是否开放。因此,我们可以防止必然的错误结果,并避免给服务器增加不必要的负载。请记住,如果由于 MQL 程序的实现方式不当而向服务器发出大量错误请求,服务器可能会开始“忽略”你的终端,并在一段时间内拒绝执行后续命令(即使命令正确)。
bool SymbolInfoSessionQuote (const string symbol, ENUM_DAY_OF_WEEK dayOfWeek, uint sessionIndex, datetime &from, datetime &to)
bool SymbolInfoSessionTrade (const string symbol, ENUM_DAY_OF_WEEK dayOfWeek, uint sessionIndex, datetime &from, datetime &to)
这些函数可以相同的方式工作。对于给定的 symbol 和星期几 dayOfWeek,它们会将交易时段为 sessionIndex 的开盘和收盘时间,分别填充通过引用传递的参数 from 和 to。交易时段索引从 0 开始。ENUM_DAY_OF_WEEK 结构体如 枚举一节中所述。
未提供用于查询交易时段数的单独函数:要查询的话,我们可使用递增的索引 sessionIndex 来调用 SymbolInfoSessionQuote 和 SymbolInfoSessionTrade,直到函数返回一个错误标志 (false)。若存在具有指定数量的交易时段,并且输出自变量 from 和 to 接收到正确的值,这些函会数返回一个成功指示符 (true)。
根据 MQL5 文档,在收到的 datetime 类型的 from 和 to 的值中,应忽略日期,仅考虑时间。这是因为该信息为当天的时间表。但是,这个规则有一个重要的例外。
由于市场可能一天 24 小时开放(如在外汇交易)或交易所在地球另一端,其白天的营业时间与你的经纪商“时区”的日期变化重叠,所以交易时段结束时间可能大于等于 24 小时。例如,如果外汇交易时段的开始时间为 00:00,则结束时间为 24:00。但是,从 datetime 类型的角度来看,24 小时为第二天的 00 小时 00 分钟。
如果对于某些交易所,其时间表相对于你的经纪商所在时区有几个小时的偏移,导致交易时段会在头一天开始,而在第二天结束,那么情况会更加复杂。因此,to 变量不仅记录时间,还记录不可忽略的“额外天数”,否则日间时间 from 将大于日间时间 to(例如,一个交易时段可能会从今天 21:00 持续到明天 8:00,即 21 > 8)。在这种情况下,检查当前时间是否在交易时段内(“时间 x 大于开始时间且小于结束时间”)会得出错误结论(例如,若 x = 23,x >= 21 && x < 8 条件不成立,但实际上该时段仍处于有效的交易状态)。
因此,我们得出的结论是,不能忽略参数 from/to 中的日期,算法中必须考虑这一点(参见示例)。
为了演示这些函数的功能,让我们回到脚本 EnvPermissions.mq5 中的示例,该示例出现在 权限 一节中。其中一种权限(也可以叫限制)特指交易的可用性。之前,该脚本考虑了终端设置 (TERMINAL_TRADE_ALLOWED) 和特定 MQL 程序的设置 (MQL_TRADE_ALLOWED)。现在,我们可以添加交易时段检查来确定在给定时刻对特定交易品种有效的交易许可。
该脚本的新版本名为 SymbolPermissions.mq5。这一版本也不是最终版本:在后面的章节中,我们将研究 交易账户 设置施加的限制。
注意,该脚本实现了 Permissions 类,提供了适用于 MQL 程序的所有类型权限/限制的集中说明。除此之外,该类还具有检查交易可用性的方法:isTradeEnabled 和 isTradeOnSymbolEnabled。前者与全局权限相关,并且几乎保持不变:
class Permissions
|
在检查了终端和 MQL 程序的特性之后,该脚本会继续执行 isTradeOnSymbolEnabled,对交易品种规范进行分析。以前这种方法几乎是空白。
除了在 symbol 参数中传递的工作交易品种之外,isTradeOnSymbolEnabled 函数还接收当前时间 (now) 和所需的交易模式 (mode)。我们将在后面的章节中更详细地讨论后者(参见 交易权限)。现在,我们只需要注意 SYMBOL_TRADE_MODE_FULL 的默认值给出了最大的自由度(允许所有交易操作)。
static bool isTradeOnSymbolEnabled(string symbol, const datetime now = 0,
|
如果没有指定 now 时间(默认等于 0),我们认为不必考虑交易时段。这意味着带有已经找到合适交易时段(即,包含给定时间的交易时段)指示的 found 变量被立即设置为 true。但是,如果指定了 now 参数,该函数将进入交易交易时段分析块。
为了从 datetime 类型的值中提取日期(不考虑时间),我们将 day 常量解释为等于一天中的秒数。类似 now % day 的表达式将返回完整日期和时间除以一天的持续时间所得的余数,这将只给出时间(datetime 中最重要的数字将为空)。
TimeDayOfWeek 函数返回给定 datetime 值对应的星期几。该函数位于我们之前已经使用过的 MQL5Book/DateTime.mqh 头文件中(参见 日期和时间)。
此外,在 while 循环中,我们还会调用 SymbolInfoSessionTrade 函数,同时不断递增交易时段索引 i,直到找到合适的交易时段或函数返回 false(不再有交易时段)。因此,该程序可以获得一周内每一天的完整交易时段列表,类似于在终端的交易品种 Specifications 窗口中显示的内容。
显然,合适的交易时段是包含交易时段开始时间 from 和结束时间 to 之间的指定 time 值的交易时段。此处,我们还考虑了与可能的全天候交易相关的问题:在不丢弃日期(from % day 或 to % day)的情况下,将 from 和 to 与 time 按原样比较。
一旦 found 等于 true,就会退出循环。否则,当超过允许的交易时段数时,该循环将结束(函数 SymbolInfoSessionTrade 将返回 false),并且不再寻找合适的交易时段。
如果根据交易时段时间表,现在允许进行交易,则会额外检查该交易品种的交易模式 (SYMBOL_TRADE_MODE)。例如,交易品种交易可以完全禁止(“指示性”)或处于“仅平仓”模式。
与文件 SymbolPermissions.mq5 中的最终版本相比,上面的代码有一些简化。此外,还实现了一种机制可用于标记导致交易被禁用的限制来源。所有这些来源都在 TRADE_RESTRICTIONS 枚举中进行了汇总。
enum TRADE_RESTRICTIONS
|
此时,限制可能来自 4 个实例:终端、程序、交易品种和交易时段时间表。我们稍后将添加更多选项。
为了记录在 Permissions 类中发现约束这一事实,我们使用了 lastFailReasonBitMask 变量,该变量允许使用辅助方法 pass 从枚举的元素中收集位掩码(当检查条件 value 为 false 且该位等于 false 时,设置该位)。
static uint lastFailReasonBitMask;
|
在适当的验证步骤中执行调用带有特定标志的 pass 方法。例如,完整的 isTradeEnabled 方法如下所示:
static bool isTradeEnabled(const string symbol = NULL, const datetime now = 0)
|
因此,当调用 TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 或 MQLInfoInteger(MQL_TRADE_ALLOWED) 的结果为否时,将分别设置 TERMINAL_RESTRICTION 或 PROGRAM_RESTRICTION 标志。
当检测到问题时,isTradeOnSymbolEnabled 方法还可以设置自己的标志,包括交易时段标志。
static bool isTradeOnSymbolEnabled(string symbol, const datetime now = 0,
|
因此,使用 Permissions::isTradeEnabled 查询的 MQL 程序在收到限制后,可以使用 getFailReasonBitMask 和 explainBitMask 方法来阐明其含义:前者“按原样”返回设置禁止标志的掩码,后者形成有关限制的文本说明(用户易于理解)。
static uint getFailReasonBitMask()
|
使用 OnStart 处理程序中的上述 Permissions 类,可检查 Market Watch(当前为 TimeCurrent)中所有交易品种的交易可用性。
void OnStart()
|
如果某个交易品种被禁止交易,我们会在日志中看到解释。
Trade is disabled for following symbols and origins:
|
在这种情况下,市场会对 "USDRUB" 交易品种关闭,对 "SP500m" 交易品种禁用交易(更严格地说,后者不对应于 SYMBOL_TRADE_MODE_FULL 模式)。
假设在运行脚本时,在终端中全局启用了算法交易。否则,我们还会在日志中看到 TERMINAL_RESTRICTION 和 PROGRAM_RESTRICTION 被禁止。