账户操作的限制和权限

在账户的特性中,有一些是对交易操作的限制,包括完全禁用交易。所有这些特性都属于 ENUM_ACCOUNT_INFO_INTEGER 枚举,并且为布尔标志,ACCOUNT_LIMIT_ORDERS 除外。

标识符

说明

ACCOUNT_TRADE_ALLOWED

允许在当前账户上进行交易

ACCOUNT_TRADE_EXPERT

允许使用 EA 交易和脚本进行算法交易

ACCOUNT_LIMIT_ORDERS

有效挂单的最大允许数量

ACCOUNT_FIFO_CLOSE

仅根据 FIFO 规则进行平仓的要求

由于本书为关于 MQL5 编程的指南,其中包括算法交易,所以需要注意的是,当 ACCOUNT_TRADE_ALLOWED 等于 false 时,将全面禁止交易,而禁用 ACCOUNT_TRADE_EXPERT 权限的重要性与之相当。经纪商可禁止使用 EA 交易和脚本进行交易,但允许手动交易。

如果使用投资密码连接到账户,ACCOUNT_TRADE_ALLOWED 特性通常等于 false

如果 ACCOUNT_FIFO_CLOSE 特性值为 true,则每个交易品种的头寸只能按其开仓时的相同顺序平仓,即首先平仓最旧的订单,然后平仓较新的订单,依此类推,直到最后一个订单。如果试图以不同顺序平仓,则会收到错误。对于没有头寸对冲的账户,即,如果 ACCOUNT_MARGIN_MODE 特性不等于 ACCOUNT_MARGIN_MODE_RETAIL_HEDGING,则 ACCOUNT_FIFO_CLOSE 特性始终为 false

权限交易和报价时段时间表 章节中,我们已经开始开发一个类来检测 MQL 程序可用的交易操作。现在我们可以用账户权限检查作为其补充,并将其完善为最终版本 (Permissions.mqh) 中。

TRADE_RESTRICTIONS 枚举中提供了限制级别,在添加了两个与账户特性相关的新元素后,该枚举采用以下形式。

class Permissions
{
   enum TRADE_RESTRICTIONS
   {
      NO_RESTRICTIONS = 0,
      TERMINAL_RESTRICTION = 1// user's restriction for all programs
      PROGRAM_RESTRICTION = 2,  // user's restriction for a specific program
      SYMBOL_RESTRICTION = 4,   // the symbol is not traded according to the specification
      SESSION_RESTRICTION = 8,  // the market is closed according to the session schedule
      ACCOUNT_RESTRICTION = 16// investor password or broker restriction
      EXPERTS_RESTRICTION = 32// broker restricted algorithmic trading
   };
   ...

在检查过程中,由于各种原因,MQL 程序可能会检测到若干限制,因此这些元素由单独的位进行编码。最终结果可以代表它们的叠加状态。

最后两个限制刚好与新特性相对应,并在 getTradeRestrictionsOnAccount 方法中设置。检测到的限制的通用位掩码(如果有的话)在 lastRestrictionBitMask 变量中形成。

private:
   static uint lastRestrictionBitMask;
   static bool pass(const uint bitflag
   {
      lastRestrictionBitMask |= bitflag;
      return lastRestrictionBitMask == 0;
   }
   
public:
   static uint getTradeRestrictionsOnAccount()
   {
      return (AccountInfoInteger(ACCOUNT_TRADE_ALLOWED) ? 0 : ACCOUNT_RESTRICTION)
         | (AccountInfoInteger(ACCOUNT_TRADE_EXPERT) ? 0 : EXPERTS_RESTRICTION);
   }
   
   static bool isTradeOnAccountEnabled()
   {
      lastRestrictionBitMask = 0;
      return pass(getTradeRestrictionsOnAccount());
   }
   ... 

如果调用代码与限制的原因无关,而只需要确定执行交易操作的可能性,那么使用 isTradeOnAccountEnabled 方法会更方便,该方法可返回一个布尔符号 (true/false)。

交易品种和终端特性的检查已根据类似的原则进行了重新组织。例如,getTradeRestrictionsOnSymbol 方法包含以前版本的类中已经熟悉的源代码(检查交易品种的交易会话和交易模式),但返回一个标志掩码。如果至少有一位被设置,则该方法描述限制的来源。

   static uint getTradeRestrictionsOnSymbol(const string symboldatetime now = 0,
      const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
   {
      if(now == 0now = TimeTradeServer();
      bool found = false;
      // checking the symbol trading sessions and setting 'found' to 'true',
      // if the 'now' time is inside one of the sessions
      ...
      
      // in addition to sessions, check the trading mode
      const ENUM_SYMBOL_TRADE_MODE m = (ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(symbolSYMBOL_TRADE_MODE);
      return (found ? 0 : SESSION_RESTRICTION)
         | (((m & mode) != 0) || (m == SYMBOL_TRADE_MODE_FULL) ? 0 : SYMBOL_RESTRICTION);
   }
   
   static bool isTradeOnSymbolEnabled(const string symbolconst datetime now = 0,
      const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
   {
      lastRestrictionBitMask = 0;
      return pass(getTradeRestrictionsOnSymbol(symbolnowmode));
   }
   ...

最后,在 getTradeRestrictionsisTradeEnabled 方法中执行对所有潜在“实例”的一般检查,包括(除了前面的水平之外)终端和程序的设置。

   static uint getTradeRestrictions(const string symbol = NULLconst datetime now = 0,
      const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
   {
      return (TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? 0 : TERMINAL_RESTRICTION)
          | (MQLInfoInteger(MQL_TRADE_ALLOWED) ? 0 : PROGRAM_RESTRICTION)
          | getTradeRestrictionsOnSymbol(symbol == NULL ? _Symbol : symbolnowmode)
          | getTradeRestrictionsOnAccount();
   }
   
   static bool isTradeEnabled(const string symbol = NULLconst datetime now = 0,
      const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
   {
      lastRestrictionBitMask = 0;
      return pass(getTradeRestrictions(symbolnowmode));
   }

脚本 AccountPermissions.mq5 演示了使用新类对交易权限全面检查。

#include <MQL5Book/Permissions.mqh>
   
void OnStart()
{
   PrintFormat("Run on %s"_Symbol);
   if(!Permissions::isTradeEnabled()) // checking for current character, default
   {
      Print("Trade is disabled for the following reasons:");
      Print(Permissions::explainLastRestrictionBitMask());
   }
   else
   {
      Print("Trade is enabled");
   }
}

如果发现了限制,可以使用 explainLastRestrictionBitMask 方法以清晰的字符串表示形式显示其位掩码。

以下是一些脚本的结果。在前两种情况下,交易在终端的全局设置中被禁用(特性 TERMINAL_TRADE_ALLOWED 和 MQL_TRADE_ALLOWED 等于 false,对应于 TERMINAL_RESTRICTION 和 PROGRAM_RESTRICTION 位)。

当在市场关闭期间在 USDRUB 上运行时,我们还会收到 SESSION_RESTRICTION:

Trade is disabled for USDRUB following reasons:
TERMINAL_RESTRICTION PROGRAM_RESTRICTION SESSION_RESTRICTION 

对于完全禁止交易的交易品种 SP500m,会出现 SYMBOL_RESTRICTION 标志。

Trade is disabled for SP500m following reasons:
TERMINAL_RESTRICTION PROGRAM_RESTRICTION SYMBOL_RESTRICTION SESSION_RESTRICTION 

最后一点,如果已经允许在终端中交易,但已经使用投资者的密码登录账户,我们将在任何交易品种上看到 ACCOUNT_RESTRICTION。

Run on XAUUSD
Trade is disabled for following reasons:
ACCOUNT_RESTRICTION 

MQL 程序中的早期权限检查有助于避免连续发送交易指令失败的情况。