English 中文 Español Deutsch 日本語 Português
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XV): Коллекция объектов-символов

Библиотека для простого и быстрого создания программ для MetaTrader (Часть XV): Коллекция объектов-символов

MetaTrader 5Примеры | 4 июля 2019, 10:26
2 985 0
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция коллекции символов

Концепция построения классов-коллекций объектов ранее была нами определена в третьей части описания библиотеки, и здесь мы не будем отходить от принятой структуры хранения данных. Таким образом, для коллекции символов нам необходимо создать список, в котором будут храниться объекты-наследники класса "символ", который был создан в прошлой статье. Наследники абстрактного символа будут уточнять информацию о символе, в них будет организовано определение доступности в программе свойств базового объекта-символа, и различаться такие объекты-символы будут по их принадлежности к группам (статус символа):

  • Форекс символ — все форекс-символы, не попавшие в следующие категории форекс-символов:
  • Форекс символ-мажор — пользовательская категория наиболее используемых форекс-символов
  • Форекс символ-минор — пользовательская категория менее используемых форекс-символов
  • Форекс символ-экзотик — пользовательская категория редко используемых форекс-символов
  • Форекс символ/рубль — пользовательская категория форекс-символов с рублём
  • Металл — пользовательская категория символов-металлов
  • Индекс — пользовательская категория символов-индексов
  • Индикатив — пользовательская категория символов-индикативов
  • Криптовалютный символ — пользовательская категория символов-криптовалют
  • Товарный символ — пользовательская категория товарных символов
  • Биржевой символ — все биржевые символы, не попавшие в следующие категории биржевых символов:
  • Фьючерс
  • Контракт на разницу (CFD)
  • Ценная бумага
  • Облигация
  • Опцион
  • Неторгуемый актив
  • Пользовательский символ
  • Общая категория — символы, не попавшие в вышеперечисленные категории

Для определения группы принадлежности символа (его статуса) создадим пользовательские наборы данных — массивы наименований символов, в которых в первую очередь будем производить поиск категории символа, к которой он должен принадлежать. Если символ не будет найден в пользовательской категории (его наименование отсутствует во всех массивах имён символов, заданных пользователем), то принадлежность символа будет определяться по его свойству: "способ вычисления величины залоговых средств" ( ENUM_SYMBOL_CALC_MODE), где можно определить принадлежность символа к некоторым из перечисленных выше категорий. То есть проводим поиск в две проверки: в категориях, заданных пользователем, и если там не удалось определить категорию, то устанавливаем категорию по способу вычисления залоговых средств для символа. Ранее планировалось использовать ещё один способ — определение по имени папки, в которой находится символ в дереве каталогов символов на сервере. Но данный способ слишком ненадёжный — имена папок могут быть любыми на разных серверах для одного и того же символа. Поэтому от него я отказался.
Пользовательская категория будет иметь приоритет — так как если пользователь желает, чтобы символ, например, USDUSC находился в его категории "мажор", то это его право — и символ будет там находиться, независимо от того, что это индикатив.

С категориями определились — для них создадим необходимые классы-наследники абстрактного символа, созданного нами в прошлой статье.
Для хранения всех объектов символов у нас будет использоваться класс CListObj, унаследованный от класса стандартной библиотеки CArrayObj, и который был нами рассмотрен в пятой статье при обсуждении реорганизации классов библиотеки. В программах, работающих на основе этой библиотеки, будет возможность выбрать с каким списком символов необходимо работать:

  1. Только один — текущий символ, к которому прикреплена программа
  2. Предопределённый набор символов, указанный в программе
  3. Работа со списком символов, находящихся в окне "Обзор рынка"
  4. Работа с полным списком символов, доступных на сервере

Таким образом, мы покроем большинство необходимых задач программирования доступа к рабочим инструментам-символам.
Стоит отметить два момента:
во-первых, это работа со списком символов из обзора рынка — в данном режиме необходимо будет использовать поиск событий окна "Обзор рынка" — для своевременной реакции на его изменения (добавление/удаление символа из списка и его сортировка мышью),
и во-вторых, при работе с полным списком символов, доступных на сервере, необходимо продумать нормальную обработку возможного большого количества доступных символов — ведь при первом запуске будет просматриваться полный список символов на сервере, и будут создаваться объекты-символы, для которых необходимо получить все их свойства. Этот процесс не быстрый — у меня на обычном средненьком ноутбуке процесс создания коллекции всех символов, находящихся на сервере, занял примерно две минуты.
Изначально, думаю, при выборе такого метода работы, необходимо хотя бы предупредить пользователя о возможно долгом сборе первоначальной информации.
Этим, и ещё отслеживанием событий символов и событий окна "Обзор рынка", займёмся в последующих статьях, а пока будем создавать список-коллекцию.

Для начала создадим ещё один включаемый файл, в нём будем хранить все нужные для библиотеки данные — массивы пользовательских групп символов, файлы, изображения, и прочие нужные впоследствии наборы данных для библиотеки.
В каталоге расположения библиотеки \MQL5\Include\DoEasy\ создадим новый включаемый файл с именем Datas.mqh и сразу же впишем в него некоторые массивы, которые я заранее уже подготовил, пробежавшись по нескольким счетам, и собрав из структуры дерева окна "Обзор рынка" данные о группах символов, заданных на разных серверах:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
//+------------------------------------------------------------------+
//| Наборы данных                                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Форекс-символы-мажоры                                            |
//+------------------------------------------------------------------+
string DataSymbolsFXMajors[]=
  {
   "AUDCAD","AUDCHF","AUDJPY","AUDNZD","AUDUSD","CADCHF","CADJPY","CHFJPY","EURAUD","EURCAD","EURCHF","EURGBP","EURJPY","EURNZD",
   "EURUSD","GBPAUD","GBPCAD","GBPCHF","GBPJPY","GBPNZD","GBPUSD","NZDCAD","NZDCHF","NZDJPY","NZDUSD","USDCAD","USDCHF","USDJPY"
  };
//+------------------------------------------------------------------+
//| Форекс-символы-миноры                                            |
//+------------------------------------------------------------------+
string DataSymbolsFXMinors[]=
  {
   "EURCZK","EURDKK","EURHKD","EURNOK","EURPLN","EURSEK","EURSGD","EURTRY","EURZAR","GBPSEK","GBPSGD"
      ,"NZDSGD","USDCZK","USDDKK","USDHKD","USDNOK","USDPLN","USDSEK","USDSGD","USDTRY","USDZAR"
  };
//+------------------------------------------------------------------+
//| Форекс-символы-экзотики                                          |
//+------------------------------------------------------------------+
string DataSymbolsFXExotics[]=
  {
   "EURMXN","USDCNH","USDMXN","EURTRY","USDTRY"
  };
//+------------------------------------------------------------------+
//| Форекс рублёвые символы                                          |
//+------------------------------------------------------------------+
string DataSymbolsFXRub[]=
  {
   "EURRUB","USDRUB"
  };
//+------------------------------------------------------------------+
//| Форекс-символы-индикативы                                        |
//+------------------------------------------------------------------+
string DataSymbolsFXIndicatives[]=
  {
   "EUREUC","USDEUC","USDUSC"
  };
//+------------------------------------------------------------------+
//| Символы-металлы                                                  |
//+------------------------------------------------------------------+
string DataSymbolsMetalls[]=
  {
   "XAGUSD","XAUUSD"
  };
//+------------------------------------------------------------------+
//| Товарные символы                                                 |
//+------------------------------------------------------------------+
string DataSymbolsCommodities[]=
  {
   "BRN","WTI","NG"
  };
//+------------------------------------------------------------------+
//| Индексы                                                          |
//+------------------------------------------------------------------+
string DataSymbolsIndexes[]=
  {
   "CAC40","HSI50","ASX200","STOXX50","NQ100","FTSE100","DAX30","IBEX35","SPX500","NIKK225"
   "Volatility 10 Index","Volatility 25 Index","Volatility 50 Index","Volatility 75 Index","Volatility 100 Index",
   "HF Volatility 10 Index","HF Volatility 50 Index","Crash 1000 Index","Boom 1000 Index","Step Index"
  };
//+------------------------------------------------------------------+
//| Криптовалютные символы                                           |
//+------------------------------------------------------------------+
string DataSymbolsCrypto[]=
  {
   "BCHUSD","BTCEUR","BTCUSD","DSHUSD","EOSUSD","ETHEUR","ETHUSD","LTCUSD","XRPUSD"
  };
//+------------------------------------------------------------------+
//| Опционы                                                          |
//+------------------------------------------------------------------+
string DataSymbolsOptions[]=
  {
   "BO Volatility 10 Index","BO Volatility 25 Index","BO Volatility 50 Index","BO Volatility 75 Index","BO Volatility 100 Index"
  };
//+------------------------------------------------------------------+

Как видно из листинга — это просто перечисление имён символов, которые добавляем в требуемые массивы, определяющие группу символов, находящихся в каждом из массивов. При желании и необходимости, можно переработать состав этих массивов имён символов — что-то отправить в другой массив, что-то добавить/удалить, и т.д.

Далее. Более "плотное" тестирование поведения абстрактного объекта-символа дало понимание, что использование констант SYMBOL_MARGIN_LONG, SYMBOL_MARGIN_SHORT, SYMBOL_MARGIN_STOP, SYMBOL_MARGIN_LIMIT и SYMBOL_MARGIN_STOPLIMIT не привело к желаемому получению свойств символа. Поэтому пришлось заменить получение данных свойств на их получение посредством функции SymbolInfoMarginRate().
И так как в данную функцию отправляется тип ордера, то пришлось создать для каждого из типов ордеров свою константу в вещественных свойствах объекта-символа в файле Defines.mqh:

//+------------------------------------------------------------------+
//| Вещественные свойства символа                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_PROP_DOUBLE
  {
   SYMBOL_PROP_BID = SYMBOL_PROP_INTEGER_TOTAL,             // Bid - лучшее предложение на продажу
   SYMBOL_PROP_BIDHIGH,                                     // Максимальный Bid за день
   SYMBOL_PROP_BIDLOW,                                      // Минимальный Bid за день
   SYMBOL_PROP_ASK,                                         // Ask - лучшее предложение на покупку
   SYMBOL_PROP_ASKHIGH,                                     // Максимальный Ask за день
   SYMBOL_PROP_ASKLOW,                                      // Минимальный Ask за день
   SYMBOL_PROP_LAST,                                        // Цена, по которой совершена последняя сделка
   SYMBOL_PROP_LASTHIGH,                                    // Максимальный Last за день
   SYMBOL_PROP_LASTLOW,                                     // Минимальный Last за день
   SYMBOL_PROP_VOLUME_REAL,                                 // Volume за день
   SYMBOL_PROP_VOLUMEHIGH_REAL,                             // Максимальный Volume за день
   SYMBOL_PROP_VOLUMELOW_REAL,                              // Минимальный Volume за день
   SYMBOL_PROP_OPTION_STRIKE,                               // Цена исполнения опциона
   SYMBOL_PROP_POINT,                                       // Значение одного пункта
   SYMBOL_PROP_TRADE_TICK_VALUE,                            // Значение SYMBOL_TRADE_TICK_VALUE_PROFIT
   SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT,                     // Рассчитанная стоимость тика для прибыльной позиции
   SYMBOL_PROP_TRADE_TICK_VALUE_LOSS,                       // Рассчитанная стоимость тика для убыточной позиции
   SYMBOL_PROP_TRADE_TICK_SIZE,                             // Минимальное изменение цены
   SYMBOL_PROP_TRADE_CONTRACT_SIZE,                         // Размер торгового контракта
   SYMBOL_PROP_TRADE_ACCRUED_INTEREST,                      // Накопленный купонный доход
   SYMBOL_PROP_TRADE_FACE_VALUE,                            // Номинальная стоимость – начальная стоимость облигации, установленная эмитентом
   SYMBOL_PROP_TRADE_LIQUIDITY_RATE,                        // Коэффициент ликвидности – доля от стоимости актива, которую можно использовать в качестве залога
   SYMBOL_PROP_VOLUME_MIN,                                  // Минимальный объем для заключения сделки
   SYMBOL_PROP_VOLUME_MAX,                                  // Максимальный объем для заключения сделки
   SYMBOL_PROP_VOLUME_STEP,                                 // Минимальный шаг изменения объема для заключения сделки
   SYMBOL_PROP_VOLUME_LIMIT,                                // Максимально допустимый совокупный объем открытой позиции и отложенных ордеров в одном направлении (покупка или продажа)
   SYMBOL_PROP_SWAP_LONG,                                   // Значение свопа в покупку
   SYMBOL_PROP_SWAP_SHORT,                                  // Значение свопа в продажу
   SYMBOL_PROP_MARGIN_INITIAL,                              // Начальная (инициирующая) маржа
   SYMBOL_PROP_MARGIN_MAINTENANCE,                          // Поддерживающая маржа по инструменту
   SYMBOL_PROP_MARGIN_LONG_INITIAL,                         // Коэффициент взимания начальной маржи по длинным позициям
   SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL,                     // Коэффициент взимания начальной маржи по BuyStop ордерам
   SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL,                    // Коэффициент взимания начальной маржи по BuyLimit ордерам
   SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL,                // Коэффициент взимания начальной маржи по BuyStopLimit ордерам
   SYMBOL_PROP_MARGIN_LONG_MAINTENANCE,                     // Коэффициент взимания поддерживающей маржи по длинным позициям
   SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE,                 // Коэффициент взимания поддерживающей маржи по BuyStop ордерам
   SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE,                // Коэффициент взимания поддерживающей маржи по BuyLimit ордерам
   SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE,            // Коэффициент взимания поддерживающей маржи по BuyStopLimit ордерам
   SYMBOL_PROP_MARGIN_SHORT_INITIAL,                        // Коэффициент взимания начальной маржи по коротким позициям
   SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL,                    // Коэффициент взимания начальной маржи по SellStop ордерам
   SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL,                   // Коэффициент взимания начальной маржи по SellLimit ордерам
   SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL,               // Коэффициент взимания начальной маржи по SellStopLimit ордерам
   SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE,                    // Коэффициент взимания поддерживающей маржи по коротким позициям
   SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE,                // Коэффициент взимания поддерживающей маржи по SellStop ордерам
   SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE,               // Коэффициент взимания поддерживающей маржи по SellLimit ордерам
   SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE,           // Коэффициент взимания поддерживающей маржи по SellStopLimit ордерам
   SYMBOL_PROP_SESSION_VOLUME,                              // Cуммарный объём сделок в текущую сессию
   SYMBOL_PROP_SESSION_TURNOVER,                            // Cуммарный оборот в текущую сессию
   SYMBOL_PROP_SESSION_INTEREST,                            // Cуммарный объём открытых позиций
   SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,                   // Общий объём ордеров на покупку в текущий момент
   SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,                  // Общий объём ордеров на продажу в текущий момент
   SYMBOL_PROP_SESSION_OPEN,                                // Цена открытия сессии
   SYMBOL_PROP_SESSION_CLOSE,                               // Цена закрытия сессии
   SYMBOL_PROP_SESSION_AW,                                  // Средневзвешенная цена сессии
   SYMBOL_PROP_SESSION_PRICE_SETTLEMENT,                    // Цена поставки на текущую сессию
   SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN,                     // Минимально допустимое значение цены на сессию 
   SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX,                     // Максимально допустимое значение цены на сессию
   SYMBOL_PROP_MARGIN_HEDGED                                // Размер контракта или маржи для одного лота перекрытых позиций (разнонаправленные позиции по одному символу).
  };
#define SYMBOL_PROP_DOUBLE_TOTAL     (58)                   // Общее количество вещественных свойств
#define SYMBOL_PROP_DOUBLE_SKIP      (0)                    // Количество неиспользуемых в сортировке вещественных свойств символа
//+------------------------------------------------------------------+

Соответственно, и общее количество вещественных свойств увеличилось до 58 (вместо 47 ранее)

И точно так же пришлось добавить соответствующие константы в перечисление возможных критериев сортировки символов:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки символов                           |
//+------------------------------------------------------------------+
#define FIRST_SYM_DBL_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)
#define FIRST_SYM_STR_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_SYMBOL_STATUS = 0,                               // Сортировать по статусу символа
   SORT_BY_SYMBOL_CUSTOM,                                   // Сортировать по признаку пользовательского символа
   SORT_BY_SYMBOL_CHART_MODE,                               // Сортировать по типу цены для построения баров – Bid или Last (из перечисления ENUM_SYMBOL_CHART_MODE)
   SORT_BY_SYMBOL_EXIST,                                    // Сортировать по признаку того, что символ с таким именем существует
   SORT_BY_SYMBOL_SELECT,                                   // Сортировать по признаку того, что символ выбран в Market Watch
   SORT_BY_SYMBOL_VISIBLE,                                  // Сортировать по признаку того, что выбранный символ отображается в Market Watch
   SORT_BY_SYMBOL_SESSION_DEALS,                            // Сортировать по количеству сделок в текущей сессии 
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                       // Сортировать по общему числу ордеров на покупку в текущий момент
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                      // Сортировать по общему числу ордеров на продажу в текущий момент
   SORT_BY_SYMBOL_VOLUME,                                   // Сортировать по Volume - объему в последней сделке
   SORT_BY_SYMBOL_VOLUMEHIGH,                               // Сортировать по максимальному Volume за день
   SORT_BY_SYMBOL_VOLUMELOW,                                // Сортировать по минимальному Volume за день
   SORT_BY_SYMBOL_TIME,                                     // Сортировать по времени последней котировки
   SORT_BY_SYMBOL_DIGITS,                                   // Сортировать по количеству знаков после запятой
   SORT_BY_SYMBOL_DIGITS_LOT,                               // Сортировать по количеству знаков после запятой в лоте
   SORT_BY_SYMBOL_SPREAD,                                   // Сортировать по размеру спреда в пунктах
   SORT_BY_SYMBOL_SPREAD_FLOAT,                             // Сортировать по признаку плавающего спреда
   SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                          // Сортировать по максимальному количеству показываемых заявок в стакане
   SORT_BY_SYMBOL_TRADE_CALC_MODE,                          // Сортировать по способу вычисления стоимости контракта (из перечисления ENUM_SYMBOL_CALC_MODE)
   SORT_BY_SYMBOL_TRADE_MODE,                               // Сортировать по типу исполнения ордеров (из перечисления ENUM_SYMBOL_TRADE_MODE)
   SORT_BY_SYMBOL_START_TIME,                               // Сортировать по дате начала торгов по инструменту (обычно используется для фьючерсов)
   SORT_BY_SYMBOL_EXPIRATION_TIME,                          // Сортировать по дате окончания торгов по инструменту (обычно используется для фьючерсов)
   SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                        // Сортировать по минимальному отступу в пунктах от текущей цены закрытия для установки Stop ордеров
   SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                       // Сортировать по дистанции заморозки торговых операций (в пунктах)
   SORT_BY_SYMBOL_TRADE_EXEMODE,                            // Сортировать по режиму заключения сделок (из перечисления ENUM_SYMBOL_TRADE_EXECUTION)
   SORT_BY_SYMBOL_SWAP_MODE,                                // Сортировать по модели расчета свопа (из перечисления ENUM_SYMBOL_SWAP_MODE)
   SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                       // Сортировать по дню недели для начисления тройного свопа (из перечисления ENUM_DAY_OF_WEEK)
   SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                    // Сортировать по режиму расчета хеджированной маржи по наибольшей стороне (Buy или Sell)
   SORT_BY_SYMBOL_EXPIRATION_MODE,                          // Сортировать по флагам разрешенных режимов истечения ордера
   SORT_BY_SYMBOL_FILLING_MODE,                             // Сортировать по флагам разрешенных режимов заливки ордера
   SORT_BY_SYMBOL_ORDER_MODE,                               // Сортировать по флагам разрешенных типов ордера
   SORT_BY_SYMBOL_ORDER_GTC_MODE,                           // Сортировать по сроку действия StopLoss и TakeProfit ордеров
   SORT_BY_SYMBOL_OPTION_MODE,                              // Сортировать по типу опциона (из перечисления ENUM_SYMBOL_OPTION_MODE)
   SORT_BY_SYMBOL_OPTION_RIGHT,                             // Сортировать по праву опциона (Call/Put) (из перечисления ENUM_SYMBOL_OPTION_RIGHT)
//--- Сортировка по вещественным свойствам
   SORT_BY_SYMBOL_BID = FIRST_SYM_DBL_PROP,                 // Сортировать по Bid
   SORT_BY_SYMBOL_BIDHIGH,                                  // Сортировать по максимальному Bid за день
   SORT_BY_SYMBOL_BIDLOW,                                   // Сортировать по минимальному Bid за день
   SORT_BY_SYMBOL_ASK,                                      // Сортировать по Ask
   SORT_BY_SYMBOL_ASKHIGH,                                  // Сортировать по максимальному Ask за день
   SORT_BY_SYMBOL_ASKLOW,                                   // Сортировать по минимальному Ask за день
   SORT_BY_SYMBOL_LAST,                                     // Сортировать по цене, по которой совершена последняя сделка
   SORT_BY_SYMBOL_LASTHIGH,                                 // Сортировать по максимальному Last за день
   SORT_BY_SYMBOL_LASTLOW,                                  // Сортировать по минимальному Last за день
   SORT_BY_SYMBOL_VOLUME_REAL,                              // Сортировать по Volume за день
   SORT_BY_SYMBOL_VOLUMEHIGH_REAL,                          // Сортировать по максимальному Volume за день
   SORT_BY_SYMBOL_VOLUMELOW_REAL,                           // Сортировать по минимальному Volume за день
   SORT_BY_SYMBOL_OPTION_STRIKE,                            // Сортировать по цене исполнения опциона
   SORT_BY_SYMBOL_POINT,                                    // Сортировать по значению одного пункта
   SORT_BY_SYMBOL_TRADE_TICK_VALUE,                         // Сортировать по значению SYMBOL_TRADE_TICK_VALUE_PROFIT
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,                  // Сортировать по рассчитанной стоимости тика для прибыльной позиции
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,                    // Сортировать по рассчитанной стоимости тика для убыточной позиции
   SORT_BY_SYMBOL_TRADE_TICK_SIZE,                          // Сортировать по минимальному изменению цены
   SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                      // Сортировать по размеру торгового контракта
   SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,                   // Сортировать по накопленному купонному доходу
   SORT_BY_SYMBOL_TRADE_FACE_VALUE,                         // Сортировать по номинальной стоимости
   SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                     // Сортировать по коэффициенту ликвидности
   SORT_BY_SYMBOL_VOLUME_MIN,                               // Сортировать по минимальному объему для заключения сделки
   SORT_BY_SYMBOL_VOLUME_MAX,                               // Сортировать по максимальному объему для заключения сделки
   SORT_BY_SYMBOL_VOLUME_STEP,                              // Сортировать по минимальному шагу изменения объема для заключения сделки
   SORT_BY_SYMBOL_VOLUME_LIMIT,                             // Сортировать по максимально допустимому совокупному объему открытой позиции и отложенных ордеров в одном направлении
   SORT_BY_SYMBOL_SWAP_LONG,                                // Сортировать по значению свопа в покупку
   SORT_BY_SYMBOL_SWAP_SHORT,                               // Сортировать по значению свопа в продажу
   SORT_BY_SYMBOL_MARGIN_INITIAL,                           // Сортировать по начальной (инициирующей) марже
   SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                       // Сортировать по поддерживающей марже по инструменту
   SORT_BY_SYMBOL_MARGIN_LONG_INITIAL,                      // Сортировать по коэффициенту взимания начальной маржи по длинным позициям
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL,                  // Сортировать по коэффициенту взимания начальной маржи по BuyStop ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_INITIAL,                 // Сортировать по коэффициенту взимания начальной маржи по BuyLimit ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_INITIAL,             // Сортировать по коэффициенту взимания начальной маржи по BuyStopLimit ордерам
   SORT_BY_SYMBOL_MARGIN_LONG_MAINTENANCE,                  // Сортировать по коэффициенту взимания поддерживающей маржи по длинным позициям
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_MAINTENANCE,              // Сортировать по коэффициенту взимания поддерживающей маржи по BuyStop ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_MAINTENANCE,             // Сортировать по коэффициенту взимания поддерживающей маржи по BuyLimit ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_MAINTENANCE,         // Сортировать по коэффициенту взимания поддерживающей маржи по BuyStopLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SHORT_INITIAL,                     // Сортировать по коэффициенту взимания начальной маржи по коротким позициям
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_INITIAL,                 // Сортировать по коэффициенту взимания начальной маржи по SellStop ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_INITIAL,                // Сортировать по коэффициенту взимания начальной маржи по SellLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_INITIAL,            // Сортировать по коэффициенту взимания начальной маржи по SellStopLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SHORT_MAINTENANCE,                 // Сортировать по коэффициенту взимания поддерживающей маржи по коротким позициям
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE,             // Сортировать по коэффициенту взимания поддерживающей маржи по SellStop ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_MAINTENANCE,            // Сортировать по коэффициенту взимания поддерживающей маржи по SellLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_MAINTENANCE,        // Сортировать по коэффициенту взимания поддерживающей маржи по SellStopLimit ордерам
   SORT_BY_SYMBOL_SESSION_VOLUME,                           // Сортировать по суммарному объёму сделок в текущую сессию
   SORT_BY_SYMBOL_SESSION_TURNOVER,                         // Сортировать по суммарному обороту в текущую сессию
   SORT_BY_SYMBOL_SESSION_INTEREST,                         // Сортировать по суммарному объёму открытых позиций
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME,                // Сортировать по общему объёму ордеров на покупку в текущий момент
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME,               // Сортировать по общему объёму ордеров на продажу в текущий момент
   SORT_BY_SYMBOL_SESSION_OPEN,                             // Сортировать по цене открытия сессии
   SORT_BY_SYMBOL_SESSION_CLOSE,                            // Сортировать по цене закрытия сессии
   SORT_BY_SYMBOL_SESSION_AW,                               // Сортировать по средневзвешенной цене сессии
   SORT_BY_SYMBOL_SESSION_PRICE_SETTLEMENT,                 // Сортировать по цене поставки на текущую сессию
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN,                  // Сортировать по минимально допустимому значению цены на сессию 
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                  // Сортировать по максимально допустимому значению цены на сессию
   SORT_BY_SYMBOL_MARGIN_HEDGED,                            // Сортировать по размеру контракта или маржи для одного лота перекрытых позиций
//--- Сортировка по строковым свойствам
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Сортировать по имени символа
   SORT_BY_SYMBOL_BASIS,                                    // Сортировать по имени базового актива для производного инструмента
   SORT_BY_SYMBOL_CURRENCY_BASE,                            // Сортировать по базовой валюте инструмента
   SORT_BY_SYMBOL_CURRENCY_PROFIT,                          // Сортировать по валюте прибыли
   SORT_BY_SYMBOL_CURRENCY_MARGIN,                          // Сортировать по валюте, в которой вычисляются залоговые средства
   SORT_BY_SYMBOL_BANK,                                     // Сортировать по источнику текущей котировки
   SORT_BY_SYMBOL_DESCRIPTION,                              // Сортировать по строковому описанию символа
   SORT_BY_SYMBOL_FORMULA,                                  // Сортировать по формуле для построения цены пользовательского символа
   SORT_BY_SYMBOL_ISIN,                                     // Сортировать по имени торгового символа в системе международных идентификационных кодов ценных бумаг — ISIN
   SORT_BY_SYMBOL_PAGE,                                     // Сортировать по адресу интернет страницы с информацией по символу
   SORT_BY_SYMBOL_PATH                                      // Сортировать по пути в дереве символов
  };
//+------------------------------------------------------------------+

Ну и раз уж мы редактируем файл Deines.mqh, то сразу и впишем в него всё необходимое, попутно рассматривая что добавляем, и зачем это нужно.

Для коллекции символов нам потребуется таймер — ведь обновлять данные по всем символам, находящимся в коллекции нам необходимо по таймеру. Причём есть особенность: нам нужно будет обновлять котировочные данные всех символов, и остальные данные — те, которые могут измениться, чтобы отследить их в классе событий символов (об этом в следующей статье), а также нам нужно проверять в таймере список символов в окне "Обзор рынка" — чтобы вовремя реагировать на его изменения и обновлять список коллекции.
Котировочные данные нам нужно обновлять чаще, чем остальные данные символов и их список в окне "Обзор рынка". Значит, нам потребуются два таймера для коллекции символов — таймер котировочных данных, и таймер для остальных действий со списками символов.

Впишем необходимые макроподстановки для двух таймеров коллекции символов:

//--- Параметры таймера1 коллекции символов
#define COLLECTION_SYM_PAUSE1          (100)                      // Пауза таймера1 коллекции символов в милисекундах (для сканирования символов в обзоре рынка)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Шаг приращения счётчика таймера1 символов
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Идентификатор счётчика таймера1 символов
//--- Параметры таймера2 коллекции символов
#define COLLECTION_SYM_PAUSE2          (300)                      // Пауза таймера2 коллекции символов в милисекундах (для событий списка символов в обзоре рынка)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Шаг приращения счётчика таймера2 символов
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Идентификатор счётчика таймера2 символов

Разница между этими данными лишь в паузе для каждого таймера и их идентификаторах — для первого таймера паузой будет 100 милисекунд, для второго — 300.

Для каждой коллекции у нас есть свой идентификатор. Не является исключением и коллекция символов.
Назначим для её списка свой идентификатор:

//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x7778+1)                 // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x7778+2)                 // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x7778+3)                 // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x7778+4)                 // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x7778+5)                 // Идентификатор списка коллекции символов

Для определения цвета фона, которым подсвечивается символ в окне "Обзор рынка" и вывода его строкового описания, мы в прошлой статье использовали конструкцию сравнения цвета с цветом clrWhite — если значение свойства больше long-значения данного цвета, то считается, что цвет фона не задан:

      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Цвет фона символа в Market Watch","Background color of the symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
         #ifdef __MQL5__
         (this.GetProperty(property)>clrWhite  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :

Однако, по сообщению разработчиков это неверно — long-значение цвета фона символа в окне "Обзор рынка", оказывается, может быть больше long-значения "белого" цвета. А значит — в некоторых случаях такая проверка не будет возвращать правильный результат.
Для правильной идентификации отсутствия цвета фона, нужно сравнивать значение свойства со значением CLR_DEFAULT и CLR_NONE.
Зададим макроподстановкой значение цвета "по умолчанию" ("отсутствующий" цвет CLR_NONE уже есть в MQL5 и MQL4):

//--- Параметры символов
#define CLR_DEFAULT                    (0xFF000000)               // Цвет по умолчанию
//+------------------------------------------------------------------+

В итоге, раздел макроподстановок файла Defines.mqh у нас теперь выглядит так:

//+------------------------------------------------------------------+
//| Макроподстановки                                                 |
//+------------------------------------------------------------------+
//--- "Описание функции с номером строки ошибки"
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Стр. " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Описание функции"
#define COUNTRY_LANG                   ("Russian")                // Язык страны
#define END_TIME                       (D'31.12.3000 23:59:59')   // Конечная дата для запросов данных истории счёта
#define TIMER_FREQUENCY                (16)                       // Минимальная частота таймера библиотеки в милисекундах
//--- Параметры таймера коллекции ордеров и сделок
#define COLLECTION_ORD_PAUSE           (250)                      // Пауза таймера коллекции ордеров и сделок в милисекундах
#define COLLECTION_ORD_COUNTER_STEP    (16)                       // Шаг приращения счётчика таймера коллекции ордеров и сделок
#define COLLECTION_ORD_COUNTER_ID      (1)                        // Идентификатор счётчика таймера коллекции ордеров и сделок
//--- Параметры таймера коллекции аккаунтов
#define COLLECTION_ACC_PAUSE           (1000)                     // Пауза таймера коллекции аккаунтов в милисекундах
#define COLLECTION_ACC_COUNTER_STEP    (16)                       // Шаг приращения счётчика таймера аккаунтов
#define COLLECTION_ACC_COUNTER_ID      (2)                        // Идентификатор счётчика таймера аккаунтов
//--- Параметры таймера1 коллекции символов
#define COLLECTION_SYM_PAUSE1          (100)                      // Пауза таймера1 коллекции символов в милисекундах (для сканирования символов в обзоре рынка)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Шаг приращения счётчика таймера1 символов
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Идентификатор счётчика таймера1 символов
//--- Параметры таймера2 коллекции символов
#define COLLECTION_SYM_PAUSE2          (300)                      // Пауза таймера2 коллекции символов в милисекундах (для событий списка символов в обзоре рынка)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Шаг приращения счётчика таймера2 символов
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Идентификатор счётчика таймера2 символов
//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x7778+1)                 // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x7778+2)                 // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x7778+3)                 // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x7778+4)                 // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x7778+5)                 // Идентификатор списка коллекции символов
//--- Параметры данных для файловых операций
#define DIRECTORY                      ("DoEasy\\")               // Каталог библиотеки для расположения папок объектов классов
//--- Параметры символов
#define CLR_DEFAULT                    (0xFF000000)               // Цвет по умолчанию
//+------------------------------------------------------------------+

Выше мы говорили о режимах работы с коллекцией символов: работа с текущим символом, работа с предопределённым в программе списком символов, работа с окном "Обзор рынка" и работа с полным списком доступных символов на сервере.
Зададим все эти режимы в перечислении:

//+------------------------------------------------------------------+
//| Данные для работы с символами                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Режимы работы с символами                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Работа только с текущим символом
   SYMBOLS_MODE_DEFINES,                                    // Работа с заданным списком символов
   SYMBOLS_MODE_MARKET_WATCH,                               // Работа с символами из окна "Обзор рынка"
   SYMBOLS_MODE_ALL                                         // Работа с полным списком символов
  };
//+------------------------------------------------------------------+

В прошлой статье мы обозначили категории символов, по которым их будем распределять. А в самом начале данной статьи мы рассмотрели немного расширенный список категорий символов. Его и будем использовать.

Добавим перечисление категорий (статусов) символов:

//+------------------------------------------------------------------+
//| Тип (статус) абстрактного символа                                |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_STATUS
  {
   SYMBOL_STATUS_FX,                                        // Форекс символ
   SYMBOL_STATUS_FX_MAJOR,                                  // Форекс символ-мажор
   SYMBOL_STATUS_FX_MINOR,                                  // Форекс символ-минор
   SYMBOL_STATUS_FX_EXOTIC,                                 // Форекс символ-экзотик
   SYMBOL_STATUS_FX_RUB,                                    // Форекс символ/рубль
   SYMBOL_STATUS_METAL,                                     // Металл
   SYMBOL_STATUS_INDEX,                                     // Индекс
   SYMBOL_STATUS_INDICATIVE,                                // Индикатив
   SYMBOL_STATUS_CRYPTO,                                    // Криптовалютный символ
   SYMBOL_STATUS_COMMODITY,                                 // Товарный символ
   SYMBOL_STATUS_EXCHANGE,                                  // Биржевой символ
   SYMBOL_STATUS_FUTURES,                                   // Фьючерс
   SYMBOL_STATUS_CFD,                                       // Контракт на разницу
   SYMBOL_STATUS_STOCKS,                                    // Ценная бумага
   SYMBOL_STATUS_BONDS,                                     // Облигация
   SYMBOL_STATUS_OPTION,                                    // Опцион
   SYMBOL_STATUS_COLLATERAL,                                // Неторгуемый актив
   SYMBOL_STATUS_CUSTOM,                                    // Пользовательский символ
   SYMBOL_STATUS_COMMON                                     // Общая категория
  };
//+------------------------------------------------------------------+

На этом подготовка данных для коллекции символов и изменения в файле Defines.mqh завершены.

Изменения также коснулись класса CSymbol, созданного нами в прошлой статье.
Так как мы теперь получаем данные о коэффициентах взимания маржи для разных типов ордеров посредством функции SymbolInfoMarginRate(), а для возврата запрашиваемых из неё значений используются переменные, переданные в функцию по ссылке, то нам необходимо создать эти переменные.

И если учитывать, что ордеров у нас восемь штук, и по каждому из них мы можем получить два типа коэффициентов — коэффициент взимания начальной маржи и коэффициент взимания поддерживающей маржи, то переменных для получения всех этих значений должно быть 16 штук. Поэтому проще и нагляднее создать под них структуру, состоящую из двух вложенных структур: в первой структуре будут определены две double-переменные для хранения коэффициентов взимания начальной и поддерживающей маржи, а во второй будут содержаться первые объявленные структуры для хранения данных по типам ордеров, для которых нужно получать коэффициенты.

Объявим в файле класса-символа Symbol.mqh, в приватной секции класса CSymbol эти структуры и переменную-член класса с типом второй структуры для обращения к структуре:

//+------------------------------------------------------------------+
//| Класс абстрактного символа                                       |
//+------------------------------------------------------------------+
class CSymbol : public CObject
  {
private:
   struct SMarginRate
     {
      double         Initial;          // коэффициент взимания начальной маржи
      double         Maintenance;      // коэффициент взимания поддерживающей маржи
     };
   struct SMarginRateMode
     {
      SMarginRate    Long;             // MarginRate длинных позиций
      SMarginRate    Short;            // MarginRate коротких позиций
      SMarginRate    BuyStop;          // MarginRate BuyStop-ордеров
      SMarginRate    BuyLimit;         // MarginRate BuyLimit-ордеров
      SMarginRate    BuyStopLimit;     // MarginRate BuyStopLimit-ордеров
      SMarginRate    SellStop;         // MarginRate SellStop-ордеров
      SMarginRate    SellLimit;        // MarginRate SellLimit-ордеров
      SMarginRate    SellStopLimit;    // MarginRate SellStopLimit-ордеров
     };
   SMarginRateMode   m_margin_rate;                                  // Структура коэффициентов взимания маржи

Добавим в приватную секцию класса метод, заполняющий все свойства символа по каждому коэффициенту взимания маржи, метод, инициализирующий переменные структур, хранящих все коэффициенты взимания маржи, а также добавим два вспомогательных метода для получения текущего дня недели и для получения количества знаков после запятой в значении double-числа:

   SMarginRateMode   m_margin_rate;                                  // Структура коэффициентов взимания маржи
   MqlTick           m_tick;                                         // Структура тика символа
   MqlBookInfo       m_book_info_array[];                            // Массив структур данных стакана
   string            m_symbol_name;                                  // Имя символа
   long              m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];         // Целочисленные свойства
   double            m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];        // Вещественные свойства
   string            m_string_prop[SYMBOL_PROP_STRING_TOTAL];        // Строковые свойства
   int               m_digits_currency;                              // Число знаков после запятой валюты счёта
   int               m_global_error;                                 // Код глобальной ошибки
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство символа
   int               IndexProp(ENUM_SYMBOL_PROP_DOUBLE property)  const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL;                                    }
   int               IndexProp(ENUM_SYMBOL_PROP_STRING property)  const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL;           }
//--- (1) Заполняет все свойства символа "коэффициент взимания маржи", (2) инициализирует коэффициенты
   bool              MarginRates(void);
   void              InitMarginRates(void);
//--- Обнуляет все данные объекта-символа
   void              Reset(void);
//--- Возвращает текущий день недели
   ENUM_DAY_OF_WEEK  CurrentDayOfWeek(void)              const;
//--- Возвращает количество знаков после запятой в double-значении
   int               GetDigits(const double value)       const;
public:

Там же — в приватной секции класса объявим методы, возвращающие данные по коэффициентам взимания маржи для каждого из типов ордеров:

//--- Получает и возвращает вещественные свойства выбранного символа из его параметров
   double            SymbolBidHigh(void)                 const;
   double            SymbolBidLow(void)                  const;
   double            SymbolVolumeReal(void)              const;
   double            SymbolVolumeHighReal(void)          const;
   double            SymbolVolumeLowReal(void)           const;
   double            SymbolOptionStrike(void)            const;
   double            SymbolTradeAccruedInterest(void)    const;
   double            SymbolTradeFaceValue(void)          const;
   double            SymbolTradeLiquidityRate(void)      const;
   double            SymbolMarginHedged(void)            const;
   bool              SymbolMarginLong(void);         
   bool              SymbolMarginShort(void);        
   bool              SymbolMarginBuyStop(void);      
   bool              SymbolMarginBuyLimit(void);     
   bool              SymbolMarginBuyStopLimit(void); 
   bool              SymbolMarginSellStop(void);     
   bool              SymbolMarginSellLimit(void);    
   bool              SymbolMarginSellStopLimit(void);
//--- Получает и возвращает строковые свойства выбранного символа из его параметров

Иногда в программе требуется узнать существование символа на сервере по имени символа. У нас уже есть метод Exist(), который возвращает такую информацию по символу класса. Перегрузим метод, чтобы он мог возвращать данные по переданному имени символа. Для этого объявим ещё одну форму вызова метода в приватной секции класса:

//--- Ищет символ и возвращает флаг его наличия на сервере
   bool              Exist(void)                         const;
   bool              Exist(const string name)            const;

и объявим в защищённой секции класса перегруженный метод, возвращающий значение существования символа по его имени в зависимости от типа программы MQL5 или MQL4:

protected:
//--- Защищённый параметрический конструктор
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name);

//--- Получает и возвращает целочисленные свойства выбранного символа из его параметров
   bool              SymbolExists(const string name)     const;
   long              SymbolExists(void)                  const;

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

//+------------------------------------------------------------------+
//| Описания свойств объекта-символа                                 |
//+------------------------------------------------------------------+
//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства символа
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_STRING property);


//--- Выводит в журнал описание свойств символа (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);
//--- Выводит в журнал краткое описание символа (реализация в наследниках)
   virtual void      PrintShort(void) {;}

//--- Сравнивает объекты CSymbol между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта-символа)

В прошлой статье при реализации объекта-символа мы добавили в публичную секцию класса несколько сервисных методов.
Добавим ещё несколько методов для возврата времени начала и окончания котировочной и торговой сессий, а также приватные методы, возвращающие целое число часов, минут и секунд в сессии и метод, возвращающий описание продолжительности сессии в виде "ЧЧ:ММ:СС" :

//--- (1) Добавляет, (2) убирает символ из окна MarketWatch (Обзор рынка), (3) Возвращает флаг синхронизированности данных по символу
   bool              SetToMarketWatch(void)                       const { return ::SymbolSelect(this.m_symbol_name,true);                                   }
   bool              RemoveFromMarketWatch(void)                  const { return ::SymbolSelect(this.m_symbol_name,false);                                  }
   bool              IsSynchronized(void)                         const { return ::SymbolIsSynchronized(this.m_symbol_name);                                }
//--- Возвращает (1) время начала и (2) время окончания котировочной сессии дня недели, (3) время начала и конца требуемой котировочной сессии
   long              SessionQuoteTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)   const;
   long              SessionQuoteTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)     const;
   bool              GetSessionQuote(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to);
//--- Возвращает (1) время начала и (2) время окончания торговой сессии дня недели, (3) время начала и конца требуемой торговой сессии
   long              SessionTradeTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)   const;
   long              SessionTradeTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)     const;
   bool              GetSessionTrade(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to);
//--- (1) Осуществляет (1) подписку на стакан цен, (2) закрытие стакана цен, (3) заполняет данные стакана цен в массив структур
   bool              BookAdd(void)                                const;
   bool              BookClose(void)                              const;
//--- Возвращает (1) описание продолжительности сессии в чч:мм:сс, число (1) часов, (2) минут и (3) секунд во времени продолжительности сессии
   string            SessionDurationDescription(const ulong duration_sec) const;
private:
   int               SessionHours(const ulong duration_sec)       const;
   int               SessionMinutes(const ulong duration_sec)     const;
   int               SessionSeconds(const ulong duration_sec)     const;
public:
//+------------------------------------------------------------------+

В публичной секции в разделе методов упрощённого доступа к свойствам объекта-символа добавим вторую форму вызова метода, возвращающего флаг наличия символа на сервере (ранее мы оъявили приватный перегруженный метод, который ищет символ на сервере по его имени и возвращает флаг с результатом поиска)

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-символа           |
//+------------------------------------------------------------------+
//--- Целочисленные свойства
   long              Status(void)                                 const { return this.GetProperty(SYMBOL_PROP_STATUS);                                      }
   bool              IsCustom(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM);                                }
   color             ColorBackground(void)                        const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR);                     }
   ENUM_SYMBOL_CHART_MODE ChartMode(void)                         const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE);          }
   bool              IsExist(void)                                const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST);                                 }
   bool              IsExist(const string name)                   const { return this.SymbolExists(name);                                                   }
   bool              IsSelect(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT);                                }
   bool              IsVisible(void)                              const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE);                               }
   long              SessionDeals(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS);                               }

В публичной секции в разделе методов упрощённого доступа к вещественным свойствам символа добавим методы, возвращающие все коэффициенты взимания маржи:

//--- Вещественные свойства
   double            Bid(void)                                    const { return this.GetProperty(SYMBOL_PROP_BID);                                         }
   double            BidHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_BIDHIGH);                                     }
   double            BidLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_BIDLOW);                                      }
   double            Ask(void)                                    const { return this.GetProperty(SYMBOL_PROP_ASK);                                         }
   double            AskHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_ASKHIGH);                                     }
   double            AskLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_ASKLOW);                                      }
   double            Last(void)                                   const { return this.GetProperty(SYMBOL_PROP_LAST);                                        }
   double            LastHigh(void)                               const { return this.GetProperty(SYMBOL_PROP_LASTHIGH);                                    }
   double            LastLow(void)                                const { return this.GetProperty(SYMBOL_PROP_LASTLOW);                                     }
   double            VolumeReal(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL);                                 }
   double            VolumeHighReal(void)                         const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL);                             }
   double            VolumeLowReal(void)                          const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL);                              }
   double            OptionStrike(void)                           const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE);                               }
   double            Point(void)                                  const { return this.GetProperty(SYMBOL_PROP_POINT);                                       }
   double            TradeTickValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE);                            }
   double            TradeTickValueProfit(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT);                     }
   double            TradeTickValueLoss(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS);                       }
   double            TradeTickSize(void)                          const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE);                             }
   double            TradeContractSize(void)                      const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE);                         }
   double            TradeAccuredInterest(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST);                      }
   double            TradeFaceValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE);                            }
   double            TradeLiquidityRate(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE);                        }
   double            LotsMin(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN);                                  }
   double            LotsMax(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX);                                  }
   double            LotsStep(void)                               const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP);                                 }
   double            VolumeLimit(void)                            const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT);                                }
   double            SwapLong(void)                               const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG);                                   }
   double            SwapShort(void)                              const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT);                                  }
   double            MarginInitial(void)                          const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL);                              }
   double            MarginMaintenance(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE);                          }
   double            MarginLongInitial(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL);                         }
   double            MarginBuyStopInitial(void)                   const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL);                     }
   double            MarginBuyLimitInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL);                    }
   double            MarginBuyStopLimitInitial(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL);                }
   double            MarginLongMaintenance(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE);                     }
   double            MarginBuyStopMaintenance(void)               const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE);                 }
   double            MarginBuyLimitMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE);                }
   double            MarginBuyStopLimitMaintenance(void         const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE);            }
   double            MarginShortInitial(void)                     const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL);                        }
   double            MarginSellStopInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL);                    }
   double            MarginSellLimitInitial(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL);                   }
   double            MarginSellStopLimitInitial(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL);               }
   double            MarginShortMaintenance(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE);                    }
   double            MarginSellStopMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE);                }
   double            MarginSellLimitMaintenance(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE);               }
   double            MarginSellStopLimitMaintenance(void)         const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE);           }
   double            SessionVolume(void)                          const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME);                              }
   double            SessionTurnover(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER);                            }
   double            SessionInterest(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST);                            }
   double            SessionBuyOrdersVolume(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);                   }
   double            SessionSellOrdersVolume(void)                const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);                  }
   double            SessionOpen(void)                            const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN);                                }
   double            SessionClose(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE);                               }
   double            SessionAW(void)                              const { return this.GetProperty(SYMBOL_PROP_SESSION_AW);                                  }
   double            SessionPriceSettlement(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT);                    }
   double            SessionPriceLimitMin(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN);                     }
   double            SessionPriceLimitMax(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX);                     }
   double            MarginHedged(void)                           const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED);                               }
   double            NormalizedPrice(const double price)          const;
//--- Строковые свойства

Для получения  данных о свойствах символа, необходимо, чтобы символ был выбран в окне "Обзор рынка". Но может быть ситуация, когда символ не выбран в окне, но его свойства необходимо получить. Для этого нам необходимо создать флаг, указывающий на то, был ли выбран символ в окне "Обзор рынка" до того, как мы обращаемся к его свойствам. И далее действуем по схеме: если символ не выбран, то выбираем его, получаем свойства и опять скрываем из окна "Обзор рынка". Если же символ уже выбран, то просто получаем его свойства.

Так же нам необходимо в конструкторе класса инициализировать данные коэффициентов взимания маржи и заполнить их для MQL5. Для MQL4 такие данные отсутствуют и их значения остаются нулевыми после инициализации.
И добавим вызов методов сохранения этих свойств в полях свойств класса.

Для этого в конструкторе класса добавим необходимый код:

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name) : m_global_error(ERR_SUCCESS)
  {
   this.m_symbol_name=name;
   if(!this.Exist())
     {
      ::Print(DFUN_ERR_LINE,"\"",this.m_symbol_name,"\"",": ",TextByLanguage("Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"));
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
     }
   bool select=::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
   ::ResetLastError();
   if(!select)
     {
      if(!this.SetToMarketWatch())
        {
         this.m_global_error=::GetLastError();
         ::Print(DFUN_ERR_LINE,"\"",this.m_symbol_name,"\": ",TextByLanguage("Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "),this.m_global_error);
        }
     }
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,"\"",this.m_symbol_name,"\": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
     }
//--- Инициализация данных
   ::ZeroMemory(this.m_tick);
   this.Reset();
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.InitMarginRates();
   ::ResetLastError();
#ifdef __MQL5__
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "),this.m_global_error);
      return;
     }
#endif 
   
//--- Сохранение целочисленных свойств
   this.m_long_prop[SYMBOL_PROP_STATUS]                                       = symbol_status;
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SELECT]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
   this.m_long_prop[SYMBOL_PROP_VISIBLE]                                      = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VISIBLE);
   this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_DEALS);
   this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS);
   this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                          = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS);
   this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMEHIGH);
   this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                    = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMELOW);
   this.m_long_prop[SYMBOL_PROP_DIGITS]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_DIGITS);
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_SPREAD_FLOAT]                                 = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD_FLOAT);
   this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TICKS_BOOKDEPTH);
   this.m_long_prop[SYMBOL_PROP_TRADE_MODE]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_MODE);
   this.m_long_prop[SYMBOL_PROP_START_TIME]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_START_TIME);
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_TIME);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_EXEMODE]                                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_EXEMODE);
   this.m_long_prop[SYMBOL_PROP_SWAP_ROLLOVER3DAYS]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SWAP_ROLLOVER3DAYS);
   this.m_long_prop[SYMBOL_PROP_EXIST]                                        = this.SymbolExists();
   this.m_long_prop[SYMBOL_PROP_CUSTOM]                                       = this.SymbolCustom();
   this.m_long_prop[SYMBOL_PROP_MARGIN_HEDGED_USE_LEG]                        = this.SymbolMarginHedgedUseLEG();
   this.m_long_prop[SYMBOL_PROP_ORDER_MODE]                                   = this.SymbolOrderMode();
   this.m_long_prop[SYMBOL_PROP_FILLING_MODE]                                 = this.SymbolOrderFillingMode();
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_MODE]                              = this.SymbolExpirationMode();
   this.m_long_prop[SYMBOL_PROP_ORDER_GTC_MODE]                               = this.SymbolOrderGTCMode();
   this.m_long_prop[SYMBOL_PROP_OPTION_MODE]                                  = this.SymbolOptionMode();
   this.m_long_prop[SYMBOL_PROP_OPTION_RIGHT]                                 = this.SymbolOptionRight();
   this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                             = this.SymbolBackgroundColor();
   this.m_long_prop[SYMBOL_PROP_CHART_MODE]                                   = this.SymbolChartMode();
   this.m_long_prop[SYMBOL_PROP_TRADE_CALC_MODE]                              = this.SymbolCalcMode();
   this.m_long_prop[SYMBOL_PROP_SWAP_MODE]                                    = this.SymbolSwapMode();

//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_POINT)]                      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_POINT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_CONTRACT_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_STEP);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_LIMIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_LONG);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_SHORT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]         = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_TURNOVER);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_INTEREST);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_OPEN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_CLOSE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_AW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]            = this.SymbolVolumeHighReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]             = this.SymbolVolumeLowReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]     = this.SymbolTradeAccruedInterest();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]           = this.SymbolTradeFaceValue();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]       = this.SymbolTradeLiquidityRate();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]              = this.SymbolMarginHedged();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]              = this.m_margin_rate.Long.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]          = this.m_margin_rate.BuyStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]         = this.m_margin_rate.BuyLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)]     = this.m_margin_rate.BuyStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]          = this.m_margin_rate.Long.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)]      = this.m_margin_rate.BuyStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)]     = this.m_margin_rate.BuyLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]             = this.m_margin_rate.Short.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]         = this.m_margin_rate.SellStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]        = this.m_margin_rate.SellLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]    = this.m_margin_rate.SellStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]         = this.m_margin_rate.Short.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]     = this.m_margin_rate.SellStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]    = this.m_margin_rate.SellLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;

//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                       = this.m_symbol_name;
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_BASE)]              = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_BASE);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_PROFIT)]            = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_PROFIT);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_MARGIN)]            = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_MARGIN);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_DESCRIPTION)]                = ::SymbolInfoString(this.m_symbol_name,SYMBOL_DESCRIPTION);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_PATH)]                       = ::SymbolInfoString(this.m_symbol_name,SYMBOL_PATH);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_BASIS)]                      = this.SymbolBasis();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_BANK)]                       = this.SymbolBank();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_ISIN)]                       = this.SymbolISIN();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_FORMULA)]                    = this.SymbolFormula();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_PAGE)]                       = this.SymbolPage();
//--- Сохранение дополнительных целочисленных свойств
   this.m_long_prop[SYMBOL_PROP_DIGITS_LOTS]                                  = this.SymbolDigitsLot();
//---
   if(!select)
      this.RemoveFromMarketWatch();
  }
//+------------------------------------------------------------------+

Теперь нам необходимо написать реализации всех объявленных методов.

За пределами тела класса напишем реализацию метода, заполняющего все переменные, хранящие коэффициенты взимания маржи:

//+------------------------------------------------------------------+
//| Заполняет переменные коэффициентов взимания маржи                |
//+------------------------------------------------------------------+
bool CSymbol::MarginRates(void)
  {
   bool res=true;
   #ifdef __MQL5__
      res &=this.SymbolMarginLong();
      res &=this.SymbolMarginBuyStop();
      res &=this.SymbolMarginBuyLimit();
      res &=this.SymbolMarginBuyStopLimit();
      res &=this.SymbolMarginShort();
      res &=this.SymbolMarginSellStop();
      res &=this.SymbolMarginSellLimit();
      res &=this.SymbolMarginSellStopLimit();
   #else 
      this.InitMarginRates();
      res=false;
   #endif 
   return res;
  }
//+------------------------------------------------------------------+

В методе для MQL5 просто вызываются методы, считывающие из свойств символа данные по коэффициентам, и записывающие их в соответствующие переменные структуры, и результат возврата всех методов суммируется и возвращается в вызывающую программу. Методы будут рассмотрены ниже. Для MQL4 просто выполняется обнуление всех полей структуры.

Метод инициализации полей структуры свойств значений коэффициентов взимания маржи:

//+------------------------------------------------------------------+
//| Инициализирует коэффициенты взимания маржи                       |
//+------------------------------------------------------------------+
void CSymbol::InitMarginRates(void)
  {
   this.m_margin_rate.Long.Initial=0;           this.m_margin_rate.Long.Maintenance=0;
   this.m_margin_rate.BuyStop.Initial=0;        this.m_margin_rate.BuyStop.Maintenance=0;
   this.m_margin_rate.BuyLimit.Initial=0;       this.m_margin_rate.BuyLimit.Maintenance=0;
   this.m_margin_rate.BuyStopLimit.Initial=0;   this.m_margin_rate.BuyStopLimit.Maintenance=0;
   this.m_margin_rate.Short.Initial=0;          this.m_margin_rate.Short.Maintenance=0;
   this.m_margin_rate.SellStop.Initial=0;       this.m_margin_rate.SellStop.Maintenance=0;
   this.m_margin_rate.SellLimit.Initial=0;      this.m_margin_rate.SellLimit.Maintenance=0;
   this.m_margin_rate.SellStopLimit.Initial=0;  this.m_margin_rate.SellStopLimit.Maintenance=0;
  }
//+------------------------------------------------------------------+

Здесь просто обнуляются все поля структуры m_margin_rate.

Реализация второй формы вызова метода, возвращающего флаг наличия символа на сервере:

//+------------------------------------------------------------------+
//| Возвращает флаг существования символа                            |
//+------------------------------------------------------------------+
long CSymbol::SymbolExists(void) const
  {
   return(#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXIST) #else this.Exist() #endif);
  }
//+------------------------------------------------------------------+
bool CSymbol::SymbolExists(const string name) const
  {
   return(#ifdef __MQL5__ (bool)::SymbolInfoInteger(name,SYMBOL_EXIST) #else this.Exist(name) #endif);
  }
//+------------------------------------------------------------------+

Здесь: для MQL5 возвращается свойство символа SYMBOL_EXIST, а для MQL4 выполняется поиск символа на сервере при помощи второй формы вызова метода Exist(const string name).

Реализация методов, заполняющих в структуре коэффициенты взимания маржи для всех типов ордеров:

//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по длинным позициям        |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginLong(void) 
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY,this.m_margin_rate.Long.Initial,this.m_margin_rate.Long.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по коротким позициям       |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginShort(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL,this.m_margin_rate.Short.Initial,this.m_margin_rate.Short.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по BuyStop-ордерам         |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginBuyStop(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY_STOP,this.m_margin_rate.BuyStop.Initial,this.m_margin_rate.BuyStop.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по BuyLimit-ордерам        |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginBuyLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY_LIMIT,this.m_margin_rate.BuyLimit.Initial,this.m_margin_rate.BuyLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по BuyStopLimit-ордерам    |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginBuyStopLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY_STOP_LIMIT,this.m_margin_rate.BuyStopLimit.Initial,this.m_margin_rate.BuyStopLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по SellStop-ордерам        |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginSellStop(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL_STOP,this.m_margin_rate.SellStop.Initial,this.m_margin_rate.SellStop.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по SellLimit-ордерам       |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginSellLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL_LIMIT,this.m_margin_rate.SellLimit.Initial,this.m_margin_rate.SellLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Заполняет коэффициенты взимания маржи по SellStopLimit-ордерам   |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginSellStopLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL_STOP_LIMIT,this.m_margin_rate.SellStopLimit.Initial,this.m_margin_rate.SellStopLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+

Здесь: для MQL5 вызывается функция SymbolInfoMarginRate(), в которой заполняются требуемые свойства, хранящиеся в структуре m_margin_rate, и возвращается результат работы функции. Для MQL4 возвращаем false.

В методе, возвращающем описание целочисленных свойств символа в блоке возврата описания цвета фона символа в окне "Обзор рынка" внесём изменения:

      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Цвет фона символа в Market Watch","Background color of the symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
         #ifdef __MQL5__
         (this.GetProperty(property)==CLR_DEFAULT || this.GetProperty(property)==CLR_NONE ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :

Ранее мы сравнивали цвет с белым (clrWhite), и если значение цвета больше, чем значение "белого" цвета, то считалось, что цвет не задан. Мы уже обсудили ошибочность данного метода сравнения, и поэтому здесь для определения отсутствия заданного цвета фона для символа в окне "Обзор рынка", сравниваем цвет с "цветом по умолчанию" или с "отсутствующим цветом".

В метод, возвращающий описания вещественных свойств символа GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE property), добавим вывод описания всех коэффициентов взимания маржи:

//--- Коэффициент взимания начальной маржи позиции Long
      property==SYMBOL_PROP_MARGIN_LONG_INITIAL          ?  TextByLanguage("Коэффициент взимания начальной маржи по длинным позициям","Coefficient of margin initial charging for long positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициент взимания начальной маржи позиции Short
      property==SYMBOL_PROP_MARGIN_SHORT_INITIAL     ?  TextByLanguage("Коэффициент взимания начальной маржи по коротким позициям","Coefficient of margin initial charging for short positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициент взимания поддерживающей маржи позиции Long
      property==SYMBOL_PROP_MARGIN_LONG_MAINTENANCE          ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по длинным позициям","Coefficient of margin maintenance charging for long positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициент взимания поддерживающей маржи позиции Short
      property==SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE          ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по коротким позициям","Coefficient of margin maintenance charging for short positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициенты взимания начальной маржи по ордерам Long
      property==SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL      ?  TextByLanguage("Коэффициент взимания начальной маржи по BuyStop ордерам","Coefficient of margin initial charging for BuyStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL     ?  TextByLanguage("Коэффициент взимания начальной маржи по BuyLimit ордерам","Coefficient of margin initial charging for BuyLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL ?  TextByLanguage("Коэффициент взимания начальной маржи по BuyStopLimit ордерам","Coefficient of margin initial charging for BuyStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициенты взимания начальной маржи по ордерам Short
      property==SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL      ?  TextByLanguage("Коэффициент взимания начальной маржи по SellStop ордерам","Coefficient of margin initial charging for SellStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL     ?  TextByLanguage("Коэффициент взимания начальной маржи по SellLimit ордерам","Coefficient of margin initial charging for SellLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL ?  TextByLanguage("Коэффициент взимания начальной маржи по SellStopLimit ордерам","Coefficient of margin initial charging for SellStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициенты взимания поддерживающей маржи по ордерам Long
      property==SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE      ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по BuyStop ордерам","Coefficient of margin maintenance charging for BuyStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE     ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по BuyLimit ордерам","Coefficient of margin maintenance charging for BuyLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по BuyStopLimit ордерам","Coefficient of margin maintenance charging for BuyStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Коэффициенты взимания поддерживающей маржи по ордерам Short
      property==SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE      ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по SellStop ордерам","Coefficient of margin maintenance charging for SellStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE     ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по SellLimit ордерам","Coefficient of margin maintenance charging for SellLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по SellStopLimit ордерам","Coefficient of margin maintenance charging for SellStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") #endif 
         )  :
   //---

Реализация второй формы вызова метода, ищущего символ по его имени на сервере, и возвращающий флаг наличия символа:

//+------------------------------------------------------------------+
//| Ищет символ и возвращает флаг его наличия на сервере             |
//+------------------------------------------------------------------+
bool CSymbol::Exist(void) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==this.m_symbol_name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+
bool CSymbol::Exist(const string name) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+

Реализация метода, подсчитывающего и возвращающего количество знаков после запятой в double-значении:

//+------------------------------------------------------------------+
//| Возвращает количество знаков после запятой в double-значении     |
//+------------------------------------------------------------------+
int CSymbol::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

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

Реализация методов, возвращающих время начала котировочной сессии от начала дня, время окончания котировочной сессии от начала дня и время начала и конца котировочной сессии:

//+------------------------------------------------------------------+
//| Возвращает время начала котировочной сессии                      |
//| в секундах от начала дня                                         |
//+------------------------------------------------------------------+
long CSymbol::SessionQuoteTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionQuote(this.m_symbol_name,day,session_index,from,to) ? from : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Возвращает время в секундах от начала дня                        |
//| до окончания котировочной сессии                                 |
//+------------------------------------------------------------------+
long CSymbol::SessionQuoteTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionQuote(this.m_symbol_name,day,session_index,from,to) ? to : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Возвращает время начала и конца требуемой котировочной сессии    |
//+------------------------------------------------------------------+
bool CSymbol::GetSessionQuote(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to)
  {
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return ::SymbolInfoSessionQuote(this.m_symbol_name,day,session_index,from,to);
  }
//+------------------------------------------------------------------+

В первые два метода передаются индекс сессии и день недели, в третий дополнительно передаются переменные с типом datetime, в которые будут записаны данные о начале и конце требуемой сессии, полученные посредством функции SymbolInfoSessionQuote().

Для удобства, если в качестве дня недели передано -1, то данные по сессии берутся для текущего дня недели. Индекс сессии должен начинаться от нуля. Время возвращается в виде количества секунд от начала дня, определяемого параметром day_of_week. Таким образом, всегда можно узнать реальное запрашиваемое время прибавлением ко времени начала дня то количество секунд, которое было получено из метода.

Таким же образом реализованы методы получения времён торговых сессий:

//+------------------------------------------------------------------+
//| Возвращает время начала торговой сессии                          |
//| в секундах от начала дня                                         |
//+------------------------------------------------------------------+
long CSymbol::SessionTradeTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionTrade(this.m_symbol_name,day,session_index,from,to) ? from : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Возвращает время в секундах от начала дня                        |
//| до окончания торговой сессии                                     |
//+------------------------------------------------------------------+
long CSymbol::SessionTradeTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionTrade(this.m_symbol_name,day,session_index,from,to) ? to : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Возвращает время начала и конца требуемой торговой сессии        |
//+------------------------------------------------------------------+
bool CSymbol::GetSessionTrade(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to)
  {
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return ::SymbolInfoSessionTrade(this.m_symbol_name,day,session_index,from,to);
  }
//+------------------------------------------------------------------+

В этих методах всё аналогично описанным выше, за исключением того, что здесь для получения нужных данных используется функция SymbolInfoSessionTrade().

Реализация метода, возвращающего текущий день недели в виде значения перечисления ENUM_DAY_OF_WEEK:

//+------------------------------------------------------------------+
//| Возвращает текущий день недели                                   |
//+------------------------------------------------------------------+
ENUM_DAY_OF_WEEK CSymbol::CurrentDayOfWeek(void) const
  {
   MqlDateTime time={0};
   ::TimeCurrent(time);
   return(ENUM_DAY_OF_WEEK)time.day_of_week;
  }
//+------------------------------------------------------------------+

Здесь всё просто: объявляем структуру даты и времени, обращаемся к функции TimeCurrent(), вторая форма вызова которой заполняет структуру даты и времени, переданную в функцию, и в итоге возвращаем день недели из заполненной структуры.

Реализация метода, возвращающего число секунд во времени продолжительности указанной сессии:

//+------------------------------------------------------------------+
//| Возвращает число скунд во времени продолжительности сессии       |
//+------------------------------------------------------------------+
int CSymbol::SessionSeconds(const ulong duration_sec) const
  {
   return int(duration_sec % 60);
  }
//+------------------------------------------------------------------+

В метод передаётся число секунд и возвращается остаток от деления на количество минут в этом промежутке времени.

Реализация метода, возвращающего число минут во времени продолжительности указанной сессии:

//+------------------------------------------------------------------+
//| Возвращает число минут во времени продолжительности сессии       |
//+------------------------------------------------------------------+
int CSymbol::SessionMinutes(const ulong duration_sec) const
  {
   return int((duration_sec-this.SessionSeconds(duration_sec)) % 3600)/60;
  }
//+------------------------------------------------------------------+

В метод передаётся число секунд и возвращается рассчитанное количество минут в этом промежутке времени за исключением числа секунд, не кратных одной минуте.

Реализация метода, возвращающего число часов во времени продолжительности указанной сессии:

//+------------------------------------------------------------------+
//| Возвращает число часов во времени продолжительности сессии       |
//+------------------------------------------------------------------+
int CSymbol::SessionHours(const ulong duration_sec) const
  {
   return int(duration_sec-this.SessionSeconds(duration_sec)-this.SessionMinutes(duration_sec))/3600;
  }
//+------------------------------------------------------------------+

В метод передаётся число секунд и возвращается количество часов в этом промежутке времени за исключением числа секунд, не кратных одной минуте и числа минут, не кратных одному часу.

Реализация метода, возвращающего описание продолжительности сессии в виде "ЧЧ:ММ:СС":

//+------------------------------------------------------------------+
//| Возвращает описание продолжительности сессии в чч:мм:сс          |
//+------------------------------------------------------------------+
string CSymbol::SessionDurationDescription(const ulong duration_sec) const
  {
   int sec=this.SessionSeconds(duration_sec);
   int min=this.SessionMinutes(duration_sec);
   int hour=this.SessionHours(duration_sec);
   return ::IntegerToString(hour,2,'0')+":"+::IntegerToString(min,2,'0')+":"+::IntegerToString(sec,2,'0');
  }
//+------------------------------------------------------------------+

Здесь просто получаем продолжительность сессии в секундах, получаем рассчитанную продолжительность сессии в секундах, минутах и часах, и выводим отформатированное сообщение в формате Часы:Минуты:Секунды при помощи функции IntegerToString() с размером длины строки для часов, минут и секунд равной двум знакам, и заполнителем в виде "0" в случае, если в значении часа, минуты или секунд будет лишь одно число.
Например, если получили 2 часа, то выводиться будет как 02.

Так как статусы объектов-символов мы немного переработали, то и подправим метод, выводящий описание статуса объекта-символа:

//+------------------------------------------------------------------+
//| Возвращает описание статуса                                      |
//+------------------------------------------------------------------+
string CSymbol::GetStatusDescription() const
  {
   return
     (
      this.Status()==SYMBOL_STATUS_FX           ? TextByLanguage("Форекс символ","Forex symbol")                  :
      this.Status()==SYMBOL_STATUS_FX_MAJOR     ? TextByLanguage("Форекс символ-мажор","Forex major symbol")      :
      this.Status()==SYMBOL_STATUS_FX_MINOR     ? TextByLanguage("Форекс символ-минор","Forex minor symbol")      :
      this.Status()==SYMBOL_STATUS_FX_EXOTIC    ? TextByLanguage("Форекс символ-экзотик","Forex Exotic Symbol")   :
      this.Status()==SYMBOL_STATUS_FX_RUB       ? TextByLanguage("Форекс символ/рубль","Forex symbol RUB")        :
      this.Status()==SYMBOL_STATUS_METAL        ? TextByLanguage("Металл","Metal")                                :
      this.Status()==SYMBOL_STATUS_INDEX        ? TextByLanguage("Индекс","Index")                                :
      this.Status()==SYMBOL_STATUS_INDICATIVE   ? TextByLanguage("Индикатив","Indicative")                        :
      this.Status()==SYMBOL_STATUS_CRYPTO       ? TextByLanguage("Криптовалютный символ","Crypto symbol")         :
      this.Status()==SYMBOL_STATUS_COMMODITY    ? TextByLanguage("Товарный символ","Commodity symbol")            :
      this.Status()==SYMBOL_STATUS_EXCHANGE     ? TextByLanguage("Биржевой символ","Exchange symbol")             : 
      this.Status()==SYMBOL_STATUS_FUTURES      ? TextByLanguage("Фьючерс","Furures")                             : 
      this.Status()==SYMBOL_STATUS_CFD          ? TextByLanguage("Контракт на разницу","Contract For Difference") : 
      this.Status()==SYMBOL_STATUS_STOCKS       ? TextByLanguage("Ценная бумага","Stocks")                        : 
      this.Status()==SYMBOL_STATUS_BONDS        ? TextByLanguage("Облигация","Bonds")                             : 
      this.Status()==SYMBOL_STATUS_OPTION       ? TextByLanguage("Опцион","Option")                               : 
      this.Status()==SYMBOL_STATUS_COLLATERAL   ? TextByLanguage("Неторгуемый актив","Collateral")                : 
      this.Status()==SYMBOL_STATUS_CUSTOM       ? TextByLanguage("Пользовательский символ","Custom symbol")       :
      this.Status()==SYMBOL_STATUS_COMMON       ? TextByLanguage("Символ общей группы","Common group symbol")     :
      ::EnumToString((ENUM_SYMBOL_STATUS)this.Status())
     );
  }
//+------------------------------------------------------------------+

В метод обновления всех данных символа допишем для MQL5 получение коэффициентов взимания маржи для всех типов ордеров и позиций.
Для MQL4, где они не используются, их значения будут нулевыми после начальной инициализации в конструкторе класса:

//+------------------------------------------------------------------+
//| Обновляет все данные символа, которые могут измениться           |
//+------------------------------------------------------------------+
void CSymbol::Refresh(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();

      return;
     }
#ifdef __MQL5__
   ::ResetLastError();
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      return;
     }
#endif 
//--- Обновление целочисленных свойств
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SELECT]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
   this.m_long_prop[SYMBOL_PROP_VISIBLE]                                      = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VISIBLE);
   this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_DEALS);
   this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS);
   this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                          = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS);
   this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMEHIGH);
   this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                    = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMELOW);
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TICKS_BOOKDEPTH);
   this.m_long_prop[SYMBOL_PROP_START_TIME]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_START_TIME);
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_TIME);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
   this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                             = this.SymbolBackgroundColor();
//--- Обновление вещественных свойств
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_CONTRACT_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_STEP);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_LIMIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_LONG);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_SHORT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]         = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_TURNOVER);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_INTEREST);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_OPEN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_CLOSE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_AW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]            = this.SymbolVolumeHighReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]             = this.SymbolVolumeLowReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]     = this.SymbolTradeAccruedInterest();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]           = this.SymbolTradeFaceValue();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]       = this.SymbolTradeLiquidityRate();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]              = this.SymbolMarginHedged();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]              = this.m_margin_rate.Long.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]          = this.m_margin_rate.BuyStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]         = this.m_margin_rate.BuyLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)]     = this.m_margin_rate.BuyStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]          = this.m_margin_rate.Long.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)]      = this.m_margin_rate.BuyStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)]     = this.m_margin_rate.BuyLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]             = this.m_margin_rate.Short.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]         = this.m_margin_rate.SellStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]        = this.m_margin_rate.SellLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]    = this.m_margin_rate.SellStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]         = this.m_margin_rate.Short.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]     = this.m_margin_rate.SellStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]    = this.m_margin_rate.SellLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
  }
//+------------------------------------------------------------------+

Здесь: для MQL5 сразу вызываем метод получения данных о коэффициентах взимания маржи MarginRates(), и если хоть один из коэффициентов не был получен (метод вернул false), то просто записываем код ошибки в переменную, хранящую код ошибки класса и без сообщения выходим из метода.
Сообщение об ошибке не выводим в журнал по причине, что метод работает в таймере, и если вдруг выйдет ошибочное получение данных, то очень быстро журнал заполнится мусорными сообщениями об одной и той же ошибке. А так как этот код ошибки можно всегда получить в классе CEngine, то и оставим обязанность её получения и обработки для него.
В самом конце метода все полученные данные о коэффициентах записываются в поля соответствующих свойств объекта-символа.

По той же вышеописанной причине удалим из метода обновления котировочных данных строку с выводом сообщения об ошибке в журнал:

//+------------------------------------------------------------------+
//| Обновляет котировочные данные по символу                         |
//+------------------------------------------------------------------+
void CSymbol::RefreshRates(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
      return;
     }
//--- Обновление целочисленных свойств
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
//--- Обновление вещественных свойств
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
  }  
//+------------------------------------------------------------------+

Теперь метод будет выглядеть так:

//+------------------------------------------------------------------+
//| Обновляет котировочные данные по символу                         |
//+------------------------------------------------------------------+
void CSymbol::RefreshRates(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
//--- Обновление целочисленных свойств
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
//--- Обновление вещественных свойств
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
  }  
//+------------------------------------------------------------------+

На этом доработка класса абстрактного символа CSymbol завершена.

Мы рассмотрели важные и наиболее значимые изменения методов класса за исключением мелких правок, которые были внесены, но не описаны здесь потому, что это просто некоторые орфографические и "смысловые" ошибки. В основном в методах описания свойств. Их можно будет видеть в прилагаемых к статье файлах.

Теперь нам необходимо создать объекты-наследники базового класса абстрактного символа. И уже именно эти объекты, распределённые по категориям, будем размещать в коллекцию объектов-символов.

Объекты-наследники базового абстрактного объекта "символ"

Вернёмся немного назад и посмотрим озвученные нами категории симвлов, а также сразу и определимся с наименованиями соответствующих классов-наследников базового класса CSymbol:

  • Форекс символ — класс CSymbolFX
  • Форекс символ-мажор — класс CSymbolFXMajor
  • Форекс символ-минор — класс CSymbolFXMinor
  • Форекс символ-экзотик — класс CSymbolFXExotic
  • Форекс символ/рубль — класс CSymbolFXRub
  • Металл — класс CSymbolMetall
  • Индекс — класс CSymbolIndex
  • Индикатив — класс CSymbolIndicative
  • Криптовалютный символ — класс CSymbolCrypto
  • Товарный символ — класс CSymbolCommodity
  • Биржевой символ — класс CSymbolExchange
  • Фьючерс — класс CSymbolFutures
  • Контракт на разницу (CFD) — класс CSymbolCFD
  • Ценная бумага — класс CSymbolStocks
  • Облигация — класс CSymbolBonds
  • Опцион — класс CSymbolOption
  • Неторгуемый актив — класс CSymbolCollateral
  • Пользовательский символ — класс CSymbolCustom
  • Общая категория — класс CSymbolCommon
Итого мы имеем девятнадцать производных классов. Рассмотрим создание класса на примере класса форекс-символа.

В папке библиотеки \MQL5\Include\DoEasy\Objects\Symbols\ создадим новый класс CSymbolFX с именем файла SymbolFX.mqh. Базовым классом для него должен быть класс абстрактного символа CSymbol.
Сразу же объявим все необходимые методы для работы класса:

//+------------------------------------------------------------------+
//|                                                     SymbolFX.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Форекс символ                                                    |
//+------------------------------------------------------------------+
class CSymbolFX : public CSymbol
  {
public:
//--- Конструктор
                     CSymbolFX(const string name) : CSymbol(SYMBOL_STATUS_FX,name) {}
//--- Поддерживаемые целочисленные свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Поддерживаемые вещественные свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Поддерживаемые строковые свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Выводит в журнал краткое описание символа
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

В конструктор класса будем передавать имя символа, а в списке инициализации конструктора класса отправим в базовый класс категорию символа (его статус) "Форекс-символ" и имя символа, передаваемое в конструктор класса CSymbolFX при его создании.

Виртуальные методы поддержания объектом целочисленных, вещественных и строковых свойств были объявлены в базовом классе, а в классах-наследниках мы сделаем их реализацию. Виртуальный метод PrintShort(), так же объявленный в базовом классе и реализованный в классе-наследнике, будет выводить краткую информацию по символу в журнал.

Забегая наперёд скажу — почти все эти методы у всех классов-наследников практически одинаковые, и их можно было сделать в базовом классе, не создавая при этом классы-наследники. Но в таком случае теряется гибкость проведения возможных изменений этих методов для каждой из групп символов. Поэтому было решено сделать разделение по категориям именно через классы-наследники, чтобы в будущем иметь при необходимости возможность изменить каждый из классов-наследников раздельно друг от друга, что гораздо проще и быстрее.

Реализация метода, возвращающего флаг поддержания объектом-символом целочисленного свойства:

//+------------------------------------------------------------------+
//| Возвращает истину, если символ поддерживает переданное           |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CSymbolFX::SupportProperty(ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(property==SYMBOL_PROP_EXIST
   #ifdef __MQL4__                                 ||
      property==SYMBOL_PROP_CUSTOM                 ||
      property==SYMBOL_PROP_SESSION_DEALS          ||
      property==SYMBOL_PROP_SESSION_BUY_ORDERS     ||
      property==SYMBOL_PROP_SESSION_SELL_ORDERS    ||
      property==SYMBOL_PROP_VOLUME                 ||
      property==SYMBOL_PROP_VOLUMEHIGH             ||
      property==SYMBOL_PROP_VOLUMELOW              ||
      property==SYMBOL_PROP_TICKS_BOOKDEPTH        ||
      property==SYMBOL_PROP_OPTION_MODE            ||
      property==SYMBOL_PROP_OPTION_RIGHT           ||
      property==SYMBOL_PROP_BACKGROUND_COLOR 
   #endif         
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

В метод передаётся проверяемое целочисленное свойство, и далее для MQL5 и MQL4, если переданное свойство "Существование символа", то возвращаем false — ведь если символ создан, то он существует, и данное его свойство нам не нужно ни для вывода в журнал, ни тем более для поиска и сортировки. Все остальные проверки относятся только к MQL4 и возвращается false при передаче в метод заведомо неподдерживаемого свойства символа в MQL4.
Если переданное свойство не было среди перечисленных в проверке свойств — значит оно поддерживается, и возвращаем true.

Реализация метода, возвращающего флаг поддержания объектом-символом вещественного свойства:

//+------------------------------------------------------------------+
//| Возвращает истину, если символ поддерживает переданное           |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CSymbolFX::SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property)
  {
   if(
     #ifdef __MQL5__
      (this.ChartMode()==SYMBOL_CHART_MODE_BID     && 
        (
         property==SYMBOL_PROP_LAST                ||
         property==SYMBOL_PROP_LASTHIGH            ||
         property==SYMBOL_PROP_LASTLOW
        )
      )                                            ||
      (this.ChartMode()==SYMBOL_CHART_MODE_LAST    && 
        (
         property==SYMBOL_PROP_BID                 ||
         property==SYMBOL_PROP_BIDHIGH             ||
         property==SYMBOL_PROP_BIDLOW              ||
         property==SYMBOL_PROP_ASK                 ||
         property==SYMBOL_PROP_ASKHIGH             ||
         property==SYMBOL_PROP_ASKLOW
        )
      )
     //--- __MQL4__
     #else 
      property==SYMBOL_PROP_ASKHIGH                            ||
      property==SYMBOL_PROP_ASKLOW                             ||
      property==SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT            ||
      property==SYMBOL_PROP_TRADE_TICK_VALUE_LOSS              ||
      property==SYMBOL_PROP_LAST                               ||
      property==SYMBOL_PROP_LASTHIGH                           ||
      property==SYMBOL_PROP_LASTLOW                            ||
      property==SYMBOL_PROP_VOLUME_LIMIT                       ||
      property==SYMBOL_PROP_MARGIN_LONG_INITIAL                ||
      property==SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL            ||
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL           ||
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL       ||
      property==SYMBOL_PROP_MARGIN_LONG_MAINTENANCE            ||
      property==SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE        ||
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE       ||
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE   ||
      property==SYMBOL_PROP_MARGIN_SHORT_INITIAL               ||
      property==SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL           ||
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL          ||
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL      ||
      property==SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE           ||
      property==SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE       ||
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE      ||
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE  ||
      property==SYMBOL_PROP_SESSION_VOLUME                     ||
      property==SYMBOL_PROP_SESSION_TURNOVER                   ||
      property==SYMBOL_PROP_SESSION_INTEREST                   ||
      property==SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME          ||
      property==SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME         ||
      property==SYMBOL_PROP_SESSION_OPEN                       ||
      property==SYMBOL_PROP_SESSION_CLOSE                      ||
      property==SYMBOL_PROP_SESSION_AW                         ||
      property==SYMBOL_PROP_SESSION_PRICE_SETTLEMENT           ||
      property==SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN            ||
      property==SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX            ||
      property==SYMBOL_PROP_VOLUME_REAL                        ||
      property==SYMBOL_PROP_VOLUMEHIGH_REAL                    ||
      property==SYMBOL_PROP_VOLUMELOW_REAL                     ||
      property==SYMBOL_PROP_OPTION_STRIKE                      ||
      property==SYMBOL_PROP_TRADE_ACCRUED_INTEREST             ||
      property==SYMBOL_PROP_TRADE_FACE_VALUE                   ||
      property==SYMBOL_PROP_TRADE_LIQUIDITY_RATE
     #endif         
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Здесь логика та же, что и в прошлом методе. Но сначала проверяем полученное свойство для MQL5, и если это одно из свойств цены последней сделки (Last), но при этом график строится по ценам Bid, то все эти свойства будут равны нулю, и соответственно — они в данном случае не поддерживаются.
Аналогично поступаем со свойствами цен Bid в случае, если график строится по ценам Last — все свойства цены Bid не поддерживаются.
Для MQL4 поступаем точно так же, как и в прошлом методе — при передаче в метод заведомо не поддерживаемого свойства символа, возвращаем false.

Реализация метода, возвращающего флаг поддержания объектом-символом строкового свойства:

//+------------------------------------------------------------------+
//| Возвращает истину, если символ поддерживает переданное           |
//| строковое свойство, возвращает ложь в противном случае           |
//+------------------------------------------------------------------+
bool CSymbolFX::SupportProperty(ENUM_SYMBOL_PROP_STRING property)
  {
   if(
   #ifdef __MQL5__ 
      property==SYMBOL_PROP_FORMULA && !this.IsCustom()
   #else
      property==SYMBOL_PROP_BASIS                  || 
      property==SYMBOL_PROP_BANK                   ||
      property==SYMBOL_PROP_ISIN                   ||
      property==SYMBOL_PROP_FORMULA                ||
      property==SYMBOL_PROP_PAGE
   #endif         
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Здесь всё так же, как и в двух предыдущих: для MQL5если переданное свойство "Формула для расчёта пользовательского символа", и при этом этот символ не пользовательский, то возвращаем false — свойство не поддерживается. А далее — проверки заведомо не поддерживаемых свойств символа для MQL4 и возврат false, если передано не поддерживаемое в MQL4 свойство.

Метод вывода в журнал краткого описания символа:

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание символа                        |
//+------------------------------------------------------------------+
void CSymbolFX::PrintShort(void)
  {
   ::Print(this.GetStatusDescription()+" "+this.Name());
  }
//+------------------------------------------------------------------+

Метод просто выводит в журнал строку, состоящую из строкового описания статуса символа и его наименования.

Остальные классы-наследники устроены точно так же и имеют такие же методы с такой же реализацией.
Отличием является класс пользовательского символа — такого типа символов однозначно нет в MQL4, поэтому и все проверки относятся лишь к MQL5:

//+------------------------------------------------------------------+
//|                                                 SymbolCustom.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Пользовательский символ                                          |
//+------------------------------------------------------------------+
class CSymbolCustom : public CSymbol
  {
public:
//--- Конструктор
                     CSymbolCustom(const string name) : CSymbol(SYMBOL_STATUS_CUSTOM,name) {}
//--- Поддерживаемые целочисленные свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Поддерживаемые вещественные свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Поддерживаемые строковые свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Выводит в журнал краткое описание символа
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+
//| Возвращает истину, если символ поддерживает переданное           |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(property==SYMBOL_PROP_EXIST) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если символ поддерживает переданное           |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property)
  {
   if(
      (this.ChartMode()==SYMBOL_CHART_MODE_BID     && 
        (
         property==SYMBOL_PROP_LAST                ||
         property==SYMBOL_PROP_LASTHIGH            ||
         property==SYMBOL_PROP_LASTLOW
        )
      )                                            ||
      (this.ChartMode()==SYMBOL_CHART_MODE_LAST    && 
        (
         property==SYMBOL_PROP_BID                 ||
         property==SYMBOL_PROP_BIDHIGH             ||
         property==SYMBOL_PROP_BIDLOW              ||
         property==SYMBOL_PROP_ASK                 ||
         property==SYMBOL_PROP_ASKHIGH             ||
         property==SYMBOL_PROP_ASKLOW
        )
      )
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если символ поддерживает переданное           |
//| строковое свойство, возвращает ложь в противном случае           |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_STRING property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание символа                        |
//+------------------------------------------------------------------+
void CSymbolCustom::PrintShort(void)
  {
   ::Print(this.GetStatusDescription()+" "+this.Name());
  }
//+------------------------------------------------------------------+

На этом мы завершили разработку классов-наследников CSymbol.
Реализацию остальных классов-наследников можно будет посмотреть в конце статьи в прикреплённых файлах.

Так как нам потребуется поиск и сортировка в коллекции символов, то следует создать для этого необходимый функционал. Откроем файл Select.mqh, расположенный в папке библиотеки \MQL5\Include\DoEasy\Services\, и внесём в него дополнения.
Первым делом подключим класс абстрактного символа:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+

В публичной секции класса объявим все необходимые методы для поиска и сортировки:

//+------------------------------------------------------------------+
//| Класс для выборки объектов, удовлетворяющих критерию             |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Метод сравнения двух величин
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
//+------------------------------------------------------------------+
//| Методы работы с ордерами                                         |
//+------------------------------------------------------------------+
   //--- Возвращает список ордеров, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Возвращает индекс ордера в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
   //--- Возвращает индекс ордера в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
//+------------------------------------------------------------------+
//| Методы работы с событиями                                        |
//+------------------------------------------------------------------+
   //--- Возвращает список событий, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Возвращает индекс события в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
   //--- Возвращает индекс события в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Методы работы с аккаунтами                                       |
//+------------------------------------------------------------------+
   //--- Возвращает список аккаунтов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Возвращает индекс события в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
   //--- Возвращает индекс события в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Методы работы с символами                                        |
//+------------------------------------------------------------------+
   //--- Возвращает список символов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Возвращает индекс символа в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property);
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property);
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property);
   //--- Возвращает индекс символа в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property);
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property);
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

За пределами класса напишем их реализацию:

//+------------------------------------------------------------------+
//| Методы работы со списками символов                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Возвращает список символов, у которых одно из целочисленных      |
//| свойств удовлетворяет заданному критерию                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CSymbol *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Возвращает список символов, у которых одно из вещественных       |
//| свойств удовлетворяет заданному критерию                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CSymbol *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Возвращает список символов, у которых одно из строковых          |
//| свойств удовлетворяет заданному критерию                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CSymbol *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс символа в списке                               |
//| с максимальным значением целочисленного свойства                 |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CSymbol *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CSymbol *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс символа в списке                               |
//| с максимальным значением вещественного свойства                  |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CSymbol *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CSymbol *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс символа в списке                               |
//| с максимальным значением строкового свойства                     |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CSymbol *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CSymbol *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс символа в списке                               |
//| с минимальным значением целочисленного свойства                  |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMin(CArrayObj* list_source,ENUM_SYMBOL_PROP_INTEGER property)
  {
   int index=0;
   CSymbol *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CSymbol *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс символа в списке                               |
//| с минимальным значением вещественного свойства                   |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMin(CArrayObj* list_source,ENUM_SYMBOL_PROP_DOUBLE property)
  {
   int index=0;
   CSymbol *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CSymbol *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс символа в списке                               |
//| с минимальным значением строкового свойства                      |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMin(CArrayObj* list_source,ENUM_SYMBOL_PROP_STRING property)
  {
   int index=0;
   CSymbol *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CSymbol *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+

Подобные методы у нас есть для каждого класса-коллекции, и их работа рассматривалась в третьей части описания билиотеки при создании класса CSelect, поэтому здесь не будем останавливаться на их разборе.

Теперь у нас всё готово для создания класса-коллекции символов.
Для работы с коллекцией символов из программы можно будет использовать четыре режима:

  1. Работа только с одним символом,
  2. Работа со списком символов,
  3. Работа с окном "Обзор рынка",
  4. Работа с полным спискм символов на сервере.

Для того чтобы класс коллекции символов "знал" с чем ему работать, будем использовать такую схему:
В программе — в её настройках, задаётся перечисление методов работы с символами. Это может быть один из четырёх озвученных режимов работы.
В программе так же должен быть строковый массив, который будет заполняться функцией из библиотеки по такому принципу:

  • если выбрана работа с одним символом, то в массиве будет содержаться только текущий символ,
  • если выбрана работа с пользовательским списком символов, который тоже может находиться в настройках программы, и в нём через запятую прописаны нужные символы, то массив будет заполнен символами из этой строки; если в этой строке будет прописан только один текущий символ или список пустой, то работа будет с текущим символом
  • если выбрана работа с обзором рынка, то в массиве, в его единственной ячейке, будет вместо названия символа прописано "MARKET_WATCH"
  • если же выбрана работа с полным списком символов на сервере, то в массиве будет вместо названия символа прописано "ALL"

Всё это будет производиться автоматически, и от пользователя нужно будет лишь сделать организацию выбора нужного режима работы с коллекцией символов и создать минимум один строковый массив, максимум — строковый массив и строку предопределённых символов в настройках.


Класс коллекции символов

В папке библиотеки \MQL5\Include\DoEasy\Collections\ создадим новый класс CSymbolsCollection в файле SymbolsCollection.mqh.
Базовым классом для него должен быть класс объекта стандартной библиотеки CObject.

Сразу же подключим к вновь созданному файлу все необходимые для работы файлы классов:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   
public:
//--- Конструктор
                     CSymbolsCollection();

  };
//+------------------------------------------------------------------+

И добавим уже ставшие стандартными для классов-коллекций библиотеки переменные-члены класса и методы:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   CListObj          m_list_all_symbols;     // Список всех объектов-символов
   
public:
//--- Возвращает полный список-коллекцию "как есть"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }

//--- Конструктор
                     CSymbolsCollection();
   
  };
//+------------------------------------------------------------------+

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

Допишем все остальные требующиеся переменные и методы для работы класса-коллекции символов:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   CListObj          m_list_all_symbols;     // Список всех объектов-символов
   ENUM_SYMBOLS_MODE m_mode_list;            // Режим работы со списками символов
   int               m_delta_symbol;         // Разница в количестве символов по сравнению с прошлой проверкой
   int               m_last_num_symbol;      // Количество символов в окне "Обзор рынка" на прошлой проверке
   int               m_global_error;         // Код глобальной ошибки
//--- Возвращает флаг наличия объекта-символа по его имени в списке всех символов
   bool              IsPresentSymbolInList(const string symbol_name);
//--- Создаёт объект-символ и помещает его в список
   bool              CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name);
//--- Возвращает тип используемого списка символов (Обзор рынка/Сервер)
   ENUM_SYMBOLS_MODE TypeSymbolsList(const string &symbol_used_array[]);

//--- Определяет по имени и возвращает принадлежность символа к группе
   ENUM_SYMBOL_STATUS SymbolStatus(const string symbol_name)      const;
//--- Возвращает принадлежность символа к категории по пользовательским критериям
   ENUM_SYMBOL_STATUS StatusByCustomPredefined(const string symbol_name)  const;
//--- Возвращает принадлежность символа к категориям по расчёту залоговых средств
   ENUM_SYMBOL_STATUS StatusByCalcMode(const string symbol_name)  const;
//--- Возвращает принадлежность символа к предопределённым (1) мажорам, (2) минорам, (3) экзотикам, (4) рублёвым,
//--- (5) индикативам, (6) металлам, (7) товарам, (8) индексам, (9) криптовалюте, (10) опционам
   bool              IsPredefinedFXMajor(const string name)       const;
   bool              IsPredefinedFXMinor(const string name)       const;
   bool              IsPredefinedFXExotic(const string name)      const;
   bool              IsPredefinedFXRUB(const string name)         const;
   bool              IsPredefinedIndicative(const string name)    const;
   bool              IsPredefinedMetall(const string name)        const;
   bool              IsPredefinedCommodity(const string name)     const;
   bool              IsPredefinedIndex(const string name)         const;
   bool              IsPredefinedCrypto(const string name)        const;
   bool              IsPredefinedOption(const string name)        const;

//--- Ищет символ и возвращает флаг его наличия на сервере
   bool              Exist(const string name)                     const;
   
public:
//--- Возвращает полный список-коллекцию "как есть"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
//--- Возвращает количество новых символов в окне "Обзор рынка"
   int               NewSymbols(void)    const                                                              { return this.m_delta_symbol;                                           }
//--- Возвращает режим работы со списками символов
   ENUM_SYMBOLS_MODE ModeSymbolsList(void)                        const                                     { return this.m_mode_list;                                              }
//--- Конструктор
                     CSymbolsCollection();
   
//--- Устанавливает список используемых символов
   bool              SetUsedSymbol(const string &symbol_used_array[]);
//--- Обновляет (1) все, (2) котировочные данные символов коллекции
   void              Refresh(void);
   void              RefreshRates(void);
  };
//+------------------------------------------------------------------+

Класс должен знать с каким списком символов ему работать: либо это один текущий символ, либо это заданный пользователем набор символов, либо работа со списком символов, находящихся в окне "Обзор рынка", или же работа с полным списком всех символов на сервере. Переменная-член класса m_mode_list хранит один из перечисленных режимов работы с символами.
При работе со списком символов, находящихся в окне "Обзор рынка", необходимо постоянно отслеживать этот список (будет реализовано в следующей статье) и знать количество символов на прошлой проверке, и соответственно — на сколько изменилось это количество при событии добавления/удаления символа/символов из списка в окне обзора рынка, чтобы вовремя перестроить список коллекции символов m_list_all_symbols и продолжить безошибочно с ними работать.
Как и в прошлых коллекциях, мы ввели новую переменную-член класса, хранящую код ошибки, которую можно "поглядеть" в базовом классе библиотеки CEngine, и вовремя и правильно её обработать.
При создании нового объекта-символа и добавления его в коллекцию, необходимо проверить, что точно такого же символа нет в списке. Для этого служит метод IsPresentSymbolInList().
Для создания нового символа и добавления его в коллекцию, будет использоваться метод CreateNewSymbol().
Так как в коллекции символов организована работа в четырёх режимах списков символов, то для определения того, с каким режимом необходимо работать, объявлен метод TypeSymbolsList().
Перед созданием нового символа, сначала нужно определить и установить группу, к которой его отнести, или к которой его относит пользователь. Для определения группы символа (его статуса) предназначен метод SymbolStatus().
При определении статуса символа, его имя сначала ищется в заданных пользовательских массивах с использованием метода StatusByCustomPredefined().
Затем, если метод вернул "общий" статус, то статус символа определяется по типу расчёта залоговых средств методом StatusByCalcMode().
Для поиска по пользовательским массивам и определения принадлежности символа к той, или иной категории, назначенной пользователем, предназначены вспомогательные методы, возвращающие флаг принадлежности символа к мажорам, минорам, индексам и прочим группам.
Метод Exist() возвращает флаг существования символа на сервере.
Метод NewSymbols() нужен будет для возврата количества новых символов, добавленных или удалённых из окна "Обзор рынка", а метод ModeSymbolsList() возвращает в вызывающую программу режим работы с одним из четырёх списков (текущий символ, предопределённый набор символов, список из обзора рынка и полный список символов на сервере).
Метод SetUsedSymbol() принимает массив символов или описание режима работы со списком символов внутри переданного массива и создаёт список-коллекцию символов.
Метод Refresh() обновляет все данные всех символов коллекции, которые могут измениться, а метод RefreshRates() обновляет только котировочные данные всех символов коллекции. Оба метода должны вызываться из таймера главного объекта библиотеки CEngine.

Итак, все методы, необходимые для работы класса коллекции символов на данном этапе, мы определили. Теперь рассмотрим их устройство.

Реализация конструктора класса:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CSymbolsCollection::CSymbolsCollection(void) : m_last_num_symbol(0),m_delta_symbol(0),m_mode_list(SYMBOLS_MODE_CURRENT)
  {
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_NAME);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Type(COLLECTION_SYMBOLS_ID);
  }
//+------------------------------------------------------------------+

В списке инициализации конструктора инициализируем количество символов на прошлой проверке, разницу между текущим и прошлым количеством символов и установим режим работы со списком символов как "работа с текущим символом".
В теле класса зададим сортировку списку коллекции символов по имени, очистим список и присвоим ему идентификатор "список коллекции символов".

Метод обновления всех данных всех символов коллекции:

//+------------------------------------------------------------------+
//| Обновляет все данные символов коллекции                          |
//+------------------------------------------------------------------+
void CSymbolsCollection::Refresh(void)
  {
   int total=this.m_list_all_symbols.Total();
   if(total==0)
      return;
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      symbol.Refresh();
     }
  }
//+------------------------------------------------------------------+

В цикле по количеству всех символов коллекции получаем очередной символ из списка коллекции и обновляем его данные при помощи метода Refresh() класса CSymbol, рассмотренного нами в прошлой статье.

Метод обновления котировочных данных всех символов коллекции:

//+------------------------------------------------------------------+
//| Обновляет котировочные данные символов коллекции                 |
//+------------------------------------------------------------------+
void CSymbolsCollection::RefreshRates(void)
  {
   int total=this.m_list_all_symbols.Total();
   if(total==0)
      return;
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      symbol.RefreshRates();
     }
  }
//+------------------------------------------------------------------+

В цикле по количеству всех символов коллекции получаем очередной символ из списка коллекции и обновляем его данные при помощи метода RefreshRates() класса CSymbol.

Метод создания нового объекта-символа и размещения его в списке коллекции символов:

//+------------------------------------------------------------------+
//| Создаёт объект-символ и помещает его в список                    |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name)
  {
   if(this.IsPresentSymbolInList(name))
     {
      return true;
     }
   if(#ifdef __MQL5__ !::SymbolInfoInteger(name,SYMBOL_EXIST) #else !this.Exist(name) #endif )
     {
      string t1=TextByLanguage("Ошибка входных данных: нет символа ","Input error: no ");
      string t2=TextByLanguage(" на сервере"," symbol on the server");
      ::Print(DFUN,t1,name,t2);
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
      return false;
     }
   CSymbol *symbol=NULL;
   switch(symbol_status)
     {
      case SYMBOL_STATUS_FX         :  symbol=new CSymbolFX(name);         break;   // Форекс символ
      case SYMBOL_STATUS_FX_MAJOR   :  symbol=new CSymbolFXMajor(name);    break;   // Форекс символ-мажор
      case SYMBOL_STATUS_FX_MINOR   :  symbol=new CSymbolFXMinor(name);    break;   // Форекс символ-минор
      case SYMBOL_STATUS_FX_EXOTIC  :  symbol=new CSymbolFXExotic(name);   break;   // Форекс символ-экзотик
      case SYMBOL_STATUS_FX_RUB     :  symbol=new CSymbolFXRub(name);      break;   // Форекс символ/рубль
      case SYMBOL_STATUS_METAL      :  symbol=new CSymbolMetall(name);     break;   // Металл
      case SYMBOL_STATUS_INDEX      :  symbol=new CSymbolIndex(name);      break;   // Индекс
      case SYMBOL_STATUS_INDICATIVE :  symbol=new CSymbolIndicative(name); break;   // Индикатив
      case SYMBOL_STATUS_CRYPTO     :  symbol=new CSymbolCrypto(name);     break;   // Криптовалютный символ
      case SYMBOL_STATUS_COMMODITY  :  symbol=new CSymbolCommodity(name);  break;   // Товарный символ
      case SYMBOL_STATUS_EXCHANGE   :  symbol=new CSymbolExchange(name);   break;   // Биржевой символ
      case SYMBOL_STATUS_FUTURES    :  symbol=new CSymbolFutures(name);    break;   // Фьючерс
      case SYMBOL_STATUS_CFD        :  symbol=new CSymbolCFD(name);        break;   // Контракт на разницу
      case SYMBOL_STATUS_STOCKS     :  symbol=new CSymbolStocks(name);     break;   // Ценная бумага
      case SYMBOL_STATUS_BONDS      :  symbol=new CSymbolBonds(name);      break;   // Облигация
      case SYMBOL_STATUS_OPTION     :  symbol=new CSymbolOption(name);     break;   // Опцион
      case SYMBOL_STATUS_COLLATERAL :  symbol=new CSymbolCollateral(name); break;   // Неторгуемый актив
      case SYMBOL_STATUS_CUSTOM     :  symbol=new CSymbolCustom(name);     break;   // Пользовательский символ
      default                       :  symbol=new CSymbolCommon(name);     break;   // Остальное
     }
   if(symbol==NULL)
     {
      ::Print(DFUN,TextByLanguage("Не удалось создать объект-символ ","Failed to create symbol object "),name);
      return false;
     }
   if(!this.m_list_all_symbols.Add(symbol))
     {
      string t1=TextByLanguage("Не удалось добавить символ ","Failed to add ");
      string t2=TextByLanguage(" в список"," symbol to the list");
      ::Print(DFUN,t1,name,t2);
      delete symbol;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

В метод передаётся статус и наименование символа, если такой символ уже есть в списке коллекции символов, то ничего не делая, "молча" возвращаем true — ошибки нет, но и символ не нужно добавлять — есть такой уже.
Далее проверяем существование символа на сервере по его имени, и если такого символа нет, то выводим сообщение об отсутствии такого символа, присваиваем коду ошибки значение "неизвестный символ" и возвращаем false.
Если символ существует, то в зависимости от статуса, переданного в метод, создаём новый объект-символ. Для создания объекта-символа используем классы-наследники абстрактного символа, соответствующие переданному статусу. При ошибке создания объекта выводим об этом сообщение и возвращаем false.
При удачном создании нового объекта-символа, добавляем его в список коллекции символов, и возвращаем true при успешном добавлении, или выводим сообщение об ошибке и возвращаем falseпри неудаче.

Реализация метода, возвращающего флаг наличия символа в списке коллекции:

//+------------------------------------------------------------------+
//| Возвращает флаг наличия объекта-символа                          |
//| по его имени в списке всех символов                              |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPresentSymbolInList(const string symbol_name)
  {
   CArrayObj *list=dynamic_cast<CListObj*>(&this.m_list_all_symbols);
   list.Sort(SORT_BY_SYMBOL_NAME);
   list=CSelect::BySymbolProperty(list,SYMBOL_PROP_NAME,symbol_name,EQUAL);
   return(list==NULL || list.Total()==0 ? false : true);
  }
//+------------------------------------------------------------------+

В метод передаётся имя искомого символа, затем список сортируется по имени символа и фильтруется по переданному имени. Если список не пустой — значит символ с таким именем найден в списке — возвращаем true, иначе — возвращаем false — нет символа в списке.

Реализация метода, устанавливающего список символов коллекции:

//+------------------------------------------------------------------+
//| Устанавливает список используемых символов                       |
//+------------------------------------------------------------------+
bool CSymbolsCollection::SetUsedSymbol(const string &symbol_used_array[])
  {
   this.m_mode_list=this.TypeSymbolsList(symbol_used_array);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_NAME);
   //--- Используется только текущий символ
   if(this.m_mode_list==SYMBOLS_MODE_CURRENT)
     {
      string name=::Symbol();
      ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
      return this.CreateNewSymbol(status,name);
     }
   else
     {
      bool res=true;
      //--- Используется предопределённый список символов
      if(this.m_mode_list==SYMBOLS_MODE_DEFINES)
        {
         int total=::ArraySize(symbol_used_array);
         for(int i=0;i<total;i++)
           {
            string name=symbol_used_array[i];
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name);
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
      //--- Используется полный список символов сервера
      else if(this.m_mode_list==SYMBOLS_MODE_ALL)
        {
         int total=::SymbolsTotal(false);
         for(int i=0;i<total;i++)
           {
            string name=::SymbolName(i,false);
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name);
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
      //--- Используется список символов из окна "Обзор рынка"
      else if(this.m_mode_list==SYMBOLS_MODE_MARKET_WATCH)
        {
         int total=::SymbolsTotal(true);
         for(int i=0;i<total;i++)
           {
            string name=::SymbolName(i,true);
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name);
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
     }
   return false;
  }
//+------------------------------------------------------------------+

Метод создаёт список-коллекцию символов в зависимости от содержания массива символов, переданного в него. Сначала определяется метод работы со списком символов (один символ/пользовательский набор символов/обзор рынка/полный список), затем список очищается и сортируется по имени.
Если используется только текущий символ, то

  • определяем статус символа по его имени и
  • возвращаем результат создания нового объекта-символа и добавления его в список коллекции символов.

Если используется предопределённый список символов, то

  • в цикле по переданному в метод массиву имён символов получаем очередное имя символа из массива
  • определяем статус символа по его имени
  • добавляем к возвращаемой переменной результат создания и добавления объекта-символа в список-коллекцию символов
  • если символ не удалось создать или добавить в список — переходим к следующему
  • по завершении цикла возвращаем состояние переменной, в которую записывались результаты создания символов коллекции

Остальные два режима обрабатываются точно так же, как и режим работы с предопределённым списком. Только вместо массива, передаваемого в метод, символы берём либо из окна обзора рынка, либо из полного списка символов на сервере.

Реализация метода, возвращающего режим работы со списками символов:

//+------------------------------------------------------------------+
//|Возвращает тип используемого списка символов (Обзор рынка/Сервер) |
//+------------------------------------------------------------------+
ENUM_SYMBOLS_MODE CSymbolsCollection::TypeSymbolsList(const string &symbol_used_array[])
  {
   int total=::ArraySize(symbol_used_array);
   if(total<1)
      return SYMBOLS_MODE_CURRENT;
   string type=::StringSubstr(symbol_used_array[0],13);
   return
     (
      type=="MARKET_WATCH" ? SYMBOLS_MODE_MARKET_WATCH   :
      type=="ALL"          ? SYMBOLS_MODE_ALL            :
      (total==1 && symbol_used_array[0]==::Symbol() ? SYMBOLS_MODE_CURRENT : SYMBOLS_MODE_DEFINES)
     );
  }
//+------------------------------------------------------------------+

В метод передаётся массив с именами символов, либо с описанием режимов работы со списками.
Если передан пустой массив, то возвращаем режим работы только с текущим символом
.
Далее получаем из нулевой ячейки массива её содержимое, и

  • если в подстроке, начиная с индекса 13, содержится запись "MARKET_WATCH", то возвращаем режим работы с окном "Обзор рынка"
  • Если там записана строка "ALL" — возвращаем режим работы с полным списком символов.
  • Иначе, 
    • если в массиве всего одна запись, и она содержит имя текущего символа, то возвращаем режим работы только с текущим символом.
    • Ну и в последнем из возможных вариантов — возвращаем режим работы с предопределённым списком.

Реализация метода, возвращающего принадлежность символа к группе по его имени:

//+------------------------------------------------------------------+
//| Определяет по имени и возвращает принадлежность символа к группе |
//+------------------------------------------------------------------+
ENUM_SYMBOL_STATUS CSymbolsCollection::SymbolStatus(const string symbol_name) const
  {
   ENUM_SYMBOL_STATUS status=this.StatusByCustomPredefined(symbol_name);
   return(status==SYMBOL_STATUS_COMMON ? this.StatusByCalcMode(symbol_name) : status);
  }
//+------------------------------------------------------------------+

В метод передаётся имя символа. Затем проверяется его принадлежность к заданным пользовательским группам. Если в итоге возвращён статус "общая группа", то ищем группу по свойству символа "режим расчёта залоговых средств". В итоге возвращаем полученный статус. Он, к слову, может быть приравнен к одной из групп по режиму расчёта прибыли и маржи, а может так и остаться в общей группе символов.
В итоге получается, что символ будет иметь статус пользовательской группы, либо — если в пользовательских группах его не оказалось, то ему может быть присвоена группа по способу расчёта залоговых средств, либо — если и в этой группе его определить не удалось, то символ останется в общей группе.

Реализация методов, возвращающих флаг принадлежности символа к определённым пользовательским группам:

//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к мажорам                      |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXMajor(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXMajors);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXMajors[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к минорам                      |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXMinor(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXMinors);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXMinors[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к экзотикам                    |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXExotic(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXExotics);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXExotics[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к рублёвым                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXRUB(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXRub);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXRub[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к индикативам                  |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedIndicative(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXIndicatives);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXIndicatives[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к металлам                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedMetall(const string name) const
  {
   int total=::ArraySize(DataSymbolsMetalls);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsMetalls[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к товарам                      |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedCommodity(const string name) const
  {
   int total=::ArraySize(DataSymbolsCommodities);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsCommodities[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к индексам                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedIndex(const string name) const
  {
   int total=::ArraySize(DataSymbolsIndexes);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsIndexes[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к криптовалюте                 |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedCrypto(const string name) const
  {
   int total=::ArraySize(DataSymbolsCrypto);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsCrypto[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает принадлежность символа к опционам                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedOption(const string name) const
  {
   int total=::ArraySize(DataSymbolsOptions);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsOptions[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+

В зависимости от наименования метода (от проверяемой пользовательской группы), в методах просто ищется в пользовательском массиве, соответствующем его группе, наличие символа, имя которого передано в метод. Если такой символ найден в массиве, то возвращается true. Иначе — false.

Метод, возвращающий статус символа по наличию его в пользовательских группах:

//+------------------------------------------------------------------+
//| Возвращает категорию по пользовательским критериям               |
//+------------------------------------------------------------------+
ENUM_SYMBOL_STATUS CSymbolsCollection::StatusByCustomPredefined(const string symbol_name) const
  {
   return
     (
      this.IsPredefinedFXMajor(symbol_name)     ?  SYMBOL_STATUS_FX_MAJOR     :
      this.IsPredefinedFXMinor(symbol_name)     ?  SYMBOL_STATUS_FX_MINOR     :
      this.IsPredefinedFXExotic(symbol_name)    ?  SYMBOL_STATUS_FX_EXOTIC    :
      this.IsPredefinedFXRUB(symbol_name)       ?  SYMBOL_STATUS_FX_RUB       :
      this.IsPredefinedOption(symbol_name)      ?  SYMBOL_STATUS_OPTION       :
      this.IsPredefinedCommodity(symbol_name)   ?  SYMBOL_STATUS_COMMODITY    :
      this.IsPredefinedCrypto(symbol_name)      ?  SYMBOL_STATUS_CRYPTO       :
      this.IsPredefinedMetall(symbol_name)      ?  SYMBOL_STATUS_METAL        :
      this.IsPredefinedIndex(symbol_name)       ?  SYMBOL_STATUS_INDEX        :
      this.IsPredefinedIndicative(symbol_name)  ?  SYMBOL_STATUS_INDICATIVE   :
      SYMBOL_STATUS_COMMON
     );
  }  
//+------------------------------------------------------------------+

В метод передаётся имя символа и поочерёдно проверяется его наличие в каждой из пользовательских групп при помощи вышерассмотренных методов. Как только символ встречается в любой из групп — возвращается статус, соответствующий той группе, где символ найден. Если ни в одной из групп символ найден не был, то возвращается статус "общая группа символов".

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

//+------------------------------------------------------------------+
//|Возвращает принадлежность к категории по расчёту залоговых средств|
//+------------------------------------------------------------------+
ENUM_SYMBOL_STATUS CSymbolsCollection::StatusByCalcMode(const string symbol_name) const
  {
   ENUM_SYMBOL_CALC_MODE calc_mode=(ENUM_SYMBOL_CALC_MODE)::SymbolInfoInteger(symbol_name,SYMBOL_TRADE_CALC_MODE);
   return
     (
      calc_mode==SYMBOL_CALC_MODE_EXCH_OPTIONS_MARGIN                                                                               ?  SYMBOL_STATUS_OPTION       :
      calc_mode==SYMBOL_CALC_MODE_SERV_COLLATERAL                                                                                   ?  SYMBOL_STATUS_COLLATERAL   :
      calc_mode==SYMBOL_CALC_MODE_FUTURES                                                                                           ?  SYMBOL_STATUS_FUTURES      :
      calc_mode==SYMBOL_CALC_MODE_CFD           || calc_mode==SYMBOL_CALC_MODE_CFDINDEX || calc_mode==SYMBOL_CALC_MODE_CFDLEVERAGE  ?  SYMBOL_STATUS_CFD          :
      calc_mode==SYMBOL_CALC_MODE_FOREX         || calc_mode==SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE                                    ?  SYMBOL_STATUS_FX           :
      calc_mode==SYMBOL_CALC_MODE_EXCH_STOCKS   || calc_mode==SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX                                     ?  SYMBOL_STATUS_STOCKS       :
      calc_mode==SYMBOL_CALC_MODE_EXCH_BONDS    || calc_mode==SYMBOL_CALC_MODE_EXCH_BONDS_MOEX                                      ?  SYMBOL_STATUS_BONDS        :
      calc_mode==SYMBOL_CALC_MODE_EXCH_FUTURES  || calc_mode==SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS                                   ?  SYMBOL_STATUS_FUTURES      :
      SYMBOL_STATUS_COMMON
     );
  }
//+------------------------------------------------------------------+

В метод передаётся имя символа, затем получаем для этого символа способ расчёта величины залоговых средств, и далее, в зависимости от полученного значения возвращаем статус символа. Если ни один из сбособов расчёта не был идентифицирован, то возвращаем статус "общая группа символов".

Метод, ищущий символ на сервере, и возвращающий флаг его наличия:

//+------------------------------------------------------------------+
//| Ищет символ и возвращает флаг его наличия на сервере             |
//+------------------------------------------------------------------+
bool CSymbolsCollection::Exist(const string name) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+

Класс коллекции символов готов. Теперь нам нужно запустить его в работу. И, как говорится, "по традиции" — у нас всё начинается с класса CEngine, запускается и управляется из него же. Внесём в него необходимые правки.

Подключим к файлу класса CEngine файл класса коллекции символов, и объявим объект-коллекцию символов и необходимые для работы методы:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CArrayObj            m_list_counters;                 // Список счётчиков таймера
   int                  m_global_error;                  // Код глобальной ошибки
   bool                 m_first_start;                   // Флаг первого запуска
   bool                 m_is_hedge;                      // Флаг хедж-счёта
   bool                 m_is_tester;                     // Флаг работы в тестере
   bool                 m_is_market_trade_event;         // Флаг торгового события на счёте
   bool                 m_is_history_trade_event;        // Флаг торгового события в истории счёта
   bool                 m_is_account_event;              // Флаг события изменения аккаунта
   ENUM_TRADE_EVENT     m_last_trade_event;              // Последнее торговое событие на счёте
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Последнее событие в свойствах счёта
//--- Возвращает индекс счётчика по id
   int                  CounterIndex(const int id) const;
//--- Возвращает (1) флаг первого запуска, (2) факт наличия флага в торговом событии
   bool                 IsFirstStart(void);
//--- Работа с событиями (1) ордеров, сделок и позиций, (2) аккаунтов
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
//--- (1) Работа с коллекцией символов и (2) событиями списка символов в окне обзора рынка
   void                 SymbolEventsControl(void);
   void                 MarketWatchEventsControl(void);
//--- Возвращает последний (1) рыночный отложенный ордер, (2) маркет-ордер, (3) последнюю позицию, (4) позицию по тикету
   COrder              *GetLastMarketPending(void);
   COrder              *GetLastMarketOrder(void);
   COrder              *GetLastPosition(void);
   COrder              *GetPosition(const ulong ticket);
//--- Возвращает последний (1) удалённый отложенный ордер, (2) исторический маркет-ордер, (3) исторический ордер (маркет или отложенный) по его тикету
   COrder              *GetLastHistoryPending(void);
   COrder              *GetLastHistoryOrder(void);
   COrder              *GetHistoryOrder(const ulong ticket);
//--- Возвращает (1) первый и (2) последний исторический маркет-ордер из списка всех ордеров позиции, (3) последнюю сделку
   COrder              *GetFirstOrderPosition(const ulong position_id);
   COrder              *GetLastOrderPosition(const ulong position_id);
   COrder              *GetLastDeal(void);
public:
   //--- Возвращает список рыночных (1) позиций, (2) отложенных ордеров и (3) маркет-ордеров
   CArrayObj           *GetListMarketPosition(void);
   CArrayObj           *GetListMarketPendings(void);
   CArrayObj           *GetListMarketOrders(void);
   //--- Возвращает список исторических (1) ордеров, (2) удалённых отложенных ордеров, (3) сделок, (4) всех маркет-ордеров позиции по её идентификатору
   CArrayObj           *GetListHistoryOrders(void);
   CArrayObj           *GetListHistoryPendings(void);
   CArrayObj           *GetListDeals(void);
   CArrayObj           *GetListAllOrdersByPosID(const ulong position_id);
//--- Возвращает список (1) аккаунтов, (2) событий аккаунтов, (3) событие изменения аккаунта по его индексу в списке
//--- (4) текущий аккаунт, (5) описание события
   CArrayObj           *GetListAllAccounts(void)                        { return this.m_accounts.GetList();                   }
   CArrayInt           *GetListAccountEvents(void)                      { return this.m_accounts.GetListChanges();            }
   ENUM_ACCOUNT_EVENT   GetAccountEventByIndex(const int index)         { return this.m_accounts.GetEvent(index);             }
   CAccount            *GetAccountCurrent(void);
   string               GetAccountEventDescription(ENUM_ACCOUNT_EVENT event);
//--- Возвращает список используемых символов
   CArrayObj           *GetListAllUsedSymbols(void)                     { return this.m_symbols.GetList();                    }
   
//--- Возвращает список событий ордеров, сделок и позиций
   CArrayObj           *GetListAllOrdersEvents(void)                    { return this.m_events.GetList();                     }
//--- Сбрасывает последнее торговое событие
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Возвращает (1) последнее торговое событие, (2) последнее событие в свойствах счёта, (3) флаг счёта-хедж, (4) флаг работы в тестере
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;                     }
   ENUM_ACCOUNT_EVENT   LastAccountEvent(void)                    const { return this.m_last_account_event;                   }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;                             }
   bool                 IsTester(void)                            const { return this.m_is_tester;                            }
   bool                 IsAccountsEvent(void)                     const { return this.m_accounts.IsAccountEvent();            }
//--- Возвращает код события аккаунта
   int                  GetAccountEventsCode(void)                const { return this.m_accounts.GetEventCode();              }
//--- Возвращает код глобальной ошибки CEngine
   int                  GetError(void)                            const { return this.m_global_error;                         }
//--- Создаёт счётчик таймера
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Таймер
   void                 OnTimer(void);
//--- Устанавливает список используемых символов
   bool                 SetUsedSymbols(const string &array_symbols[])   { return this.m_symbols.SetUsedSymbol(array_symbols); }
//--- Конструктор/Деструктор
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

В методе SymbolEventsControl() будем осуществлять обновление котировочных данных всех символов коллекции, а в методе MarketWatchEventsControl() будем обновлять остальные данные всех символов коллекции и отслеживать события в окне "Обзор рынка" (в классе событий коллекции символов в следующей статье).
Метод GetListAllUsedSymbols() возвращает в вызывающую программу полный список коллекции символов при помощи метода GetList() класса CSymbolsCollection.
Метод SetUsedSymbols() вызывает одноимённый метод SetUsedSymbol() класса CSymbolsCollection, который в свою очередь заполняет список коллекции объектами-символами всех используемых символов в программе.

Рассмотрим устройство этих методов.

В конструкторе класса создадим счётчики первого и второго таймеров коллекции символов. В первом таймере будем обновлять котировочные данные всех символов коллекции, а во втором — остальные данные символов и управлять отслеживанием событий окна "Обзор рынка".

//+------------------------------------------------------------------+
//| CEngine конструктор                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT),m_last_account_event(ACCOUNT_EVENT_NO_EVENT),m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
  }
//+------------------------------------------------------------------+

В обработчик OnTimer() класса впишем строки работы с двумя таймерами коллекции символов:

//+------------------------------------------------------------------+
//| CEngine таймер                                                   |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Таймер коллекций исторических ордеров и сделок и рыночных ордеров и позиций
   int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась - работаем с событиями коллекций ордеров, сделок и позиций
            if(counter.IsTimeDone())
               this.TradeEventsControl();
           }
         //--- Если тестер - работаем с событиями коллекций по тику
         else
            this.TradeEventsControl();
        }
     }
//--- Таймер коллекции аккаунтов
   index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась - работаем с событиями коллекции аккаунтов
            if(counter.IsTimeDone())
               this.AccountEventsControl();
           }
         //--- Если тестер - работаем с событиями коллекций по тику
         else
            this.AccountEventsControl();
        }
     }
     
//--- Таймер1 коллекции символов (обновление котировочных данных символов в коллекции)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась - обновляем котировочные данные всех символов в коллекции
            if(counter.IsTimeDone())
               this.SymbolEventsControl();
           }
         //--- Если тестер - обновляем котировочные данные всех символов в коллекции по тику
         else
            this.SymbolEventsControl();
        }
     }
//--- Таймер2 коллекции символов (обновление всех данных всех символов в коллекции и отслеживание событий списка символов в окне обзора рынка)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась
            if(counter.IsTimeDone())
              {
               //--- обновляем данные всех символов в коллекции
               this.m_symbols.Refresh();
               //--- Если работаем со списком из обзора рынка - проверяем события окна обзора рынка
               if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
                  this.MarketWatchEventsControl();
              }
           }
         //--- Если тестер - обновляем данные всех символов в коллекции по тику
         else
            this.m_symbols.Refresh();
        }
     }
  }
//+------------------------------------------------------------------+

Здесь вся логика расписана в комментариях к коду, и останавливаться на её повторном разборе, думаю, не имеет смысла.

Метод, работающий с коллекцией символов, в данной реализации просто обновляет котировочные данные всех символов коллекции посредством метода RefreshRates класса CSymbolsCollection:

//+------------------------------------------------------------------+
//| Работа с коллекцией символов                                     |
//+------------------------------------------------------------------+
void CEngine::SymbolEventsControl(void)
  {
   this.m_symbols.RefreshRates();
  }
//+------------------------------------------------------------------+

Метод работы с событиями окна "Обзор рынка" в данной реализиции ничего не делает, лишь проверяет, что вызван из тестера, и выходит в случае работы в тестере (в тестере нет смысла отслеживать события окна обзора рынка):

//+------------------------------------------------------------------+
//| Работа с событиями списка символов в окне обзора рынка           |
//+------------------------------------------------------------------+
void CEngine::MarketWatchEventsControl(void)
  {
   if(this.IsTester())
      return;
//--- Отслеживание событий окна "Обзор рынка"

//---
  }
//+------------------------------------------------------------------+

Всю работу с событиями коллекции символов сделаем в следующей статье.

Это все необходимые изменения класса CEngine.

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

Откроем файл сервисных функций DELib.mqh из папки \MQL5\Include\DoEasy\Services\ и впишем в него нужную функцию:

//+------------------------------------------------------------------+
//| Подготавливает массив символов для коллекции символов            |
//+------------------------------------------------------------------+
bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[])
  {
   //--- Если работа с текущим символом
   if(mode_used_symbols==SYMBOLS_MODE_CURRENT)
     {
      //--- Запишем в единственную ячейку массива имя текущего символа
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=Symbol();
      return true;
     }
   //--- Если работа с предопределённым набором символов (из строки defined_used_symbols)
   else if(mode_used_symbols==SYMBOLS_MODE_DEFINES)
     {
      //--- Установим разделитель - запятая
      string separator=",";
      //--- Заменим ошибочные разделители на верный
      if(StringFind(defined_used_symbols,";")>WRONG_VALUE)  StringReplace(defined_used_symbols,";",separator);   
      if(StringFind(defined_used_symbols,":")>WRONG_VALUE)  StringReplace(defined_used_symbols,":",separator); 
      if(StringFind(defined_used_symbols,"|")>WRONG_VALUE)  StringReplace(defined_used_symbols,"|",separator);   
      if(StringFind(defined_used_symbols,"/")>WRONG_VALUE)  StringReplace(defined_used_symbols,"/",separator); 
      if(StringFind(defined_used_symbols,"\\")>WRONG_VALUE) StringReplace(defined_used_symbols,"\\",separator);  
      if(StringFind(defined_used_symbols,"'")>WRONG_VALUE)  StringReplace(defined_used_symbols,"'",separator); 
      if(StringFind(defined_used_symbols,"-")>WRONG_VALUE)  StringReplace(defined_used_symbols,"-",separator);   
      if(StringFind(defined_used_symbols,"`")>WRONG_VALUE)  StringReplace(defined_used_symbols,"`",separator);
      //--- Пока есть пробелы - удаляем
      while(StringFind(defined_used_symbols," ")>WRONG_VALUE && !IsStopped()) 
         StringReplace(defined_used_symbols," ","");
      //--- Пока есть двойные разделители (после удаления между ними пробелов) - меняем на разделитель
      while(StringFind(defined_used_symbols,separator+separator)>WRONG_VALUE && !IsStopped())
         StringReplace(defined_used_symbols,separator+separator,separator);
      //--- Если остался один разделитель перед первым символом в строке - заменяем на пробел
      if(StringFind(defined_used_symbols,separator)==0) 
         StringSetCharacter(defined_used_symbols,0,32);
      //--- Если остался один разделитель после последнего символа в строке - заменяем на пробел
      if(StringFind(defined_used_symbols,separator)==StringLen(defined_used_symbols)-1)
         StringSetCharacter(defined_used_symbols,StringLen(defined_used_symbols)-1,32);
      //--- Убираем всё лишнее слева и справа
      #ifdef __MQL5__
         StringTrimLeft(defined_used_symbols);
         StringTrimRight(defined_used_symbols);
      //---  __MQL4__
      #else 
         defined_used_symbols=StringTrimLeft(defined_used_symbols);
         defined_used_symbols=StringTrimRight(defined_used_symbols);
      #endif 
      //--- Подготавливаем массив 
      ArrayResize(used_symbols_array,0);
      ResetLastError();
      //--- разделяем строку по разделителям (запятая) и вписываем все найденные подстроки в массив
      int n=StringSplit(defined_used_symbols,StringGetCharacter(separator,0),used_symbols_array);
      //--- если ничего не нашли - выведем об этом сообщение (при этом автоматически будет выбрана работа с текущим символом)
      if(n<1)
        {
         string err=
           (n==0  ?  
            DFUN_ERR_LINE+TextByLanguage("Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols is empty, the symbol will be used: ")+Symbol() :
            DFUN_ERR_LINE+TextByLanguage("Не удалось подготовить массив используемых символов. Ошибка ","Failed to create an array of used characters. Error ")+(string)GetLastError()
           );
         Print(err);
         return false;
        }
     }
   //--- Если работа с окном "Обзор рынка" или с полным списком
   else
     {
      //--- В единственную ячейку массива впишем наименование режима работы (mode_used_symbols)
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=EnumToString(mode_used_symbols);
     }
   return true;
  }
//+------------------------------------------------------------------+

В метод передаются режим работы с коллекцией символов, который должен задаваться либо в настройках программы, либо жёстко задаваться (если не требуется выбора режимов), строка с перечислением через запятую списка символов, с которыми необходимо работать (либо пустая строка) и массив, в который будут вписан список символов, либо режим работы (для режимов работы с обзором рынка и полным списком символов на сервере), и который будет отправляться в класс CEngine для установки режима работы библиотеки с символами.
Все действия, проводимые  функцией со списком и массивом подробно описаны прямо в листинге и отдельно их рассматривать не имеет смысла.

На этом разработка класса коллекции символов завершена, и у нас всё готово для его тестирования.

Тест коллекции символов

Для тестирования коллекции возьмём советник из прошлой статьи и сохраним его под новым именем \MQL5\Experts\TestDoEasy\ Part15\TestDoEasyPart15_1.mq5.

Сразу же во входных параметрах допишем выбор режима работы с коллекцией символов библиотеки и строковую переменную, хранящую список пользовательских символов, с которыми нужно работать в случае выбора в настройках такого режима:

//--- input variables
input ulong             InpMagic             =  123;  // Magic number
input double            InpLots              =  0.1;  // Lots
input uint              InpStopLoss          =  50;   // StopLoss in points
input uint              InpTakeProfit        =  50;   // TakeProfit in points
input uint              InpDistance          =  50;   // Pending orders distance (points)
input uint              InpDistanceSL        =  50;   // StopLimit orders distance (points)
input uint              InpSlippage          =  0;    // Slippage in points
input double            InpWithdrawal        =  10;   // Withdrawal funds (in tester)
input uint              InpButtShiftX        =  40;   // Buttons X shift 
input uint              InpButtShiftY        =  10;   // Buttons Y shift 
input uint              InpTrailingStop      =  50;   // Trailing Stop (points)
input uint              InpTrailingStep      =  20;   // Trailing Step (points)
input uint              InpTrailingStart     =  0;    // Trailing Start (points)
input uint              InpStopLossModify    =  20;   // StopLoss for modification (points)
input uint              InpTakeProfitModify  =  60;   // TakeProfit for modification (points)
input ENUM_SYMBOLS_MODE InpModeUsedSymbols   =  SYMBOLS_MODE_CURRENT;   // Mode of used symbols list
input string            InpUsedSymbols       =  "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)

//--- global variables

В список глобальных переменных добавим переменную для хранения пользовательского списка символов и строковый массив для передачи списка символов в библиотеку:

//--- global variables
CEngine        engine;
#ifdef __MQL5__
CTrade         trade;
#endif 
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
bool           trailing_on;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
string         used_symbols;
string         array_used_symbols[];
//+------------------------------------------------------------------+

В обработчике OnInit() советника присвоим пользовательский список переменной для его хранения, заполним массив используемых символов и отправим его в библиотеку:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Вызов данной функции выводит в журнал список констант перечисления, 
//--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант
   //EnumNumbersTest();

//--- Установка глобальных переменных советника
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   
//--- Заполнение массива используемых символов
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray(InpModeUsedSymbols,used_symbols,array_used_symbols);

//--- Установка типа используемого списка символов в коллекции символов
   engine.SetUsedSymbols(array_used_symbols);

//--- Проверка и удаление неудалённых графических объектов советника

Функция SetUsedSymbols(), рассмотренная нами выше, создаст массив символов для отправки его в класс коллекции символов. В зависимости от выбранного режима в массиве будет либо текущий символ, либо пользовательский список символов, либо строковое описание режима работы с окном "Обзор рынка" или с полным списком символов на сервере.

В самом конце обработчика OnInit() впишем код для быстрой проверки списков символов, создаваемых классом коллекции символов:

//--- Установка параметров торгового класса CTrade
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Быстрая проверка коллекции объектов-символов
   CArrayObj *list=engine.GetListAllUsedSymbols();
   CSymbol *symbol=NULL;
   if(list!=NULL)
     {
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol=list.At(i);
         if(symbol==NULL)
            continue;
         symbol.Refresh();
         symbol.RefreshRates();
         symbol.PrintShort();
         if(InpModeUsedSymbols<SYMBOLS_MODE_MARKET_WATCH)
            symbol.Print();
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Здесь: получаем полный список всех символов коллекции, в цикле по полученному списку получаем из него очередной символ, обновляем все данные и распечатываем в журнал описание символа — сначала краткое, а затем, если не выбран режим "Работа с окном обзора рынка" или "Работа с полным списком символов на сервере", то выводим в журнал полное описание свойств символа.

Запустим советник в окне терминала и выберем в настройках режим "Работа с символами из окна Обзор рынка". В результате в журнал будет распечатан список с краткими описаниями всех символов коллекции, созданный классом коллекции символов:

2019.06.27 10:01:52.756 Ценная бумага ALNU
2019.06.27 10:01:52.756 Ценная бумага SU25075RMFS1
2019.06.27 10:01:52.756 Облигация SU46022RMFS8
2019.06.27 10:01:52.756 Облигация SU26214RMFS5
2019.06.27 10:01:52.756 Ценная бумага AESL
2019.06.27 10:01:52.756 Ценная бумага 123456.bin
2019.06.27 10:01:52.756 Ценная бумага ARMD
2019.06.27 10:01:52.757 Облигация SU46018RMFS6
2019.06.27 10:01:52.757 Ценная бумага GAZP
2019.06.27 10:01:52.757 Металл XAUUSD
2019.06.27 10:01:52.757 Ценная бумага EURRUB_TOD
2019.06.27 10:01:52.757 Ценная бумага GBPRUB_TOM
2019.06.27 10:01:52.757 Фьючерс Si-9.19
2019.06.27 10:01:52.757 Фьючерс RTS-3.20
2019.06.27 10:01:52.758 Форекс символ-минор USDNOK
2019.06.27 10:01:52.758 Форекс символ-мажор USDJPY
2019.06.27 10:01:52.758 Форекс символ-мажор EURUSD
2019.06.27 10:01:52.758 Форекс символ-минор USDCZK
2019.06.27 10:01:52.758 Форекс символ-мажор USDCAD
2019.06.27 10:01:52.758 Форекс символ-минор USDZAR
2019.06.27 10:01:52.758 Форекс символ-минор USDSEK
2019.06.27 10:01:52.758 Форекс символ-мажор AUDUSD
2019.06.27 10:01:52.758 Форекс символ-минор USDDKK
2019.06.27 10:01:52.758 Форекс символ-мажор NZDUSD
2019.06.27 10:01:52.759 Форекс символ-минор USDPLN
2019.06.27 10:01:52.759 Форекс символ-мажор GBPUSD
2019.06.27 10:01:52.759 Форекс символ USDRUR
2019.06.27 10:01:52.759 Форекс символ-экзотик USDMXN
2019.06.27 10:01:52.759 Форекс символ USDHUF
2019.06.27 10:01:52.759 Форекс символ-минор USDTRY
2019.06.27 10:01:52.759 Форекс символ-минор USDHKD
2019.06.27 10:01:52.760 Форекс символ-мажор USDCHF
2019.06.27 10:01:52.760 Форекс символ-минор USDSGD

Теперь проверим поиск заданных значений в коллекции символов.
Переименуем советник и сохраним его под новым именем \MQL5\Experts\TestDoEasy\ Part15\TestDoEasyPart15_2.mq5.

Поменяем в обработчике OnInit() код быстрой проверки списка символов коллекции. Удалим распечатку символов в журнал, оставив лишь обновление данных символов коллекции, и допишем строки получения максимального и минимального свопа длинной и короткой позиций, и максимальный и минимальный спред символов в коллекции. Полученные данные выведем в журнал:

//--- Установка параметров торгового класса CTrade
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Быстрая проверка коллекции объектов-символов
   CArrayObj *list=engine.GetListAllUsedSymbols();
   CSymbol *symbol=NULL;
   if(list!=NULL)
     {
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol=list.At(i);
         if(symbol==NULL)
            continue;
         symbol.Refresh();     
         symbol.RefreshRates();
        }
     }
//--- Получение минимального и максимального значений
   //--- получим свойства текущего аккаунта (нам нужно количество знаков после запятой для валюты счёта)
   CAccount *account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      int index_min=0, index_max=0, dgc=(int)account.CurrencyDigits();
      //--- Если работа с окном "Обзор рынка", то оставим в списке только видимые символы
      if(InpModeUsedSymbols==SYMBOLS_MODE_MARKET_WATCH)
         list=CSelect::BySymbolProperty(list,SYMBOL_PROP_VISIBLE,true,EQUAL);
      
      //--- мин/макс своп длинных позиций
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SWAP_LONG);  // индекс символа в списке коллекции с минимальным значением свопа длинных позиций
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SWAP_LONG);  // индекс символа в списке коллекции с максимальным значением свопа длинных позиций
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Минимальный своп длинных позиций у символа ",symbol.Name()," = ",NormalizeDouble(symbol.SwapLong(),dgc));
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Максимальный своп длинных позиций у символа ",symbol.Name()," = ",NormalizeDouble(symbol.SwapLong(),dgc));
        }
      
      //--- мин/макс своп коротких позиций
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SWAP_SHORT); // индекс символа в списке коллекции с минимальным значением свопа коротких позиций
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SWAP_SHORT); // индекс символа в списке коллекции с максимальным значением свопа коротких позиций
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Минимальный своп коротких позиций у символа ",symbol.Name()," = ",NormalizeDouble(symbol.SwapShort(),dgc));
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Максимальный своп коротких позиций у символа ",symbol.Name()," = ",NormalizeDouble(symbol.SwapShort(),dgc));
        }
      
      //--- мин/макс спред
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SPREAD);     // индекс символа в списке коллекции с минимальным значением спреда
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SPREAD);     // индекс символа в списке коллекции с максимальным значением спреда
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Минимальный спред у символа ",symbol.Name()," = ",symbol.Spread());
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Максимальный спред у символа ",symbol.Name()," = ",symbol.Spread());
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Скомпилируем и запустим советник на графике терминала. Выберем в настройках работу с полным списком всех символов на сервере. После создания списка коллекции всех символов на сервере (займёт некоторое время), в журнал будет выведена информация по максимальному и минимальному свопу длинных и коротких позиций, и по максимальному и минимальному спреду из всех символов, имеющихся в списке коллекции символов:

2019.06.27 10:36:28.885 Минимальный своп длинных позиций у символа USDZAR = -192.9
2019.06.27 10:36:28.885 Максимальный своп длинных позиций у символа USDMXN = 432.7
2019.06.27 10:36:28.886 Минимальный своп коротких позиций у символа XAUUSD = -17.8
2019.06.27 10:36:28.886 Максимальный своп коротких позиций у символа USDMXN = 200.0
2019.06.27 10:36:28.886 Минимальный спред у символа SU52001RMFS3 = 0
2019.06.27 10:36:28.886 Максимальный спред у символа GBPRUB_TOM = 3975


Что дальше

В следующей статье разработаем класс событий коллекции символов.

Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.

К содержанию

Статьи этой серии:

Часть 1. Концепция, организация данных
Часть 2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть 7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть 8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 - Подготовка данных
Часть 10. Совместимость с MQL4 - События открытия позиций и активации отложенных ордеров
Часть 11. Совместимость с MQL4 - События закрытия позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть 14. Объект "Символ"
  


Прикрепленные файлы |
MQL5.zip (218.99 KB)
MQL4.zip (218.8 KB)
Управление оптимизацией (Часть 2): Создание ключевых объектов и логики приложения Управление оптимизацией (Часть 2): Создание ключевых объектов и логики приложения
Данная статья является продолжением предыдущей публикации на тему создания графического интерфейса для управления оптимизациями. В ней будет рассмотрена логика работы создаваемого дополнения. Создадим обертку для терминала MetaTrader 5 для его запуска как управляемый процесс через C#. А также будет рассмотрена работа с конфигурационными файлами и файлами настроек. Логика программы же будет поделена на две части: в первой описаны методы, вызываемые после нажатия на ту или иную клавишу, а вторая часть — запуск и управление оптимизациями.
Управление оптимизацией  (Часть I): Создание графического интерфейса Управление оптимизацией (Часть I): Создание графического интерфейса
В данной статье описывается процесс создания расширения для терминала MetaTrader. Предлагаемое решение помогает автоматизировать процесс оптимизации путем запуска оптимизаций в других терминалах. На базе данной статьи будет написано еще несколько статей, развивающих затронутую тему. Расширение написано с использованием языка C# и шаблонов программирования, что демонстрирует помимо основной задачи данной статьи возможность терминала к расширению изначально заложенных в него возможностей путем написания собственных модулей, а также то, как просто можно создавать пользовательскую графику в языке с наиболее удобным для этого функционалом.
Создаем кроссплатформенный советник-сеточник (Часть III): сетка на коррекциях с мартингейлом Создаем кроссплатформенный советник-сеточник (Часть III): сетка на коррекциях с мартингейлом
В этой статье мы попробуем создать лучший из возможных советников, работающих по принципу сеточника. Как обычно, это будет кроссплатформенный советник, способный работать как в MetaTrader 4, так и в MetaTrader 5. Первый советник был хорош всем, кроме того, что не мог принести прибыль на длительном промежутке времени. Второй советник мог работать на интервалах более нескольких лет. Но принести более 50% прибыли в год при максимальной просадке менее 50% он был не способен.
Почтовая рассылка сервисами Google Почтовая рассылка сервисами Google
Задача организации почтовой рассылки вполне может возникнуть у трейдера, поддерживающего деловые отношения с другими трейдерами, с подписчиками, клиентами, даже просто с друзьями. Разослать скриншоты, какие то журналы, логи, или отчеты, это вполне актуальные задачи, востребованные не каждый день, но и не так уж редко, в любом случае хотелось бы обладать такой возможностью. В статье рассмотрены вопросы использования сразу нескольких сервисов Google, написанию соответствующей сборки на C# и интеграции с инструментами на MQL.