Обзор функций получения свойств символа

Полная спецификация каждого символа может быть получена путем запроса его свойств: для этой цели в MQL5 API предусмотрено 3 функции — SymbolInfoInteger, SymbolInfoDouble, SymbolInfoString, — каждая из которых ответственна за свойства конкретного типа. Сами свойства описаны как элементы трех перечислений — ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE и ENUM_SYMBOL_INFO_STRING, соответственно. Аналогичный технический прием использован в уже знакомых нам API графиков и объектов.

В любую из функций передаются имя символа и идентификатор запрашиваемого свойства.

Каждая из функций представлена в 2-х формах — сокращенной и полной. Сокращенная непосредственно возвращает запрошенное свойство, а полная записывает его в выходной параметр, переданный по ссылке. Например, для свойств, совместимых с целым типом, функции имеют такие прототипы:

long SymbolInfoInteger(const string symbol, ENUM_SYMBOL_INFO_INTEGER property)

bool SymbolInfoInteger(const string symbol, ENUM_SYMBOL_INFO_INTEGER property, long &value)

Вторая форма возвращает логический признак успеха (true) или ошибки (false). К наиболее возможным причинам, по которым функция может вернуть false, относятся неправильное имя символа (MARKET_UNKNOWN_SYMBOL, 4301) или неверный идентификатор запрошенного свойства (MARKET_WRONG_PROPERTY, 4303) — их можно уточнить с помощью _LastError.

Как и ранее, свойства в перечислении ENUM_SYMBOL_INFO_INTEGER относятся к различным типам, совместимым с целым числом: bool, int, long, color, datetime и специальные перечисления (все они будут рассмотрены в отдельных разделах).

Для свойств с типом вещественных чисел определены следующие 2 формы функции SymbolInfoDouble.

double SymbolInfoDouble(const string symbol, ENUM_SYMBOL_INFO_DOUBLE property)

bool SymbolInfoDouble(const string symbol, ENUM_SYMBOL_INFO_DOUBLE property, double &value)

Наконец, для строковых свойств аналогичные функции выглядят так:

string SymbolInfoString(const string symbol, ENUM_SYMBOL_INFO_STRING property)

bool SymbolInfoString(const string symbol, ENUM_SYMBOL_INFO_STRING property, string &value)

Свойства различных типов, наиболее востребованные при будущей разработке экспертов, логически сгруппированы в описаниях следующих разделов этой главы.

На основе вышеописанных функций создадим универсальный класс SymbolMonitor (файл SymbolMonitor.mqh) для получения любых свойств символа. Его основу будет составлять набор перегруженных методов get для 3 перечислений.

class SymbolMonitor
{
public:
   const string name;
   SymbolMonitor(): name(_Symbol) { }
   SymbolMonitor(const string s): name(s) { }
   
   long get(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      return SymbolInfoInteger(nameproperty);
   }
   
   double get(const ENUM_SYMBOL_INFO_DOUBLE propertyconst
   {
      return SymbolInfoDouble(nameproperty);
   }
   
   string get(const ENUM_SYMBOL_INFO_STRING propertyconst
   {
      return SymbolInfoString(nameproperty);
   }
   ...

Еще три похожих метода дают возможность избавиться от типа перечисления в первом параметре и выбирать компилятором нужную перегрузку за счет второго фиктивного параметра (его тип здесь всегда совпадает с типом результата). Мы воспользуемся этим в будущих классах-шаблонах.

   long get(const int propertyconst longconst
   {
      return SymbolInfoInteger(name, (ENUM_SYMBOL_INFO_INTEGER)property);
   }
 
   double get(const int propertyconst doubleconst
   {
      return SymbolInfoDouble(name, (ENUM_SYMBOL_INFO_DOUBLE)property);
   }
 
   string get(const int propertyconst stringconst
   {
      return SymbolInfoString(name, (ENUM_SYMBOL_INFO_STRING)property);
   }
   ...

Таким образом, создав объект с нужным именем символа, можно единообразно запрашивать его свойства любых типов. Для запроса и вывода в журнал всех свойств одного типа мы могли бы реализовать примерно такой метод.

   // проект (черновик)
   template<typename E,typename R>
   void list2log()
   {
      E e = (E)0;
      int array[];
      const int n = EnumToArray(earray0USHORT_MAX);
      for(int i = 0i < n; ++i)
      {
         e = (E)array[i];
         R r = get(e);
         PrintFormat("% 3d %s=%s"iEnumToString(e), (string)r);
      }
   }

Однако из-за того, что в свойствах типа long на самом деле "скрываются" значения других типов, которые желательно отображать специфическим образом (например, для перечислений — вызывать EnumToString, для даты и времени — TimeToString и т.д.), имеет смысл определить еще одну тройку перегруженных методов, которые возвращали бы строковое представление свойства. Назовем их stringify. Тогда в приведенном наброске list2log можно будет использовать stringify вместо приведения значений к (string), а сам метод избавится от одного шаблонного параметра.

   template<typename E>
   void list2log()
   {
      E e = (E)0;
      int array[];
      const int n = EnumToArray(earray0USHORT_MAX);
      for(int i = 0i < n; ++i)
      {
         e = (E)array[i];
         PrintFormat("% 3d %s=%s"iEnumToString(e), stringify(e));
      }
   }

Для вещественного и строкового типов реализация stringify выглядит довольно прямолинейно.

   string stringify(const ENUM_SYMBOL_INFO_DOUBLE propertyconst string format = NULLconst
   {
      if(format == NULLreturn (string)SymbolInfoDouble(nameproperty);
      return StringFormat(formatSymbolInfoDouble(nameproperty));
   }
   
   string stringify(const ENUM_SYMBOL_INFO_STRING propertyconst
   {
      return SymbolInfoString(nameproperty);
   }

А вот для ENUM_SYMBOL_INFO_INTEGER все немного сложнее. Конечно, когда свойство имеет тип long или int его достаточно привести к (string). Все остальные случаи требуется индивидуально анализировать и преобразовывать внутри оператора switch.

   string stringify(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      const long v = SymbolInfoInteger(nameproperty);
      switch(property)
      {
         ...
      }
      
      return (string)v;
   }

Например, если свойство имеет логический тип, его удобно представить строкой "true" или "false" (таким образом, оно будет визуально отличаться от простых чисел 1 и 0). Немного забегая вперед, для примера скажем, что среди свойств имеется SYMBOL_EXIST, эквивалентное функции SymbolExist, то есть возвращающее логический признак того, существует ли указанный символ. Для его обработки и прочих логических свойств имеет смысл реализовать вспомогательный метод boolean.

   static string boolean(const long v)
   {
      return v ? "true" : "false";
   }
   
   string stringify(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      const long v = SymbolInfoInteger(nameproperty);
      switch(property)
      {
         case SYMBOL_EXIST:
            return boolean(v);
         ...
      }
      
      return (string)v;
   }

Для свойств, являющихся перечислениями, наиболее подходящим решением будет шаблонный метод, использующий функцию EnumToString.

   template<typename E>
   static string enumstr(const long v)
   {
      return EnumToString((E)v);
   }

Например, одно из свойств SYMBOL_SWAP_ROLLOVER3DAYS определяет, в какой день недели на открытые позиции по символу начисляется тройной своп, и это свойство имеет тип ENUM_DAY_OF_WEEK. Значит, для его обработки мы можем написать внутри switch:

         case SYMBOL_SWAP_ROLLOVER3DAYS:
            return enumstr<ENUM_DAY_OF_WEEK>(v);

Особый случай представляют свойства, значения которых являются комбинациями битовых флагов. В частности, по каждому символу брокером устанавливаются разрешения на приказы конкретных типов, такие как рыночные, лимитные, стоплосс, тейкпрофит и другие (мы рассмотрим эти разрешения отдельно). Каждый тип приказов обозначается константой с взведенным одним битом, так что их суперпозиция (объединение битовым оператором ИЛИ '|') хранится в свойстве SYMBOL_ORDER_MODE, и в отсутствие ограничений взведены все биты одновременно. Для подобных свойств мы определим в нашем заголовочном файле собственные перечисления, например:

enum SYMBOL_ORDER
{
   _SYMBOL_ORDER_MARKET = 1,
   _SYMBOL_ORDER_LIMIT = 2,
   _SYMBOL_ORDER_STOP = 4,
   _SYMBOL_ORDER_STOP_LIMIT = 8,
   _SYMBOL_ORDER_SL = 16,
   _SYMBOL_ORDER_TP = 32,
   _SYMBOL_ORDER_CLOSEBY = 64,
};

Здесь для каждой встроенной константы, такой как SYMBOL_ORDER_MARKET, описан соответствующий элемент, идентификатор которого совпадает с константой, но предваряется символом подчеркивания, чтобы избежать конфликта имен.

Для представления комбинаций флагов из таких перечислений в виде строки мы реализуем еще один шаблонный метод maskstr.

   template<typename E>
   static string maskstr(const long v)
   {
      string text = "";
      for(int i = 0; ; ++i)
      {
         ResetLastError();
         const string s = EnumToString((E)(1 << i));
         if(_LastError != 0)
         {
            break;
         }
         if((v & (1 << i)) != 0)
         {
            text += s + " ";
         }
      }
      return text;
   }

Его суть похожа на enumstr, но функция EnumToString вызывается для каждого взведенного бита в значении свойства, после чего полученные строки "склеиваются".

Теперь обработка SYMBOL_ORDER_MODE в операторе switch возможна по похожей схеме:

         case SYMBOL_ORDER_MODE:
            return maskstr<SYMBOL_ORDER>(v);

Приведем полный код метода stringify для ENUM_SYMBOL_INFO_INTEGER. Со всеми свойствами и перечислениями мы поэтапно познакомимся в следующих разделах.

   string stringify(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      const long v = SymbolInfoInteger(nameproperty);
      switch(property)
      {
         case SYMBOL_SELECT:
         case SYMBOL_SPREAD_FLOAT:
         case SYMBOL_VISIBLE:
         case SYMBOL_CUSTOM:
         case SYMBOL_MARGIN_HEDGED_USE_LEG:
         case SYMBOL_EXIST:
            return boolean(v);
         case SYMBOL_TIME:
            return TimeToString(vTIME_DATE|TIME_SECONDS);
         case SYMBOL_TRADE_CALC_MODE:   
            return enumstr<ENUM_SYMBOL_CALC_MODE>(v);
         case SYMBOL_TRADE_MODE:
            return enumstr<ENUM_SYMBOL_TRADE_MODE>(v);
         case SYMBOL_TRADE_EXEMODE:
            return enumstr<ENUM_SYMBOL_TRADE_EXECUTION>(v);
         case SYMBOL_SWAP_MODE:
            return enumstr<ENUM_SYMBOL_SWAP_MODE>(v);
         case SYMBOL_SWAP_ROLLOVER3DAYS:
            return enumstr<ENUM_DAY_OF_WEEK>(v);
         case SYMBOL_EXPIRATION_MODE:
            return maskstr<SYMBOL_EXPIRATION>(v);
         case SYMBOL_FILLING_MODE:
            return maskstr<SYMBOL_FILLING>(v);
         case SYMBOL_START_TIME:
         case SYMBOL_EXPIRATION_TIME:
            return TimeToString(v);
         case SYMBOL_ORDER_MODE:
            return maskstr<SYMBOL_ORDER>(v);
         case SYMBOL_OPTION_RIGHT:
            return enumstr<ENUM_SYMBOL_OPTION_RIGHT>(v);
         case SYMBOL_OPTION_MODE:
            return enumstr<ENUM_SYMBOL_OPTION_MODE>(v);
         case SYMBOL_CHART_MODE:
            return enumstr<ENUM_SYMBOL_CHART_MODE>(v);
         case SYMBOL_ORDER_GTC_MODE:
            return enumstr<ENUM_SYMBOL_ORDER_GTC_MODE>(v);
         case SYMBOL_SECTOR:
            return enumstr<ENUM_SYMBOL_SECTOR>(v);
         case SYMBOL_INDUSTRY:
            return enumstr<ENUM_SYMBOL_INDUSTRY>(v);
         case SYMBOL_BACKGROUND_COLOR// Байты: Transparency Blue Green Red
            return StringFormat("TBGR(0x%08X)"v);
      }
      
      return (string)v;
   }

Для проверки работы класса SymbolMonitor создан простой скрипт SymbolMonitor.mq5. Он выводит в журнал все свойства рабочего символа графика.

#include <MQL5Book/SymbolMonitor.mqh>
   
void OnStart()
{
   SymbolMonitor m;
   m.list2log<ENUM_SYMBOL_INFO_INTEGER>();
   m.list2log<ENUM_SYMBOL_INFO_DOUBLE>();
   m.list2log<ENUM_SYMBOL_INFO_STRING>();
}

Например, если запустить скрипт на графике EURUSD можем получить следующие записи (приведено с сокращениями).

ENUM_SYMBOL_INFO_INTEGER Count=36
  0 SYMBOL_SELECT=true
  ...
  4 SYMBOL_TIME=2022.01.12 10:52:22
  5 SYMBOL_DIGITS=5
  6 SYMBOL_SPREAD=0
  7 SYMBOL_TICKS_BOOKDEPTH=10
  8 SYMBOL_TRADE_CALC_MODE=SYMBOL_CALC_MODE_FOREX
  9 SYMBOL_TRADE_MODE=SYMBOL_TRADE_MODE_FULL
 10 SYMBOL_TRADE_STOPS_LEVEL=0
 11 SYMBOL_TRADE_FREEZE_LEVEL=0
 12 SYMBOL_TRADE_EXEMODE=SYMBOL_TRADE_EXECUTION_INSTANT
 13 SYMBOL_SWAP_MODE=SYMBOL_SWAP_MODE_POINTS
 14 SYMBOL_SWAP_ROLLOVER3DAYS=WEDNESDAY
 15 SYMBOL_SPREAD_FLOAT=true
 16 SYMBOL_EXPIRATION_MODE=_SYMBOL_EXPIRATION_GTC _SYMBOL_EXPIRATION_DAY »
    _SYMBOL_EXPIRATION_SPECIFIED _SYMBOL_EXPIRATION_SPECIFIED_DAY
 17 SYMBOL_FILLING_MODE=_SYMBOL_FILLING_FOK
 ... 
 23 SYMBOL_ORDER_MODE=_SYMBOL_ORDER_MARKET _SYMBOL_ORDER_LIMIT _SYMBOL_ORDER_STOP »
    _SYMBOL_ORDER_STOP_LIMIT _SYMBOL_ORDER_SL _SYMBOL_ORDER_TP _SYMBOL_ORDER_CLOSEBY
 ... 
 26 SYMBOL_VISIBLE=true
 27 SYMBOL_CUSTOM=false
 28 SYMBOL_BACKGROUND_COLOR=TBGR(0xFF000000)
 29 SYMBOL_CHART_MODE=SYMBOL_CHART_MODE_BID
 30 SYMBOL_ORDER_GTC_MODE=SYMBOL_ORDERS_GTC
 31 SYMBOL_MARGIN_HEDGED_USE_LEG=false
 32 SYMBOL_EXIST=true
 33 SYMBOL_TIME_MSC=1641984742149
 34 SYMBOL_SECTOR=SECTOR_CURRENCY
 35 SYMBOL_INDUSTRY=INDUSTRY_UNDEFINED
ENUM_SYMBOL_INFO_DOUBLE Count=57
  0 SYMBOL_BID=1.13681
  1 SYMBOL_BIDHIGH=1.13781
  2 SYMBOL_BIDLOW=1.13552
  3 SYMBOL_ASK=1.13681
  4 SYMBOL_ASKHIGH=1.13781
  5 SYMBOL_ASKLOW=1.13552
 ...
 12 SYMBOL_POINT=1e-05
 13 SYMBOL_TRADE_TICK_VALUE=1.0
 14 SYMBOL_TRADE_TICK_SIZE=1e-05
 15 SYMBOL_TRADE_CONTRACT_SIZE=100000.0
 16 SYMBOL_VOLUME_MIN=0.01
 17 SYMBOL_VOLUME_MAX=500.0
 18 SYMBOL_VOLUME_STEP=0.01
 19 SYMBOL_SWAP_LONG=-0.7
 20 SYMBOL_SWAP_SHORT=-1.0
 21 SYMBOL_MARGIN_INITIAL=0.0
 22 SYMBOL_MARGIN_MAINTENANCE=0.0
 ...
 28 SYMBOL_TRADE_TICK_VALUE_PROFIT=1.0
 29 SYMBOL_TRADE_TICK_VALUE_LOSS=1.0
 ...
 43 SYMBOL_MARGIN_HEDGED=100000.0
 ...
 47 SYMBOL_PRICE_CHANGE=0.0132
ENUM_SYMBOL_INFO_STRING Count=15
  0 SYMBOL_BANK=
  1 SYMBOL_DESCRIPTION=Euro vs US Dollar
  2 SYMBOL_PATH=Forex\EURUSD
  3 SYMBOL_CURRENCY_BASE=EUR
  4 SYMBOL_CURRENCY_PROFIT=USD
  5 SYMBOL_CURRENCY_MARGIN=EUR
 ...
 13 SYMBOL_SECTOR_NAME=Currency

В частности, видно, что цены символа транслируются с 5 знаками (SYMBOL_DIGITS), символ существует (SYMBOL_EXIST), размер контракта составляет 100000.0 (SYMBOL_TRADE_CONTRACT_SIZE), и т.д. Вся информация соответствует спецификации.