English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации (Часть 53): Всё усложняется (V)

Разработка системы репликации (Часть 53): Всё усложняется (V)

MetaTrader 5Примеры | 25 октября 2024, 08:28
406 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 52): Всё усложняется (IV), мы создали новую структуру данных, чтобы индикатор мыши мог взаимодействовать с индикатором управления. Хотя вначале взаимодействие может идти гладко и стабильно, у нас возникают некоторые проблемы, которые заставляют нас немного изменить ситуацию.

Проблема не в коде, не в платформе, не в используемых концепциях, а в наших намерениях и в том, как мы работаем. Лично я прошу прощения у всех, кто следил за этой серией статей до момента появления системы репликации/моделирования. Честно говоря, я не ожидал, что мне придется использовать некоторые приемы, найденные в модульных системах, но невозможно продолжать развитие системы репликации/моделирования без использования некоторых приемов, разработанных десятилетия назад.

Для многих из вас то, что я вам расскажу здесь, покажется крайне странным, а для кого-то - очень знакомым. Однако, как бы вы ни смотрели на это, механизм, который я здесь объясню и который мы начнем использовать все чаще и чаще, присутствует в MetaTrader 5, и MQL5 позволяет нам его использовать. Но при использовании MQL5 мы ограничены тем, что можем делать это только внутри графика или внутри самого MetaTrader 5. Это не плохо, наоборот, это навязанная мера безопасности, так как платформа предназначена для работы с деньгами, и мы не хотим, чтобы нечто странное заставляло нас терять деньги направо и налево.

Так что, несмотря на ограничения, не стоит жаловаться. То, что мы начнем использовать здесь, может заставить вас, как начинающего программиста, на некоторое время почувствовать себя потерянным. Но это только от того, что вы не знаете контент и как его использовать. Однако я уже использую данную технику некоторое время, но не тем способом, каким мы начнем пользоваться с этого момента. Так что пришло время поднять планку и применять в статьях более сложные концепции.


Объяснение концепции

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

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

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

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

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

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

Это вас очень сильно удивит, но для новичков всё становится еще сложнее. Однако, чтобы не усложнять ситуацию, я буду вести объяснение в рамках MetaTrader 5, но ограничиваясь им. Всё гораздо шире и сложнее. Поэтому надо хорошо понимать этот момент: MetaTrader 5 отправляет сообщения в вашу программу. Данные сообщения перехватываются и управляются некоторыми процедурами, присутствующими в вашей программе. Среди этих процедур есть одна, которая является более общей и позволяет отправлять гораздо более сложные элементы, а также использует общий и четко определенный интерфейс. Название этой процедуры - OnChartEvent. С этим закончили. Теперь наступает сложная часть.

MQL5 (буду пока только о нем, чтобы не усложнять ситуацию) позволяет нам, как программистам, определять пользовательские события. Эти события обозначаются константой и значением. Имя этой константы: CHARTEVENT_CUSTOM. Таким образом, используя данную константу, можно отправить сообщение обработчику сообщений из любой точки нашей программы, что позволяет сконцентрировать обработку как специфических, так и общих событий в одной точке, но в любом случае без вызова диспетчера сообщений. Вы должны сделать это правильным способом, и, чтобы упростить ситуацию, MQL5 предоставляет нам функцию для данной цели: EventChartCustom. Используя эту функцию, можно отправлять сообщения в стандартный обработчик сообщений, вышеупомянутый OnChartEvent, который, в моем случае, вызывает DispatchMessage.

Всё это прекрасно выглядит и отлично работает, позволяя нам делать множество вещей. Однако здесь кроется опасность. Мы имеем в виду CHARTEVENT_CUSTOM. Самая большая опасность этих пользовательских вызовов кроется не в моей программе, не в вашей и не в программе любого другого хорошего программиста, а заключается в том, что пользователь не знает, что делает каждая программа. Не стоит недооценивать бедных пользователей. Часто они не имеют ни малейшего представления о том, что происходит на самом деле. Вот почему важно НИКОГДА не использовать что-то, без понимания, чем оно является на самом деле. Программа может прекрасно работать в нескольких сценариях, но будет только один, в котором взаимодействие между программами превратится в полный кошмар: это может привести к сбоям в работе платформы, исчезновению или появлению предметов из ниоткуда, и вы не сможете понять, что происходит. Хорошо, когда эффекты ощутимы, но как быть, если они происходят абсолютно незаметно? Вам может показаться, что всё идет как по маслу, а на самом деле ситуация может быть словно вы на краю пропасти.

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

Я не буду углубляться в эту тему, потому что это выходит за рамки основной цели данного объяснения, но важно кое-что понять: MetaTrader 5 похож на операционную систему Windows. Если использовать правильные инструменты правильным образом, у нас никогда не будет проблем с платформой. Но если вы начнете всё смешивать, будьте осторожны, так как можно столкнуться с рядом проблем. Зная, что MetaTrader 5 - это графическая среда, ориентированная на торговлю на финансовых рынках, возникает вопрос: что будет, если две программы используют EventChartCustom, ведь эта функция заставляет MetaTrader 5 передавать сообщения по запросу программиста?

Хорошо. Вопрос очень простой, и действительно, это правильный вопрос. Но чтобы понять, давайте начнем с более простого случая: что происходит, когда ОДНА программа использует функцию EventChartCustom? По сути, MetaTrader 5 отправит пользовательское событие, которое будет обработано процедурой OnChartEvent. Это кажется очевидным, не так ли? На в самом деле нет. Это не так очевидно, и в этом кроется опасность.

Типичное использование EventChartCustom можно увидеть в коде индикатора управления, который был показан в предыдущей статье. В строке 30 кода индикатора управления можно увидеть следующий код:

30.     EventChartCustom(user00, C_Controls::evInit, Info.s_Infos.iPosShift, Info.df_Value, "");

Итак, когда MetaTrader 5 выполнит эту строку, возникнет событие ChartEvent, которое приведет к выполнению OnChartEvent тем кодом, который присутствует на графике. И так продолжится до тех пор, пока мы не найдем функцию DispatchMessage, которая находится в классе C_Control, в 161 строке кода, показанного в предыдущей статье, но чтобы было проще, давайте приведем этот фрагмент здесь.

161.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
162.                    {
163.                            u_Interprocess Info;
164.                            
165.                            switch (id)
166.                            {
167.                                    case CHARTEVENT_CUSTOM + C_Controls::evInit:
168.                                            Info.df_Value = dparam;
169.                                            m_Slider.Minimal = Info.s_Infos.iPosShift;
170.                                            SetPlay(Info.s_Infos.isPlay);
171.                                            if (!Info.s_Infos.isPlay) CreateCtrlSlider();
172.                                            break;

Фрагмент кода, присутствующий в классе C_Control

Теперь, поскольку событие является пользовательским, MetaTrader 5 установит значение ID в вызове процедуры OnChartEvent, чтобы данный идентификатор был значением, соответствующим CHARTEVENT_CUSTOM плюс еще одно значение. В продемонстрированном случае значение будет соответствовать значению C_Control:evInit. Но каково значение C_Control:evInit? Чтобы выяснить это, нужно перейди в строку 35 кода класса C_Control и проверить значение.

035.            enum EventCustom {evInit};

Данное значение является частью перечисления. Поскольку перечисление имеет только это значение и не инициализируется, оно будет начинаться со значения по умолчанию, то есть с НОЛЬ. Тогда та же самая строка 30, присутствующая в индикаторе управления, в действительности будет интерпретироваться MetaTrader 5 так:

30.     EventChartCustom(user00, CHARTEVENT_CUSTOM + 0, Info.s_Infos.iPosShift, Info.df_Value, "");

Этот код будет работать идеально и безопасно, позволяя нам, как программистам, в любой момент сделать пользовательский вызов, чтобы заставить процедуру DispatchMessage, присутствующую внутри класса C_Control, инициализировать некоторые значения, которые не были установлены в конструкторе класса. Подобные вещи встречаются очень часто и являются вполне соответствующей формой программирования, весьма полезной в самых разных ситуациях.

То же самое относится и к индикатору мыши. Давайте теперь посмотрим на него, чтобы понять кое-что еще, но всё же в рамках ОДНОЙ программы, использующей пользовательские события в MetaTrader 5.

Во всем коде индикатора мыши не видно ни одного вызова EventChartCustom. Однако в обработчике сообщений есть код для реагирования на пользовательское событие. Данный код существовал довольно долго, почти невольно предвидя свое будущее использование. Вы можете увидеть этот код обработки в строках 196 и 199 класса C_Mouse. Если вы внимательно присмотритесь, то заметите несколько вещей. Давайте приведем этот фрагмент в данной статье, чтобы лучше понять то, что я хочу объяснить.

189. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
190.                    {
191.                            int w = 0;
192.                            static double memPrice = 0;
193.                            
194.                            if (m_Mem.szShortName == NULL) switch (id)
195.                            {
196.                                    case (CHARTEVENT_CUSTOM + ev_HideMouse):
197.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
198.                                            break;
199.                                    case (CHARTEVENT_CUSTOM + ev_ShowMouse):
200.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
201.                                            break;

Фрагмент кода, присутствующий в классе C_Mouse

Обратите внимание, в данном фрагменте мы используем ev_HideMouse и ev_ShowMouse. Если какая-либо программа захочет скрыть линию индикатора мыши, достаточно будет попросить MetaTrader 5 отправить пользовательское событие индикатору мыши. Таким образом можно скрыть или показать линию мыши. Обратите внимание, что мы не уничтожаем объект, а просто изменяем свойство цвета.

Но данные значения ev_HideMouse и ev_ShowMouse являются перечислениями, но откуда они берутся? Можно увидеть их в строке 34 класса C_Mouse. Опять же, для простоты объяснения, давайте перенесем данную строку в эту статью.

034.            enum eEventsMouse {ev_HideMouse, ev_ShowMouse};

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


Видео 01 - Демонстрация

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


Видео 02 - Конфликты

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

Почему два индикатора, которые, казалось бы, хорошо работают независимо друг от друга, работают вместе таким странным образом?


Понимаем концепцию рабочей среды

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

В MetaTrader 5 данный вопрос среды рассматривается очень серьезно. Если вы не понимаете эту идею, то не поймете и причину конфликта, показанного в видеоролике 02. И, что самое главное, вы не сможете разрешить этот самый конфликт. Запомните: Мы хотим, чтобы индикатор мыши работал в полной гармонии с любым другим индикатором. Таким образом, устраним ряд проблем, и он становится модулем еще более крупной системы.

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

После просмотра видео 01 мы видим, что индикаторы не конфликтуют, однако в видео 02 странные вещи происходят с индикатором мыши и индикатором управления при их совместном размещении на одном графике Хорошо видно, что MetaTrader 5 рассматривает каждый график как среду, полностью изолированную от остальных.

Данный вид наблюдения очень важен и будет использоваться в дальнейшем, но есть еще один не менее важный момент в видео 02. Происходит несколько вещей, когда MetaTrader 5 обновляет график в ответ на пользовательское событие, чтобы изменить таймфрейм. Чтобы понять это, необходимо знать, как работает MetaTrader 5.

Когда мы запрашиваем смену таймфрейма (как показано на видео) MetaTrader 5 временно удаляет всё на графике, а затем заменяет то, что действительно необходимо. По этой причине на графике заменяются только индикаторы и советник. Если программист не примет это во внимание, все программы будут конфликтовать. Это связано с тем, что порядок, в котором MetaTrader 5 восстанавливает объекты, скорее всего, не будет совпадать с порядком, в котором пользователь разместил их на графике. Возможно, пользователь размещает всё на графике в таком порядке, что ничего не происходит. Но как только что-то произойдет, и MetaTrader 5 перестроит график, порядок может оказаться другим, и тогда начнутся проблемы. Многие могут винить платформу, некоторые - операционную систему, некоторые - Бога или дьявола. Но на самом деле проблема заключается в том, что программа, созданная вами, или кем-то еще, не предусматривает совместного использования нескольких элементов. А тот факт, что многие коды закрыты, еще больше усложняет понимание причин конфликта.

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

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

Индикаторы управления и мыши компилируются отдельно. В коде мы используем перечисления, которые должны быть каким-то образом связаны с классом, который будет отвечать на сообщения. Да, но такого рода рассуждения означают, что мы всё-таки делаем какие-то предположения, а в программировании не стоит делать предположений. Для компилятора данный код пользовательского события всегда будет значением, сдвинутым от константы. Константа это CHARTEVENT_CUSTOM, и поскольку перечисления начинаются со значения по умолчанию (ноль), то и код обработки сообщений индикатора мыши и индикатора управления начинается с одного и того же индекса - CHARTEVENT_CUSTOM плюс ноль.

Можно подумать, что это не объясняет причину конфликта, но вы ошибаетесь. Всё именно так. И еще другие вещи. Также это объясняет то, как устроена рабочая среда MetaTrader 5.

Каждый график представляет собой рабочую среду в MetaTrader 5. Когда программа отправляет пользовательское событие в MetaTrader 5, и это событие не отправляется в программу или какую-то другую конкретную программу, а отправляется в MetaTrader 5. Платформа запустит данное событие для любой программы, присутствующей в рабочей среде, то есть в графике. Поэтому в видеоролике 02 можно увидеть, как это происходит. Это связано с тем, что все программы в пределах графика, как только сработает пользовательское событие, получают одинаковое уведомление от MetaTrader 5. Теперь подумайте о советнике, который использует пользовательское событие для открытия и закрытия позиции. Если это же событие имеет в качестве индекса событие для отправки сигнала в индикаторе, который находится на том же графике, что и советник, что же произойдет, когда советник или индикатор запустит пользовательское событие? У вас будут большие проблемы. Всё именно так. 

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

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


Заключение

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

Чтобы лучше понять и усвоить то, о чем здесь говорилось, вы можете создать небольшие программы, которые могут быть простыми индикаторами, единственная цель которых - генерировать и реагировать на пользовательские события. Разместите их на одном или на нескольких графиках. Понаблюдайте, как они ведут себя, когда находятся вместе и по-отдельности. Но это, прежде всего, золотой ключик к данной статье: попробуйте заставить один индикатор изменить что-то в другом индикаторе, как на одном графике, так и на разных.

Если не можете заставить их взаимодействовать через пользовательские события, когда они находятся в разных графиках, то не волнуйтесь. Не думайте, что вы плохой программист. Возможно, у вас просто еще нет нужных знаний. Здесь я не только покажу вам, как это сделать, но и выведу всё на другой уровень понимания, поскольку, как вы могли заметить, я избегаю, насколько это возможно, использования некоторых моментов, например, внешних DLL, чтобы покрыть отдельные недостатки MQL5, поскольку до сих пор он прекрасно справлялся с тем, что мы делаем, не требуя причудливых решений и тому подобных вещей.

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

Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/11932

Прикрепленные файлы |
Anexo.zip (420.65 KB)
Разрабатываем мультивалютный советник (Часть 19): Создаём этапы, реализованные на Python Разрабатываем мультивалютный советник (Часть 19): Создаём этапы, реализованные на Python
Пока что мы рассматривали автоматизацию запуска последовательных процедур оптимизации советников исключительно в штатном тестере стратегий. Но что делать, если между такими запусками нам хотелось бы выполнить некоторую обработку уже полученных данных, используя другие средства? Попробуем добавить возможность создания новых этапов оптимизации, выполняемых программами, написанными на Python.
Модифицированный советник Grid-Hedge в MQL5 (Часть IV): Оптимизация простой сеточной стратегии (I) Модифицированный советник Grid-Hedge в MQL5 (Часть IV): Оптимизация простой сеточной стратегии (I)
В четвертой части мы вернемся к советникам Simple Hedge и Simple Grid, разработанным ранее. В этот раз будем совершенствовать советник Simple Hedge. Будем использовать математический анализ и подход грубой силы (brute force) чтобы оптимизировать стратегию. Эта статья углубляется в математическую оптимизацию стратегии и закладывает основу для будущего исследования оптимизации на основе кода в последующих частях.
Упрощаем торговлю на новостях (Часть 2): Управляем рисками Упрощаем торговлю на новостях (Часть 2): Управляем рисками
В этой статье мы добавим наследование в предыдущий и новый код. Для обеспечения эффективности будет внедрена новая структура базы данных. Кроме того, мы создадим класс по управлению рисками для расчета объемов.
Нейросети в трейдинге: Контрастный Трансформер паттернов (Окончание) Нейросети в трейдинге: Контрастный Трансформер паттернов (Окончание)
В последней статье нашей серии мы рассмотрели фреймворк Atom-Motif Contrastive Transformer (AMCT), который использует контрастное обучение для выявления ключевых паттернов на всех уровнях — от базовых элементов до сложных структур. В этой статье мы продолжаем реализацию подходов AMCT средствами MQL5.