English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации (Часть 28): Проект советника — класс C_Mouse (II)

Разработка системы репликации (Часть 28): Проект советника — класс C_Mouse (II)

MetaTrader 5Тестер | 14 февраля 2024, 17:26
621 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье "Разработка системы репликации (часть 27): Проект Expert Advisor (II)", мы начали разработку нового класса. Однако ближе к концу статьи я убедился в важности представления иного подхода к программированию. Мы представим это только из любопытства, с целью приблизиться к более естественному языку.

Для тех, кто давно занимается программированием, возможно, не будет иметь особого смысла то, что покажем ниже. Зачем нужны эти хлопоты по приближению программирования к естественному языку? Ответ прост: вы программируете не для машины, а для других программистов. Когда начали выпускаться первые системы, способные что-то факторизовать, всё зависело от того, насколько хорошо инженеры знали проект. Такова была реальность на рассвете компьютерной техники, когда не было даже терминала, чтобы что-то программировать.

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

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

Но, учитывая, что язык MQL5 является производным от языков C/C++ и обладает способностью делать вещи таким образом, чтобы код был более читабельным, он становится идеальным для демонстрации чего-то другого. Затем, после некоторого времени анализа и осознания того, что я могу помочь энтузиастам понять, что программируется, даже без полного понимания кода, было решено на короткое время изменить способ выражения кода. В конце концов, всё будет понято компилятором, так что для него это не имеет значения. Но для энтузиастов это будет иметь большое значение, так как язык будет гораздо ближе к естественному. Хотя на первый взгляд код может показаться странным и необычным, новичку будет значительно проще разобраться в нем.

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


Теперь создаем файл Defines.mqh

Две статьи назад мы рассмотрели использование директивы компиляции #define. Я упомянул, что существует особый случай, когда определение не удаляется в конце файла, хотя в то время я не показал практического применения данного использования директивы, потому что там нет правильного способа сделать этого. Поэтому мы оставили этот вопрос открытым. Здесь ключевым моментом является то, что если вы поймете некоторые вещи и понятия о MQL5, зная, что он является производным от C/C++, то вам станет интереснее выполнять определенные операции на языке без особых проблем, делая код более читабельным как для человека, который не является программистом и не может разобраться во всех этих символах, так и для программиста, которому необходимо понимать, что делается.

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

Ниже приведено полное содержание данного файла:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
/*+------------------------------------------------------------------+
Definitions for increasing the level of the coded language in MQL5, for
more details see the article at the following link:
https://www.mql5.com/en/articles/11349
+------------------------------------------------------------------+*/
#define equal =
#define different !=
#define identical ==
#define and &&
//+------------------------------------------------------------------+

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

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

После того как файл Defines.mqh создан, нам нужно как-то заставить компилятор использовать его. Для этого мы включим этот файл в один из самых основных файлов построения всей системы - C_Terminal.mqh. Если посмотреть на часть включений в файле C_Terminal.mqh, мы увидим следующую строку кода:

#include "Macros.mqh"
#include "..\..\Defines.mqh"

Будьте внимательны, потому что это очень важно. Мы объявляем файл Macros.mqh в двойных кавычках, что указывает компилятору на то, что этот файл находится в той же папке, что и файл C_Terminal.mqh. Однако, когда мы включаем файл Defines.mqh, он также заключен в двойные кавычки, но с одной особенностью. Эта разница, обозначенная (..\), указывает компилятору, на сколько уровней, начиная с каталога, в котором находится C_Terminal.mqh, нам понадобится подняться в структуре каталогов, чтобы найти файл Defines.mqh. В данном случае нам нужно подняться на два уровня вверх, поскольку структура каталогов содержит разные уровни для организации кода. Таким образом, файл Defines.mqh будет расположен в корневой части структуры каталогов проекта. Если по какой-то причине корень проекта изменится, это не повлияет на компилятор, который всегда будет искать файл Defines.mqh в том же месте.

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

if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
if_case m_Info.Study identical eStudyExecute then ExecuteStudy(memPrice);

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

#define if_case if(
#define then )

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

Давайте вернемся к тому месту, где мы остановились в предыдущей статье: там мы собирались рассмотреть последнюю функцию, присутствующую в классе C_Mouse.


Функция DispatchMessage: Наши средства связи с внешним миром

Все классы, которые так или иначе должны получать события от платформы MetaTrader 5, будут иметь в своем портфеле эту функцию DispatchMessage. Она будет служить классу способом получения и реагирования на генерируемые события. Самое важное, - это понять, что MetaTrader 5 - это программа, основанная на событиях, то есть программа типа REAL TIME. Работать с этим, мягко говоря, довольно сложно. Поэтому любой код должен быть очень специфичным при работе с подобными событиями. Но прежде чем мы рассмотрим функцию DispatchMessage, нам нужно ознакомиться с другим кодом, который будет встречаться в классе C_Terminal. Этот код можно увидеть ниже:

const double AdjustPrice(const double arg) const { return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); }

Данный код можно было бы поместить в класс C_Mouse, но из-за других факторов мы решили поместить его в класс C_Terminal. Этот код представляет собой просто факторизацию, которая стремится скорректировать цену таким образом, чтобы она всегда была равна значению, ожидаемому торговым сервером. Нередко ордеры отклоняются сервером из-за того, что указанная цена неверна. Многие отказываются от изучения возможности создания советника только потому, что при попытке отправить ордер на торговый сервер получают ошибку в ответ. Некоторые классы активов имеют более простую факторизацию для корректировки цены, в то время как другие имеют гораздо более сложную, включающую несколько вопросов. Однако на практике вышеупомянутая функция управляет всеми этими факторами, гарантируя, что цена всегда будет соответствующей, независимо от типа используемого актива. Это очень важно для нас, потому что для создания и использования советника в системе репликации/моделирования необходимо, чтобы независимо от актива цена была действительно соответствующей, будь то на DEMO-счете или даже на РЕАЛЬНОМ счете. Поэтому вы можете использовать и даже злоупотреблять этой функцией. Если это сделать, то вы увидите, как система действительно способна адаптироваться к любому типу рынка и актива. Так что используйте и изучайте эти знания по максимуму.

Теперь, когда мы увидели функцию, которая будет корректировать цену по мере необходимости, мы можем приступить к рассмотрению функции DispatchMessage. Вы можете полностью ее увидеть ниже:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      int w equal 0;
      static double memPrice equal 0;
                                
      C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
      switch (id)
      {
         case (CHARTEVENT_CUSTOM + ev_HideMouse):
            ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
            break;
         case (CHARTEVENT_CUSTOM + ev_ShowMouse):
            ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
            break;
         case CHARTEVENT_MOUSE_MOVE:
            ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X equal (int)lparam, m_Info.Data.Position.Y equal (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
            ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price equal AdjustPrice(m_Info.Data.Position.Price));
            if (m_Info.Study different eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
            m_Info.Data.ButtonStatus equal (uint) sparam;
            if (CheckClick(eClickMiddle) and ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) different clrNONE)) CreateStudy();
            if (CheckClick(eClickLeft) and (m_Info.Study identical eStudyCreate))
            {
               ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
               ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice equal m_Info.Data.Position.Price);
               m_Info.Study equal eStudyExecute;
            }
            if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
            break;
         case CHARTEVENT_OBJECT_DELETE:
            if (sparam identical def_NameObjectLineH) CreateLineH();
            break;
      }
   }

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

int w equal 0;
static double memPrice equal 0;

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

int w = 0;
static double memPrice = 0;

Как видите, разницы нет. Но, конечно, в обоих случаях любой человек мог понять код. В первом случае мы имеем «буквальный» формат. Но не волнуйтесь, мы только начинаем. Приведенный пример не полностью отражает все наши возможности, чтобы сделать код более читабельным.

Давайте посмотрим на еще один фрагмент. Это уже не имеет прямого отношения к читабельности кода, но всё же требует пояснения.

case (CHARTEVENT_CUSTOM + ev_HideMouse):
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
   break;

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

case (CHARTEVENT_CUSTOM + ev_ShowMouse):
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
   break;

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

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

C_Terminal::DispatchMessage(id, lparam, dparam, sparam);

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

case CHARTEVENT_OBJECT_DELETE:
   if (sparam identical def_NameObjectLineH) CreateLineH();
   break;

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

case CHARTEVENT_OBJECT_DELETE:
   if (sparam == def_NameObjectLineH) CreateLineH();
   break;

Независимо от того, как представлен код, его результат следующий: когда объект удаляется из графика активов, то платформой запускается сообщение или, скорее, событие. Оно сообщает программе, запросившей такой тип сообщения, о том, что объект был удален из графика. Встретив обработчик CHART_EVENT_OBJECT_DELETE, программа должна выполнить присутствующий в нем код. Если имя объекта, указанное в константной переменной param, совпадает с тестируемым, то выполняемый код пересоздаст ценовую линию.

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

case CHARTEVENT_MOUSE_MOVE:
   ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X equal (int)lparam, m_Info.Data.Position.Y equal (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
   ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price equal AdjustPrice(m_Info.Data.Position.Price));
   if (m_Info.Study different eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
   m_Info.Data.ButtonStatus equal (uint) sparam;
   if (CheckClick(eClickMiddle) and ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) different clrNONE)) CreateStudy();
   if (CheckClick(eClickLeft) and (m_Info.Study identical eStudyCreate))
   {
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice equal m_Info.Data.Position.Price);
      m_Info.Study equal eStudyExecute;
   }
   if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
   break;

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

  • Если `m_Info.Study` отличается от `eStudyNull`, значит, что-то должно быть выполнено.
  • `m_Info.Data.ButtonStatus` равно `sparam`.
  • Если была нажата средняя кнопка и что-либо (цвет ценовой линии) отличается от `clrNONE`, то выполните следующее.
  • Если она была нажата левой кнопкой мыши и `m_Info.Study` равно `eStudyCreate`, то будет выполнено это действие.
  • Присвойте `eStudyExecute` значение `m_Info.Study`.
  • Если `m_Info.Study` равно `eStudyExecute`, то выполните это.

Видно, что даже если прочитать все вышеперечисленные пункты, что было продемонстрировано, это всё равно показывает, что мы можем добавить еще больше вещей в наш файл Defines.mqh, чтобы сделать язык еще более читабельным, чем я демонстрирую. Связано это с тем, что мы можем добавить больше элементов, чтобы сделать программу еще более читабельной. Это качество хорошего языка программирования, и оно всегда будет присутствовать в программах хорошего технического качества. Еще один способ сделать код достаточно читабельным - всегда добавлять комментарии к наиболее важным функциям или пунктам, и в этом отношении MQL5 значительно превосходит C/C++. Попробуйте поместить комментарии к переменным и процедурам. При использовании редактора MetaEditor он отображает такие комментарии в качестве подсказки, что оказывается очень полезным.

А как на самом деле будет запрограммирован приведенный выше код? Или, точнее, как компилятор на самом деле видит приведенный выше код? Это показано ниже:

case CHARTEVENT_MOUSE_MOVE:
   ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
   ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
   if (m_Info.Study != eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
   m_Info.Data.ButtonStatus = (uint) sparam;
   if (CheckClick(eClickMiddle) && ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
   if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
   {
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
      m_Info.Study = eStudyExecute;
   }
   if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
   break;

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

И прежде чем закончить статью, давайте вкратце рассмотрим код советника на данном этапе разработки. Ниже можно ознакомиться с ним полностью:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.28"
#property link "https://www.mql5.com/en/articles/11349"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 equal clrBlack;          //Price Line
input color     user01 equal clrDarkGreen;      //Positive Study
input color     user02 equal clrMaroon;         //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse equal new C_Mouse(user00, user01, user02);
                
   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   ChartRedraw();
}
//+------------------------------------------------------------------+

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


Заключение

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

Еще одна важная деталь: в прилагаемом коде можно найти два последних класса (C_Terminal и C_Mouse), использующих содержимое файла Defines.mqh. Это делает код более читабельным. Однако, в отличие от того, что мы сказали в начале статьи, где мы подразумевали, что весь код будет следовать этому форматированию, это просто любопытство. При желании можно воспользоваться этой техникой. В начале своей карьеры программиста на C/C++ я некоторое время использовал данный подход, чтобы лучше понять синтаксис языка. Я знаю, что поначалу это может быть довольно запутанным, но со временем можно привыкнуть. Эта техника может быть полезна, особенно в сложных проектах, требующих анализа обширных булевых и логических комбинаций. Наличие двойных символов в этом контексте может усложнить ситуацию для новичков. В качестве примера можно привести следующий факт:

Кто не путал, даже среди опытных программистов, использование операции LOGICAL AND (&) с BOOLEAN AND (&&)? Обратите внимание, они практически одинаковы. Но в первом случае операция выполняется бит за битом, а во втором - разбирается вся переменная и возвращается true или false. Это многих захватывает в моменты, когда нам нужно быстро создавать программы.

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


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

Прикрепленные файлы |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
Разметка данных в анализе временных рядов (Часть 3):Пример использования разметки данных Разметка данных в анализе временных рядов (Часть 3):Пример использования разметки данных
В этой серии статей представлены несколько методов разметки временных рядов, которые могут создавать данные, соответствующие большинству моделей искусственного интеллекта (ИИ). Целевая разметка данных может сделать обученную модель ИИ более соответствующей пользовательским целям и задачам, повысить точность модели и даже помочь модели совершить качественный скачок!
Теория категорий в MQL5 (Часть 23): Другой взгляд на двойную экспоненциальную скользящую среднюю Теория категорий в MQL5 (Часть 23): Другой взгляд на двойную экспоненциальную скользящую среднюю
В этой статье мы продолжаем рассматривать популярные торговые индикаторы под новым углом. Мы собираемся обрабатывать горизонтальную композицию естественных преобразований. Лучшим индикатором для этого является двойная экспоненциальная скользящая средняя (Double Exponential Moving Average, DEMA).
Использование алгоритмов оптимизации для настройки параметров советника "на лету" Использование алгоритмов оптимизации для настройки параметров советника "на лету"
В статье рассматриваются практические аспекты использования алгоритмов оптимизации для поиска наилучших параметров советников "на лету", виртуализация торговых операций и логики советника. Данная статья может быть использована как своеобразная инструкция для внедрения алгоритмов оптимизации в торгового советника.
Разработка системы репликации (Часть 27): Проект советника — класс C_Mouse (I) Разработка системы репликации (Часть 27): Проект советника — класс C_Mouse (I)
В этой статье мы воплотим в жизнь класс C_Mouse. Он обеспечивает возможности программирования на самом высоком уровне. Однако разговоры о высокоуровневых или низкоуровневых языках программирования не связаны с включением в код нецензурных слов или жаргона. Всё наоборот. Когда мы говорим о высокоуровневом или низкоуровневом программировании, мы имеем в виду, насколько легко или сложно понять код другим программистам.