
В библиотеке языка «MQL5» имеется класс «CList«. Он является классом динамического списка объектов. В иных языках программирования (например: Java, C#, ) такие списки называют коллекциями. Коллекции чем-то похожи на массивы, но с одним отличием. Массивы это набор однотипных данных, а коллекции могут содержать в себе набор значений как одного, так и различных типов.
Главное отличие списка «CList» от коллекции состоит в том, что он формируется из экземпляров класса «CObject» и/или его наследников. Количество таких объектов может исчисляться тысячами. Как тогда понять к какому типу относится тот или иной объект? Как отличить графический объект от списка отложенных заявок или от открытой заявки. А как отличить один графический объект (например: кнопка) от другого (например: поле ввода)?
Тип объекта
Если вы работали с другими языками программирования, то наверняка встречались с операторами «instanceof» или «typeof», которые помогали определить принадлежность объекта к тому или иному типу. В языке «MQL5» таких операторов нет. Поэтому мы напишем класс «ETypeCode», который возьмёт на себя эту функцию.
Для начала добавим новое перечисление. Для этого в конце файла «Enums.mqh» добавим:
//Все числовые значения этого перечисления должны быть отличны от 0 enum ENUM_OBJECT_TYPE_CODE { // Тип объекта "Узел" ENUM_OBJECT_NODE = 512, // Тип объекта "Список" ENUM_OBJECT_LIST = 1024, // Тип объекта "Графический элемент" ENUM_OBJECT_GRAPHIC = 1536, // Тип объекта "Заявка" ENUM_OBJECT_RECORD = 2048, // Тип объекта "Холст" ENUM_OBJECT_CANVAS = 2560, // Тип объекта "Событие" ENUM_OBJECT_EVENT = 3072, // Тип объекта "Время" ENUM_OBJECT_TIME = 3584 }
В этом перечислении представлены типы объектов, что будут использоваться в дальнейшем. Каждому объекту присваивается свой номер. Это определитель типа объекта.
Например все объекты, со значением определителя равным «1536», являются графическими. Все объекты, со значением определителя равным «3584», являются объектами времени. И так далее. Все эти типы являются общими.
Каждый тип объекта может иметь несколько подтипов, а точнее 512. Так все объекты, у которых значения определителя типа лежат в диапазоне от «1536» до «2048», относятся к типу «графические». А вот к какому подтипу относится объект (кнопка, или поле ввода, или чекбокс) зависит уже от программиста.
Вот пример перечисления с подтипами:
enum ENUM_OBJECT_TYPE_CODE { // Тип объекта"Узел" ENUM_OBJECT_NODE = 512, // Тип объекта"Список" ENUM_OBJECT_LIST = 1024, // Тип объекта "Список". Подтип "Открытые ордера" ENUM_OBJECT_LIST_OPEN = 1030, // Тип объекта "Список". Подтип "Закрытые ордера" ENUM_OBJECT_LIST_CLOSE = 1035, // Тип объекта "Список". Подтип "Отложенные ордера" ENUM_OBJECT_LIST_PENDING = 1040, // Тип объекта "Графический элемент" ENUM_OBJECT_GRAPHIC = 1536, // Тип объекта "Графический элемент". Подтип "Кнопка" ENUM_OBJECT_GRAPHIC_BUTTON = 1540, // Тип объекта "Графический элемент". Подтип "Кнопка BUY" ENUM_OBJECT_GRAPHIC_BUTTON_BUY = 1541, // Тип объекта "Графический элемент". Подтип "Кнопка SELL" ENUM_OBJECT_GRAPHIC_BUTTON_SELL = 1542, // Тип объекта "Графический элемент". Подтип "Поле ввода" ENUM_OBJECT_GRAPHIC_EDIT = 1550, // Тип объекта "Заявка" ENUM_OBJECT_RECORD = 2048, // Тип объекта "Холст" ENUM_OBJECT_CANVAS = 2560, // Тип объекта "Событие" ENUM_OBJECT_EVENT = 3072, // Тип объекта "Время" ENUM_OBJECT_TIME = 3584 }
Данное перечисление можно дополнять или менять как вам захочется. Теперь перейдём к классу «ETypeCode».
Класс «ETypeCode»
Для начала добавим папку «TypeCode» в наш проект. Там создадим файл «ETypeCode.mqh».

В нём прописываем следующее:
include "....\Helpers\Prepare.mqh" class ETypeCode { };
Как видите к данному файлу мы подключаем файл «Prepare.mqh«. Ранее он подключался к «ETime.mqh«. Теперь нам надо открыть файл»ETime.mqh». Подключить файл «ETypeCode.mqh» и добавить переменную «codeType», которая будет хранить тип объекта.
include "..\TypeCode\ETypeCode.mqh" class ETime { private: DataTime eTimeCustom; ulong codeType; bool Calculate(datetime tm=0); bool Init(datetime tm){ if(tm>0)Time(tm); if(eTimeCustom.time==tm)return(true); return(false); } public:ETime();
~ETime();
ulong getType()const{return (codeType);} void setType(ulong set){codeType = set;}
Затем добавляем методы «getType()» и «setType()». Теперь в начале конструктора класса «ETime» добавляем две строчки:
ETime::ETime() { //+--- // code - это экземпляр объекта "ECodeType". Он объявляется в файле "ECodeType.mqh" code.Init(ENUM_OBJECT_TIME); codeType = code.getType(); //+--- eTimeCustom.seconds = 0; eTimeCustom.minutes = 0; eTimeCustom.hours = 0; eTimeCustom.days = 0; eTimeCustom.weeks = 0; eTimeCustom.months = 0; eTimeCustom.years = 0; eTimeCustom.mqlDateTime.sec = 0; eTimeCustom.mqlDateTime.min = 0; eTimeCustom.mqlDateTime.hour = 0; eTimeCustom.mqlDateTime.day = 0; eTimeCustom.mqlDateTime.day_of_week = 0; eTimeCustom.mqlDateTime.day_of_year = 0; eTimeCustom.mqlDateTime.mon = 0; eTimeCustom.mqlDateTime.year = 0; eTimeCustom.offset.inSeconds = 0; eTimeCustom.offset.inMinutes = 0; eTimeCustom.offset.inHours = 0; eTimeCustom.lastDay = 0; eTimeCustom.lastHour = 0; eTimeCustom.lastMinute = 0; eTimeCustom.lastMonth = 0; eTimeCustom.lastWeek = 0; eTimeCustom.lastYear = 0; //+--- }
На этом изменения файла «ETime.mqh» закончены. Теперь возвращаемся к классу «ETypeCode». В нём объявляем следующие переменные и методы
class ETypeCode { private: string terminal; string program_type; string program_name; string object_type; public: ETypeCode(); ~ETypeCode(void); // Возвращает версию терминала (в виде числа). int getTerminalVersion() const{return toInt(terminal)/100;} // Сохраняет версию терминала в виде строки void setTerminalVersion(int set){ terminal = toString(set);} // // Возвращает тип работающей программы (в виде числа) int getProgramType() const{return toInt(program_type);} // Сохраняет значение типа работающей программы в виде строки void setProgramType(int set){ program_type = "0"+toString(set);} // Возвращает имя работающей программы (в виде числа) int getProgramName() const{return toInt(programm_name);} // Сохраняет значение имени работающей программы в строковом формате. void setProgramName(int set){ program_name = toString(set);} // Возвращает тип объекта (в виде числа) int getObjectType() const{return toInt(object_type);} // Сохраняет значение типа объекта в строковом формате. void setObjectType(ENUM_OBJECT_TYPE_CODE obType){ object_type = toString(toInt(obType));} // Генератор указателя типа объекта // Соединяет строки между собой, а затем переводит их в числовой формат. "000" - условный разделитель. Именно из-за его использования значение типа объекта ("object_type") должно отличаться от 0. ulong getType(){return toULong(terminal+program_name+"000"+object_type+program_type);} // Инициализация класса bool Init(ENUM_OBJECT_TYPE_CODE obType); // Преобразует имя работающей программы в числовое значение int integerName(); // Возвращает значение одной из четырёх переменных, в зависимости от значения переменной "mod" string toString(string mod); // Получает, в качестве аргумента, числовое значение указателя типа объекта и разбирает его на составные элементы bool Parse(ulong codeType); }; ETypeCode code;
При помощи объявления «ETypeCode code» выводим класс в глобальный доступ. Поскольку он занимается только генерацией определителя типа объекта, а полученный результат хранится в другом месте, то достаточно одного экземпляра класса «ETypeCode».
В переменной «terminal» класса «ETypeCode» храниться версия терминала. Значение «400» говорит о том, что объект сгенерирован терминалом MetaTrader 4, а значение «500» указывает на терминал MetaTrader 5. «000» указывает на то, что версия терминала не известна. При вызове метода «getTerminalVersion()» он вернёт числовое значение (4, 5 или 0).
Переменная «programm_type» используется для хранения типа программы (скрипт, эксперт или индикатор), которая сгенерировала данный объект. Значение «01» указывает на скрипт, «02» говорит о том, что это работа эксперта, а «03» обозначает индикатор. Метод » getProgramType()» вернёт значения 1,2 или 3.
В переменной «programm_name» храниться имя работающей программы. Имя предварительно переводится в число. Если надо узнать принадлежит ли объект текущей программе, то достаточно воспользоваться методом «integerName()».
Переменная «object_type» — хранит тип (подтип) объекта.
Все значения переменных представлены в виде строк. Это упрощает формирование указателя типа. Указатель создаётся при помощи конкатенации строк. За это отвечает метод «getType()».
Метод «Init()» присваивает значения четырём переменным, которые в последующем сформируют указатель типа. В файле «Defines.mqh» определены три константы. Две из них «PATH» и «TYPE» нам сейчас понадобятся. Но для начала запишем, в конце этого файла, ещё одну константу:
//--- Это имя работающей программы define NAME ::MQLInfoString(MQL_PROGRAM_NAME)
Чтобы представить имя текущей программы в число, достаточно каждый строковый символ перевести в числовой тип, а затем сложить все результаты.
int ETypeCode::integerName() { int sum=0; for(int i=0;i<lenght(NAME);i++) { sum+= toInt(sToChar(substring(NAME,i,1))); } return (sum); }
Чтобы определить версию терминала, необходимо проанализировать путь к исполняемому файлу. Он хранится в константе «PATH». Если он содержит расширение «.ex4», то сейчас работает терминал MetaTrader4, а если «.ex5», то это терминал MetaTrader5
bool ETypeCode::Init(ENUM_OBJECT_TYPE_CODE obType) { terminal = (find(PATH,".ex5")>0)?"500":(find(PATH,".ex4")>0)?"400":"000"; program_name = toString(integerName()); program_type = "0"+TYPE; setObjectType(obType); return (terminal!="" && program_name!="" && program_type !="" && object_type!="")?true:false; }
Переменной » programm_type » присваивается значение константы «TYPE» с добавлением «0» в начале строки.
Переменной «object_type» значение присваивается при помощи метода «setObjectType()».
Метод «toString()». Возвращает строковое представление одной из переменных. Для этого используется переменная «mod».
string ETypeCode::toString(string mod)
{
if(mod == "#m")return (terminal);
if(mod == "#n")return (programm_name);
if(mod == "#t")return (programm_type);
if(mod == "#o")return (object_type);
return ("");
}
Метод » Parse()» анализирует указатель типа объекта, передаваемого в качестве аргумента функции.
bool ETypeCode::Parse(ulong codeType) { terminal =""; program_name =""; program_type =""; object_type =""; string codeN = toString(codeType); // Версия терминала обозначается первыми тремя цифрами terminal = substring(codeN,0,3); // 400 or 500 codeN = replace(codeN,terminal,""); // Тип программы указывается двумя последними числами program_type = substring(codeN,lenght(codeN)-2,2); codeN = replace(codeN,program_type,""); // Переворачиваем строку. Так проще будет найти условный разделитель "000". Он помогает отделить тип объекта от имени программы. Функция "replace" ищет первое попавшуюся строку "000". codeN = reverse(codeN); codeN = replace(codeN,"000","_"); codeN = reverse(codeN); string spl[]; split(codeN,"_",spl); // Имя программы program_name = spl[0]; // Тип объекта object_type = spl[1]; if(terminal!="" && program_name!="" && program_type!="" && object_type!="")return (true); return (false); }
Чтобы протестировать данный класс достаточно создать скрипт. В нём пишем следующее:
include "..\Shared Projects\Trade Framework\Classes\TypeCode\ETypeCode.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { code.Init(ENUM_OBJECT_RECORD); ulong c =code.getType(); Print("+---- Script "+NAME+" ---+\n"+"TypeCode: "+toString(c)); code.Parse(c); if(code.getTerminalVersion()==5)Print("Terminal version: "+code.toString("#m")); if(code.getProgramType()==1)Print("Program type: "+code.toString("#t")); if(code.getProgramName()==code.integerName())Print("Program name: "+code.toString("#n")); if(code.getObjectType()==ENUM_OBJECT_RECORD)Print("Object type: "+code.toString("#o")); }
Результат выполнения скрипта будет выглядеть так:

Как видите всё работает. Данный класс довольно примитивный, но на данный момент этого вполне достаточно.