Ограничения и разрешения для операций по счету

Среди свойств счета имеется несколько, представляющих собой ограничения на торговые операции, включая и полный их запрет. Все эти свойства относятся к перечислению ENUM_ACCOUNT_INFO_INTEGER и являются логическими флагами, за исключением ACCOUNT_LIMIT_ORDERS.

Идентификатор

Описание

ACCOUNT_TRADE_ALLOWED

Разрешение торговли на текущем счете

ACCOUNT_TRADE_EXPERT

Разрешение алгоритмической торговли экспертами и скриптами

ACCOUNT_LIMIT_ORDERS

Максимально допустимое количество действующих отложенных ордеров

ACCOUNT_FIFO_CLOSE

Признак того, что позиции нужно закрывать только по правилу FIFO

Поскольку наша книга посвящена программированию на MQL5, что включает и алготрейдинг, следует отметить, что запрет ACCOUNT_TRADE_EXPERT является столь же критическим, как и общий запрет на торговлю, когда ACCOUNT_TRADE_ALLOWED равно false. Брокер имеет возможность запретить торговлю через эксперты и скрипты, но при этом оставить торговлю вручную.

Свойство 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// запрет пользователем для всех программ
      PROGRAM_RESTRICTION = 2,  // запрет пользователем для конкретной программы
      SYMBOL_RESTRICTION = 4,   // символ не торгуется согласно спецификации
      SESSION_RESTRICTION = 8,  // рынок закрыт согласно расписанию сессий
      ACCOUNT_RESTRICTION = 16// пароль инвестора или ограничение брокера
      EXPERTS_RESTRICTION = 32// брокер запретил алготрейдинг
   };
   ...

В ходе проверки 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;
      // проверка торговых сессий символа и установка found в true,
      // если время now попало внутрь одной из сессий
      ...
      
      // в дополнение к сессиям проверяем режим торговли
      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));
   }
   ...

Наконец, общая проверка по всем потенциальным "инстанциям", включая (помимо предыдущих уровней) настройки терминала и программы, осуществляется в методах getTradeRestrictions и isTradeEnabled.

   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()) // проверка для текущего символа, по умолчанию
   {
      Print("Trade is disabled for 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-программе позволит избежать серийных неудачных попыток отправки торговых приказов.