От начального до среднего уровня: События (I)
Введение
В предыдущей статье "От начального до среднего уровня: Struct (II)", мы говорили о базовых структурах и о том, как использовать их для передачи значений в функциях и процедурах. Хотя данная тема о структурах ещё не была изучена глубоко, я считаю, что сейчас ещё не время рассматривать некоторые существующие и возможные аспекты в структурах. Это связано с тем, что, насколько я смог заметить, MQL5 не требует очень сложного программирования. Пожалуйста, поймите меня правильно.
Я имею в виду, что, обладая очень небольшими знаниями, но хорошо проработанной базой концепций, вы можете создать и реализовать практически любой вид приложения для MetaTrader 5. И поскольку в 95% случаев (если не сказать больше) вы будете создавать простые приложения, я не вижу смысла вдаваться в более сложные подробности.
Поэтому мы пока отложим объяснение основных концепций программирования и начнем рассматривать создание кода, чтобы показать, как реализовать приложение для MetaTrader 5 с помощью чистого MQL5.
Так как данная статья рассказывает об основных принципах реализации кода на MQL5, давайте начнем с самой простой реализации. Но прежде нам нужно поговорить о другом.
Основная концепция событий
Я не буду подробно объяснять этот вопрос, поскольку для этого буквально понадобится целая книга о программировании, поскольку это очень обширная тема со множеством тонкостей. Однако прежде, чем мы начнем говорить о реализации приложения для MetaTrader 5, следует немного прояснить ситуацию.
Практически все графические приложения используют события. Время генерации каждого события не зависит от нашей программы или того, что мы реализуем. Как правило, это так, но есть особые случаи, когда мы можем запускать события изнутри нашего приложения. Однако, поскольку это особые случаи, не стоит воспринимать это как способ управления кодом в любом случае. На самом деле, во многих случаях программист, который собирается реализовать некий код для выполнения в определенной среде, на самом деле вовсе не программирует, а указывает, как код должен реагировать на каждое происходящее событие.
Некоторые события могут быть проигнорированы в одном приложении, в то время как в другом они будут иметь приоритет над любой другой деятельностью. И это очень сложно понять многим новичкам, потому что до сих пор все представленные коды были НЕ НАПРАВЛЕНЫ НА РАБОТУ С СОБЫТИЯМИ. Они просто делают то, для чего их запрограммировали.
Однако, когда мы программируем индикатор или даже советник для MetaTrader 5, мы совсем не беспокоимся о том, чтобы всё получилось. Хотя это может показаться странным, нас интересует то, как индикатор или советник должен реагировать на то или иное событие.
Например, когда мы используем такую программу, как Gimp, которая позволяет рисовать в окне, мы представляем, что программа работает определенным образом. Но по своей сути это просто реакция на события, которые мы, как пользователи, генерируем, например, щелчок, нажатие клавиши или даже перетаскивание мыши. Всё это - события. Программа НЕ ЗНАЕТ, что мы делаем и не заботится об этом. Просто ждёт события и реагирует на него. В итоге мы получаем измененное и нарисованное изображение.
То же самое касается и MetaTrader 5: неважно, что делает пользователь, приложение просто стоит в ожидании какого-то события. Когда это происходит, оно реагирует определенным образом.
«Но подождите, кто же тогда отвечает за генерацию событий?» Ну, если не вдаваться в подробности, то это вы. Ответственность за указание того, какое графическое приложение должно получить то или иное событие, лежит на операционной системе. Как пользователи, мы не можем сообщить об этом операционной системе. Мы просто сообщаем операционной системе, что какое-то приложение должно делать некое действие, например, сворачиваться, разворачиваться или что происходит, когда надо закрыть приложение.
Вопреки мнению многих людей, это НЕ операционная система определяет способ закрытия приложения, а само приложение. Поэтому, когда мы пишем текст и просим закрыть окно редактирования, редактор может спросить нас, хотим ли мы сохранить изменения. Если бы об этом заботилась операционная система, всё было бы гораздо сложнее. Поэтому в конечном итоге именно приложение, а не операционная система определяет, как оно будет реагировать на то или иное событие. Таким образом утверждать, что одна система лучше другой, указывает, на мой взгляд, на непонимание пользователем того, как всё на самом деле работает.
И хотя MetaTrader 5 может работать не только под управлением Windows, давайте предположим, что платформа работает именно под Windows. Даже если мы используем веб-версию терминала MetaTrader 5, для простоты мы будем считать, что базовой системой является Windows.
Хорошо, но зачем создавать данную концепцию? Причина в том, что вы, будучи заинтересованными в изучении программирования для MetaTrader 5, должны понимать, что любое приложение, работающее в MetaTrader 5, будет реагировать не на события, поступающие непосредственно из Windows, а на события, поступающие из MetaTrader 5. Ещё раз подчеркнем: есть конкретные ситуации, когда приложение, работающее на MetaTrader 5, будет реагировать на Windows напрямую. Но это очень специфично и требует довольно продвинутого уровня программирования, который на данный момент не применим.
Теперь, когда мы поговорили об этих вещах и получили о них некоторое представление, мы можем сложить их в одно изображение. Данное изображение находится чуть ниже.

Изображение 01
Изображение весьма символично, поскольку показывает путь, который проходит пользовательское событие, пока его не получит приложение, работающее в MetaTrader 5. Прошу заметить, что хотя мы упоминаем такое приложение в качестве советника, на самом деле это может быть любое приложение, работающее на MetaTrader 5. Однако, поскольку советник реагирует на подавляющее большинство событий, я использовал его, чтобы объяснить, где будет находиться наше приложение во всем этом процессе.
Это самая простая часть; теперь перейдем к самой увлекательной. Существует два типа событий: синхронные и асинхронные. Синхронные могут происходить очень часто, поэтому они каким-то образом связаны с системными часами. Примером данного типа события является появление нового бара на графике. Это всегда будет происходить время от времени. Однако синхронные события могут происходить и из асинхронных, что делает некоторые моменты весьма интересными. Но прежде, чем говорить о синхронных событиях, которые возникают из асинхронных, нужно понять, что такое асинхронное событие.
Асинхронное событие - это любое случайно сгенерированное событие. Например, нажатие клавиши, перемещение мыши или даже открытие или закрытие позиции. В данном случае позиция будет открыта или закрыта по такой причине: цена достигла определенного уровня. Короче говоря, асинхронное событие - это любое событие, для которого неизвестно, когда и в каком порядке оно произойдет.
Но, как уже говорилось выше, синхронные события могут основываться и на асинхронных. Примером может быть ситуация, когда мы выделяем что-то на графике MetaTrader 5, а затем перетаскиваем или удаляем объект. Время, в которое произошло каждое событие, не имеет значения, но имеет значение порядок или последовательность, в которой они произошли. Именно поэтому многие люди испытывают трудности при программировании определенных типов приложений. Это связано с тем, что может возникнуть необходимость реагировать на синхронные события асинхронного происхождения. В любом случае, теперь нам не нужно об этом беспокоиться. По мере продвижения по статье мы увидим, как работать с такими ситуациями.
Первые события на практике
Этот момент, признаться, вызывает у меня некоторые затруднения, ведь я хочу показать вам, как происходят события, но не хочу создавать что-то бессмысленное. Поэтому нам придется прибегнуть к некоторым простым и понятным средствам. Таким образом, мы не будем тратить много времени на объяснение событий и их обработку. Подобные вещи интуитивно понятны для тех, кто разбирается в событиях, но могут сильно запутать остальных. Давайте теперь сделаем первый захват и обработку событий на практике.
Прежде, чем мы начнем, нам необходимо понять исключительную особенность MQL5: разделение между различными типами приложений. По сути, в MQL5 у нас есть два вида приложений: те, которые могут реагировать на события пользователя, и те, которые не могут. Это очень просто. Среди тех, которые могут улавливать и, соответственно, реагировать на события взаимодействия с пользователем, - индикаторы и советники. Среди тех, которые не могут реагировать на события пользователя, поскольку не может их захватывать, - скрипты и сервисы. Это важно знать, чтобы выбрать лучшую альтернативу.
В основном мы работали только со скриптами. В них у нас есть точка входа, которой является процедура OnStart. Дальше всё должно быть сгенерировано и контролироваться нами, программистами. Однако в данном случае мы хотим фиксировать события любого типа. Поэтому нужно разобраться в том, что может предложить нам каждая модель, разрешенная в MetaTrader 5.
Если вы читаете эту статью, то, скорее всего, вы уже хотя бы частично использовали MetaTrader 5 с приложениями по умолчанию. В таком случае вы, наверное, заметили, что на одном графике может быть несколько индикаторов, но советник может быть только один. Затем, в зависимости от вашей цели, мы можем реализовать всё это в индикаторе или в советнике.
Однако есть одна маленькая деталь, которую необходимо понять до того, как мы приступим к реализации кода. Она относится к существующим ограничениям индикаторов и советников. Да, есть ограничения.
Например, индикатор НЕ МОЖЕТ установить контакт с системой ордеров. Данная функция принадлежит исключительно советнику. С другой стороны, советник НЕ МОЖЕТ, по крайней мере, простым способом, рисовать линии или выполнять расчеты, направленные на указание чего-либо на графике. Более того, у советника в принципе НЕТ СПОСОБА получения доступа к функциям библиотеки MQL5 для рисования на графике. Данная функция уникальна для индикаторов. Поэтому очень важно знать, как работать с этими моделями. Именно это мы здесь и покажем на протяжении нескольких статьей.
Можно подумать: "Но тогда нет возможности создать что-то действительно полезное, потому что есть ограничения, которые не позволяют нам создать уникальное приложение". На самом деле, мой дорогой читатель, MetaTrader 5 был разработан как очень стабильная и безопасная платформа. Поэтому в будущем появятся механизмы, которые позволят нам взаимодействовать с различными приложениями, что даст нам возможность создать нечто очень интересное и легко поддерживаемое в долгосрочной перспективе.
После этого первого краткого введения в тему давайте начнем с того, как происходит захват событий. Пока что мы не будем беспокоиться об их обработке, - только о захвате. Для этого вначале мы используем индикатор. Можно увидеть его чуть ниже, в минимальном коде.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+
Код 01
Мы начнем с минимального кода, чтобы концепция события легко воспринималась. Нет смысла копировать код, не понимая, почему его необходимо реализовывать именно так.
Когда этот индикатор будет помещен на график, произойдет несколько логически последовательных событий. Понимание этого очень важно для того, чтобы создавать свои собственные коды. В коде 01 мы фиксируем два события. От того, как MetaTrader 5 будет работать с этими событиями, зависит то, как мы будем их обрабатывать. Я знаю, что поначалу это может показаться непонятным, но мы можем заставить наше приложение взаимодействовать и выполнять действия, которые разработчики не представляли при создании MetaTrader 5. Однако для этого нам нужно понять, когда и как будет срабатывать каждое событие.
Естественно, поскольку мы здесь находимся на ранней стадии и уделяем внимание только дидактике, мы не будем показывать, как можно "заставить" MetaTrader 5 делать то, что многие считают невозможным. Но если вы будете упорно заниматься и тренироваться, то сможете сделать практически всё, что угодно, используя MQL5 вместе с MetaTrader 5, например, редактор видео или изображений. Это те вещи, для которых MetaTrader 5 не предназначен, поскольку его основная задача - позволить нам покупать и продавать финансовые инструменты на электронном рынке.
Хорошо, теперь мы можем взять изображение 01 и развернуть его, чтобы показать код 01, который является индикатором. Таким образом, мы получаем следующий поток сообщений.

Изображение 02
На изображении 02 показано продолжение изображения 01. То, что мы видим на изображении 02, - это то, что происходит внутри, в последней точке, где обрабатываются события. А поскольку мы движемся в определенном направлении, возможно, нам будет интересно узнать немного больше о других типах событий, которые мы не будем рассматривать здесь в статьях.
Для этого я рекомендую обратиться к документации по MQL5 по адресу: Функции обработки событий, поскольку здесь вы получите гораздо подробнее информацию о событиях, охватываемых MQL5, так как каждое из них имеет свою цель и задачу.
Но давайте вернемся к нашему вопросу и разберемся, когда срабатывает каждое из событий, которые мы видели в коде 01. Чтобы понять это, достаточно добавить небольшую команду, которая уже использовалась в предыдущих статьях. Таким образом, код 01 будет выглядеть так:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. Print(__FUNCTION__); 07. 08. return INIT_SUCCEEDED; 09. }; 10. //+------------------------------------------------------------------+ 11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 12. { 13. Print(__FUNCTION__); 14. 15. return rates_total; 16. }; 17. //+------------------------------------------------------------------+
Код 02
Отлично, теперь мы знаем, что происходит за кулисами. Но, глядя на код 02, можно подумать: "Друг, этот код бесполезен". Зачем создавать что-то подобное? Что ж, этот код на самом деле очень полезен для наших целей. Прошу заметить следующее: всё, что сделает этот код, - это выведение на терминал сообщения, указывающего, какое событие захватывается. Это происходит в строках 06 и 13. Однако посмотрите, что происходит, когда мы помещаем его на любой график.

Анимация 01
На анимации 01 мы видим, как в MetaTrader 5 сработало событие, которое захватил наш индикатор, показанный в коде 02. И это именно то, что вы видите, дорогие читатели. Хотя наш скромный код кажется совершенно бесполезным, он смог показать нам, как событие, вызванное MetaTrader 5, привело к тому, что мы запрограммировали сами. В данном случае мы обрабатываем захваченные события, выводя на терминал простое сообщение, чего, как нам кажется, поначалу не должно было произойти. Можно заметить последовательность срабатываний, но также можно видеть, что строка 13 была выведена дважды: первый раз, когда поместили индикатор на график, и второй раз, когда цена инструмента изменилась.
Понимание этого очень важно и пригодится нам, поскольку, имея дело с событиями, поступающими из MetaTrader 5, нам придется изменить способ реализации кодов при работе с индикаторами и советниками, которые являются единственными реагирующими на события, запускаемые MetaTrader 5.
Супер! Мы собираемся внести новое изменение в код 02, чтобы добавить еще один обработчик событий. Теперь нужно выполнить следующий код:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. Print(__FUNCTION__); 07. 08. return INIT_SUCCEEDED; 09. }; 10. //+------------------------------------------------------------------+ 11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 12. { 13. Print(__FUNCTION__); 14. 15. return rates_total; 16. }; 17. //+------------------------------------------------------------------+ 18. void OnDeinit(const int reason) 19. { 20. Print(__FUNCTION__); 21. }; 22. //+------------------------------------------------------------------+
Код 03
И снова мы имеем очень простой код, который, с виду, не будет служить никакой цели. Однако, когда мы запустим его, то увидим, что произойдет.

Анимация 02
Как и в первом случае, когда мы наблюдали за тем, как наше приложение реагирует на события, запускаемые MetaTrader 5, здесь мы имеем другой вид захвата. Я хочу, чтобы вы поняли следующее: индикатор находился на графике, а график использовал определенный таймфрейм. Как только мы меняем таймфрейм, срабатывает событие, которое, в свою очередь, фиксируется индикатором, показанным в коде 03.
Теперь мы подходим к интересующей нас части. Когда происходила смена таймфрейма с индикатором, показанным на графике в коде 02, мы просто потеряли это событие. Другими словами, мы проигнорировали, что MetaTrader 5 сработает, и просто заявили следующее:
Слушайте, MetaTrader 5, делай всё нужное, чтобы всё работало, потому что у меня нет проблем с этим.
В MetaTrader 5 это событие произойдет, но код 02 его проигнорирует. Таким образом, у нас сложилось впечатление, что индикатор знает, как действовать в таких ситуациях. Однако это не всегда то, чего мы хотим на самом деле. В подобных случаях мы можем использовать процедуру из строки 18 кода 03, чтобы захватить это событие и таким образом принять необходимые меры для предотвращения других неожиданных событий.
Я знаю, что многие из вас, особенно те, кто уже немного знаком с программированием на MQL5, считают, что нам не нужно беспокоиться о большом количестве событий, происходящих в индикаторе, но бывают ситуации, когда, проигнорировав определенные события, можно столкнуться со странными сбоями, о которых сообщает MetaTrader 5. Если наше приложение не примет необходимых мер, MetaTrader 5 будет вынужден сделать всё необходимое, чтобы проблемы с нашим кодом не мешали работе платформы в целом.
Хотя многие думают, что индикаторам сложно иметь ошибки, они всё же случаются, и если в MetaTrader 5 произойдет событие, которое наш код проигнорирует, то могут возникнуть проблемы с производительностью платформы, такие как замедление работы или даже сбои. Но виноват не MetaTrader 5, а приложение пользователя, которое не реагирует должным образом на срабатывающие события.
Чтобы продемонстрировать это, мы реализуем очень простой и интересный код, который преследует очень простую цель: рассказать нам, сколько раз событие OnCalculate срабатывало в течение определенного периода времени. Данный код можно увидеть ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. uint gl_Counter; 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. gl_Counter = 0; 09. 10. Print(__FUNCTION__); 11. 12. return INIT_SUCCEEDED; 13. }; 14. //+------------------------------------------------------------------+ 15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 16. { 17. if (!gl_Counter) 18. { 19. uint arr[]; 20. 21. if (FileLoad(_Symbol, arr) > 0) 22. gl_Counter = arr[0]; 23. } 24. 25. Print(__FUNCTION__, " :: ", ++gl_Counter); 26. 27. return rates_total; 28. }; 29. //+------------------------------------------------------------------+ 30. void OnDeinit(const int reason) 31. { 32. uint arr[1]; 33. 34. arr[0] = gl_Counter; 35. FileSave(_Symbol, arr); 36. 37. Print(__FUNCTION__); 38. }; 39. //+------------------------------------------------------------------+
Код 04
В этом коде нет ничего, что мы ещё не увидели и не обсудили, поэтому он вполне понятен любому, кто изучает и практикует то, что показано в этих статьях. Пожалуй, единственная часть, которая может вызвать некоторые сомнения, - это часть, связанная с функциями FileLoad и FileSave. Поскольку их очень хорошо объясняют в самой документации, я не вижу смысла приводить здесь более подробные сведения.
Однако при его выполнении код 04 выдает что-то вроде следующего.

Анимация 03
Прошу заметить, что даже если мы меняем временной интервал, счетчик продолжает увеличиваться. Так происходит, потому, что в строке 30 этого кода мы захватываем событие, которое MetaTrader 5 запускает, когда нашему коду нужно что-то сделать. В этом случае мы не устраняем ни одну из причин, которые привели к срабатыванию MetaTrader 5, мы лишь указываем MetaTrader 5 сохранить определенную переменную на диск. Это можно сделать и другими способами, о которых мы расскажем ниже. Но наша цель - показать, что могут быть ситуации, в которых мы должны иметь дело с событием, и ситуации, в которых мы можем его игнорировать. Всё зависит от того, что и как мы собираемся делать для достижения нашей конечной цели.
Прошу заметить, что если бы процедура в строке 30 не существовала или мы не обработали бы событие должным образом, то счетчик начинался бы с нуля каждый раз, когда мы меняли бы временной интервал. Это происходит в строке 08, где мы начинаем подсчитывать, сколько раз был выполнен OnCalculate. Этот код довольно интересен, не так ли? Он прост, но позволяет понять различные аспекты, связанные с событиями, генерируемыми MetaTrader 5.
Заключительные идеи
В этой статье всё стало еще веселее. Это первая статья из серии о том, как справляться с событиями. Я постараюсь подойти к этой теме в развлекательной, простой и обучающей манере и покажу вам, что не всегда следует использовать решение, предложенное другим программистом. Это связано с тем, что чаще всего решение, предложенное одним человеком, может не подходить нам в данный конкретный момент.
Я знаю, что многим не терпится узнать, о чем пойдет речь в следующих статьях, но прежде чем погрузиться в то, что будет становиться всё сложнее и сложнее, я рекомендую вам попрактиковаться и изучить то, что было рассказано в этой статье. Уверяю вас, что практиковаться по сегодняшнему материалу будет очень полезно для вас. В качестве предложения и стимула к размышлению о том, что можно применить на практике, попробуйте изменить код 04 из приложения таким образом, чтобы значение счетчика сохранялось только при наличии события изменения таймфрейма. При любом другом типе события, которое запускает MetaTrader 5, счетчик должен сбросить свой счет с нуля.
В следующей статье я расскажу о том, как сделать это безопасно и легко. Так что пора работать и учиться.
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15732
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Создание вероятностного рыночно-нейтрального робота на основе распределения доходностей
Автоматизация торговых стратегий на MQL5 (Часть 8): Создание советника с помощью гармонических паттернов Butterfly
Автоматизация торговых стратегий на MQL5 (Часть 9): Создаем советник для стратегии прорыва азиатской сессии
Как опубликовать код в CodeBase: Практическое руководство
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования