Видеоуроки по программированию на MQL5 - страница 6

 

MQL5 Первый робот. Готовим include-файл. Часть 1


MQL5 Первый робот. Готовим include-файл. Часть 1

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

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

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

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

Примером такой функции будет функция "AccountPositions()", которую мы уже написали ранее. У нее есть базовый вариант и три варианта перегрузки. Все это можно было бы реализовать в виде одной функции, но тогда мы столкнулись бы с проблемой объяснения функции, какие именно позиции она должна считать в каждом случае.

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

Чтобы реализовать все четыре варианта перегрузки функции "AccountPositions()", мы используем перечисление "Direction". Переменная "direction" будет выступать в качестве переключателя между этими вариантами.

Это позволяет упростить код функции и не требует запоминания всех положений переменной "direction". Также это позволяет изменить функцию, не затрагивая другие варианты и перегрузки.

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

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

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

Но давайте посмотрим на функцию "CountPositions()" более внимательно. Обратите внимание, что в двух последних вариантах ее выполнения используются не все параметры, которые функция принимает. Например, в некоторых вариантах используется только одна переменная, хотя у функции есть три формальных параметра.

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

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

Кстати, в данном контексте массивы и перечисления тоже называются переменными, так как они представляют собой упорядоченные наборы значений.

Чтобы закончить функцию, нужно реализовать все возможные варианты ее действия. Например, функция "CountPositions()" считает открытые позиции по всем возможным вариантам их фильтрации, используя базовые варианты такой фильтрации.

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

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

Если в качестве формального параметра используется какой-то базовый тип данных (int, double, dates, time), то передавать его функции лучше по значению, то есть путем копирования значения из аргумента, который мы указываем при вызове функции.

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

Факт, что все позиции в терминале имеют индексацию, аналогичную массивам, позволяет удобно работать с ними. Например, для получения общего количества позиций на счете, можно использовать функцию "PositionSelect()".

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

Также можно задать условия поиска позиции по различным критериям, например, по магическому номеру или символу, на котором она открыта. Если все свойства позиции удовлетворяют критериям поиска, то увеличиваем счетчик на 1.

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

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

Эти и другие функции могут быть полезны для работы с позициями в автоматизированной торговле. Код и комментарии будут доступны в исходном файле робота для более детального ознакомления.

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

Не забывайте поддержать автора канала, поставив палец вверх, подписавшись на канал и оставив комментарий под видео. Надеюсь, видео было познавательным и интересным для всех. Пока!

Тайм-коды, они же содержание:

00:00:00
- Приветствие и тема урока

00:00:52 - Первое правило

00:01:51 - Пример как не надо делать №1

00:03:50 - Почему так не надо делать

00:05:28 - Пример как не надо делать №2

00:07:00
- Почему так не надо делать

00:07:34 - Резюмируем первое правило

00:08:34
- Второе правило

00:09:42 - Пример как надо делать

00:10:48
- Резюмируем второе правило

00:11:06 - Третье правило

00:11:57 - Пример из MQL5

00:13:11
- Функция-пример

00:13:27
- Передача переменных по константным ссылкам

00:13:41
- Пример константной ссылки

00:14:02 - Передача массивов в функции с примерами

00:15:12
- Резюмируем третье правило

00:17:19 - Как не надо делать. И почему

00:17:54 - Резюмируем четвертое правило

00:18:07
- Итоговый вывод

00:18:57 - Планы на следующее видео и самостоятельная работа

00:19:23 - Заключение
MQL5 Первый робот. Готовим include-файл. Часть 1
MQL5 Первый робот. Готовим include-файл. Часть 1
  • 2022.01.22
  • www.youtube.com
Продолжаем изучать MQL5 на примере написания торгового эксперта. В этом виде-уроке начнем готовить include-файл с функциями к использованию в серьезных прогр...
 

MQL5 Первый робот. Готовим Include-файл. Часть 2


MQL5 Первый робот. Готовим Include-файл. Часть 2

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

Начнем с функции выставления стоп-приказов PositionSLTP(). Она нарушает правило номер два, так как реализует не все возможные варианты установки стоп-приказов. Кроме того, функции входа в рынок UpOnBuy() и UpOnSell() нарушают сразу три правила. Они реализуют как открытие рыночных позиций, так и отложенных ордеров, нарушая правило номер четыре, и не соответствуют своему назначению, нарушая правило номер один.

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

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

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

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

Аналогично поступим с тейк-профитом. Если значение параметра tp меньше нуля, а леракость (коэффициент тейк-профита) больше 0, то теперь обновляем.

Далее идут действия, если значение параметра типа (свойства стоп-приказа) больше 0. А на случай, если какой-то из параметров sl или tp равен нулю, у нас никаких действий не предусмотрено, и соответствующие поля запроса наша функция не трогает. Следовательно, значение соответствующих стоп-приказов у позиции не изменится.

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

Теперь о том, почему я не хочу реализовывать в функции PositionSLTP() такие вещи, как выставление стоп-приказов по ценовым уровням и о том, почему не хочу касаться коэффициентов. Все потому, что нам в любом случае придется приводить вид котировки или коэффициенты к виду в пунктах. Проверка на ограничение Freeze Level никуда не денется. Ограничение это задается именно в пунктах. Зачем делать еще один вариант функции и расписывать все возможные преобразования, тем самым усложняя эту функцию?

Противоположное рассуждение также верно. Если вы все равно придете к виду уже существующего варианта, а вот чтобы каждый раз перед вызовом функции PositionSLTP() не заморачиваться с пересчетом разницы котировок в его пункты, можно задействовать очень простую функцию PriceDistance(). Ее объявление будет больше, чем ее реализация. Назовем ее PriceDistance() и принимать она будет три аргумента - два типа double и один типа string. PriceA и PriceB это цены котировки, расстояние между которыми нужно найти. Символ - имя торгового инструмента, на котировку которого и будем считать расстояние. Функция возвращает целое число типа int, которое будет результатом разницы между PriceA и PriceB, деленное на минимальный шаг изменения котировки торгового инструмента, указанного параметром символ.

Деление тут необходимо для преобразования вещественного результата разницы котировок в целые пункты. А так как имеем дело с вычитанием, чтобы случайно не получить отрицательное значение, и при этом даже не вспоминать о таких вещах, как уменьшаемое и вычитаемое, и кто из них на таком месте должен быть, используем функцию MathAbs(). Принудительно приводим результат к типу int, итоговый результат поэтому все наше выражение тоже заключено в скобки. Теперь, вычислив нужным ценовым уровнем, вызываем функцию PriceDistance() передав в нее все необходимые сведения и можем вызывать ее столько раз, сколько потребуется, изменяя значения аргументов по необходимости. Эту функцию можно использовать не только для модификации позиций, но и для других нужд.

Например, нужно удостовериться, что у позиции размер стоп-приказа соответствует заданному значению. Вытаскиваем из позиции значение цены открытия уровня и требуемого стоп-приказа. Имя торгового инструмента, на котором позиция открыта, передаем эти данные функции PriceDistance(). Остается только сравнить полученный результат с эталонным значением. Если все сходится, идем дальше, если нет - действуем по ситуации.

На данный момент все. В следующем видео будем работать с функциями входа в рынок. А пока что не забывайте про палец вверх, звонить в колокол и обязательно подписывайтесь на канал. Также пишите в комментариях свое мнение об увиденном и услышанном. Всем пока!

Тайм-коды, они же содержание:

00:00:00 - Приветствие

00:00:10
- Что не так с функциями скрипта

00:00:27 - Быстрая навигация по функциям в файле

00:00:54
- Что не так с функциями скрипта. Продолжение

00:02:41
- Общий план действий

00:03:05 - Функция модификации позиций. Подробности

00:04:43
- Дополняем Position_SLTP(...)

00:06:25 - Position_SLTP(...). Почему именно так

00:07:07
- Вспомогательная функция

00:09:10 - Заключение

MQL5 Первый робот. Готовим Include-файл. Часть 2
MQL5 Первый робот. Готовим Include-файл. Часть 2
  • 2022.01.28
  • www.youtube.com
Продолжаем изучать MQL5 на примере написания торгового эксперта. В этом видео-уроке рассматриваем функции, которые написаны для скрипта из предыдущих частей ...
 

MQL5 Первый робот. Готовим Include-файл. Окончание


MQL5 Первый робот. Готовим Include-файл. Окончание

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

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

Приступим. Для начала нужно развести между собой механизмы открытия рыночных позиций и установки отложенных ордеров. Сейчас я переделаю уже существующие функции, но в описании под видео выложу исходники, в которых существующие на данный момент функции будут просто закомментированы, чтобы вы могли наглядно оценить разницу в подходе. А на экране я сейчас просто удалю лишние операции из функций open и bypass. Также не нужно забывать о том, что некоторые формальные параметры этой функции тоже перестали быть нужными, и от них нужно избавиться. И не забудем подкорректировать вывод в журнал на случай возникновения ошибок.

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

Теперь заполнять запрос мы будем тремя условными группами. Условные они потому, что порядок заполнения полей запроса важен только тогда, когда вы используете значение одного поля при вычислении значений другого поля. Сами же группы будут формироваться по принципу обязательности явного заполнения того или иного поля торгового запроса.

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

Так что в режиме TRADE_EXECUTION_INSTANT или TRADE_EXECUTION_MARKET достаточно заполнить всего 5 полей в торговом запросе. Эти поля обязательны и в оставшихся режимах исполнения сделок.

Вот они и войдут в первую группу: action, symbol, type, volume и filling. То есть, требуемое действие, символ, тип ордера, объем входа в позицию и политику заполнения объема входа.

Во вторую группу войдут поля, которые дополнительно нужно заполнить, если сделки на торговом инструменте исполняются в режиме TRADE_EXECUTION_INSTANT или TRADE_EXECUTION_REQUEST. Здесь это поля price, sl, и tp. И, соответственно, параметры slippage и magic тоже должны быть в обязательном порядке.

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

Поля comment и magic - необязательные, в любом случае они войдут в третью группу.

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

Не забываем выводить в журнал, чтобы не потерять информацию о работе нашего робота.

Теперь можно заняться установкой отложенных ордеров. За основу возьмем функцию открытия позиций и внесем нужные модификации. В первую очередь модифицируем имя самой функции. Значение проскальзывания нам теперь не нужно, так что соответствующий параметр можно удалить. Но в обязательном порядке понадобятся такие параметры как уровень установки самого ордера и уровень его стопа, а также допустимая проскальзывание при совершении сделки. Перед заполнением поля price обязательно запрашиваем нужную текущую цену торгового инструмента. Для сделки на покупку это цена Ask. А вот поле direction теперь нужно заполнить в любом случае, так что никаких "if". Поскольку у нашей функции уже не нужно обращаться к отложенным ордерам, то поле magic и соответствующий параметр magic можно убрать. Этот момент я тоже закомментирую, но вы можете оставить его, чтобы функция была более универсальной.

Обязательные поля второй группы - price, sl и tp. И, соответственно, параметры slippage и magic. Необязательные поля - comment и magic.

Остается только собрать нашу функцию и проверить ее работу.

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

void OpenOrder(
    int action, 
    string symbol, 
    int volume, 
    int filling,
    double price,
    double sl,
    double tp,
    datetime expiration
) 
{
    ...
}
Теперь, по поводу уровней стоп-приказов отложенного ордера, возникает такой же вопрос, как и с модификациями уже открытых позиций. В таком виде их передавать в функцию? Я склоняюсь к виду "расстояния" в пунктах. Проверку ограничения стоп-лосс проходить все равно придется, и проверка это удобнее реализуется именно в виде пунктов, так что параметры стоп-лосс и тейк-профит будут иметь тип int. Нулевое значение какого-то из этих параметров будет говорить о том, что соответствующий стоп-приказ использоваться не будет.

Приступим собственно к работе с функцией. В перво-наперво, поле request.action у нас не сделка, а отложенный ордер. Далее, аналогично открытию позиции, имя торгового инструмента и объем входа. Пропали request.type, которая отвечает за политику заполнения объема входа. Поговорим чуть дальше, а пока просто оставим как есть. А вот пропал request.deviation. Поговорим прямо сейчас.

Теперь тип ордера будет напрямую зависеть от положения требуемой точки входа относительно текущих рыночных цен. Так что для начала заполним поле request.price, установив тем самым точку срабатывания отложенного ордера. Не забываем прогнать значения параметра Ask через функцию NormalizeDouble(). Для этого придется объявить переменную digits, которая и будет отвечать за точность значения поля request.price, а также за точность полей стоп-приказов. И так, имея точку входа на графике, можно определиться с типом требуемого ордера. Узнаем у терминала текущую цену Ask, так как именно по этой цене будет активирован наш отложенный ордер, и смотрим, где находится уровень установки ордера относительно этой цены. Если выше, ордер будет типа стоп, если ниже, ордер будет типа лимит.

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

if(stopLossPips >= 0)
{
    ...
}
Но я оставлю по-своему.

Далее определяемся со стоп-приказами. Тут все просто. Если значение соответствующего параметра больше нуля, то проверяем это значение на соответствие ограничению 100 пунктов. Если проверку не проходим, корректируем значение и вычисляем сам уровень стоп-приказа. Для этого нужно получить значение Point указанного торгового инструмента в виде служебной переменной. Если значение формального параметра равно нулю или меньше, то стоп-приказ не выставляем, и соответствующему полю запроса явно присваиваем 0. Аналогично поступим с другим стоп-приказом, не забывая изменить необходимые значения и знаки арифметических действий.

А вот проскальзывание при установке отложенного ордера нам не нужно, так что поле request.deviation смело удаляем.

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

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

И если вы хотите ограничить время жизни отложенного ордера, нужно обязательно заполнить поле запроса request.type и возможных значений всего 4. 1. ORDER_TIME_GTC. Это значение поля request.type говорит о том, что отложенный ордер будет существовать до отмены, пользователем, неважно, человеком это или программа. Если поле request.type не указано явно, по умолчанию время жизни отложенного ордера выставляется как ORDER_TIME_GTC. На форекс, если вы не хотите ограничивать время жизни вашего ордера, поле запроса request.type можно не использовать вообще.

Но бывают случаи, когда это поле должно быть явно заполнено, даже если время жизни ордера не будет ограничено. Например, если вы хотите продавать вашего робота через Маркет сайта "Прямо" или "5-Ком". Также есть торговые площадки, которые прямо запрещают использование отложенных ордеров без ограничения времени их жизни. Очевидный пример - фьючерсы. В сути, все торговые инструменты на рынке фьючерсов - это контракты, у которых есть строка истечения. Логично, что отложенный ордер на таких инструментах не должен жить дольше, чем живет сам контракт. И в этом случае нужно использовать иное значение поля request.type. Например, ORDER_TIME_DAY. При таком значении поля request.type, отложенный ордер будет жить до конца текущего дня, или до 23 часов 59 минут 59 секунд текущей даты. Это удобно в том случае, если торговая площадка взымает комиссию за перенос отложенных ордеров на следующий торговый день. Такие жалобы тоже есть.

Еще можно указать точное время экспирации отложенного ордера. Для этого полю request.type нужно присвоить значение ORDER_TIME_SPECIFIED. И теперь, по формальному параметру expiration_time, вот этому полю в теории и следует присвоить значение.

И последнее возможное значение поля request.type - ORDER_TIME_SPECIFIED_DAY. Ну, за спешу, фиг с ним, с правильным произношением слов. И ему в пару тоже обязательно поле expiration_time. Но теперь достаточно указать только конкретный день, в 23 часа 59 минут 59 секунд которого ордер будет отменен. Если указанное время не торговое, то отмена состоится в ближайший доступный для торговых операций момент.

Это правило, кстати, распространяется на все виды экспирации, кроме, естественно, ORDER_TIME_GTC. Также нужно упомянуть о том, что все расчеты происходят по времени торгового сервера. И немаловажен тот факт, что могут поддерживаться несколько типов экспирации одновременно. Естественно, для одного ордера можно использовать только один вариант из списка разрешенных.

Как уже упоминалось ранее, фьючерсы работают с экспирацией долларов отмены. Но как правило, всегда есть возможность установить экспирацию по методу ORDER_TIME_SPECIFIED.

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

Теперь пришло время вернуться к кулера квесту. Пилинг и разобраться с политиками заполнения объема будущей сделки. Ситуация с ними отчасти напоминает ситуацию с экспирацией, но на самом деле все немного проще. Сложности такие же, как и с экспирацией, а именно: брокер может разрешать несколько из имеющихся политик работы с объемами. Напоминаю, их всего три: ORDER_FILLING_FOK, ORDER_FILLING_IOC, и ORDER_FILLING_RETURN. Можно указать любую из доступных, но в случае отложенного ордера для поля request.filling всегда можно использовать значение ORDER_FILLING_RETURN. Именно такой формат установки отложенных ордеров рекомендует документация по MQL5.

Используя значение ORDER_FILLING_RETURN, мы автоматически соглашаемся на ту политику работы с объемами, которую предлагает брокер. Даже если сама политика ORDER_FILLING_RETURN не поддерживается, но не запрещено указать любую из доступных. Для применения в нашем случае, не грех поступить по инструкции с заполнением торгового запроса.

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

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

Обязательно нужно проверить код на синтаксические ошибки при помощи компиляции. Теперь стоит ненадолго вернуться к функции PositionSetStopLoss, которая модифицирует стоп-приказы открытых позиций. Дело в том, что проверка на соблюдение ограничения freeze level ловил неполное. Сейчас мы ее дополнили, поскольку программа с использованием этой функции будет работать в том числе и на реальном рынке, то опираться при расчетах только на цену открытия позиции не совсем корректно. Если стоп-приказы большие, то никаких проблем не возникает. Но если они не большие, то могут начать вылезать ошибки. Так что нужно внести еще одну проверку уровней стоп-приказов, а именно, с учетом цены по которой позиция будет закрываться. Для покупок это цена bid. Теперь, если расстояние между ценой закрытия позиции и вычисленным ранее уровнем стоп-приказа не соответствует ограничению freeze level, то откладываем расстояние стоп-приказа от цены закрытия, а не от цены открытия. И так со всеми остальными стоп-приказами, не забывая о знаках арифметических действий и нужных ценах закрытия позиции.

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


Содержание:

00:00:00 - Вступление

00:00:32 - Выделяем механизм открытия позиций

00:01:25
- Меняем порядок формирования торгового приказа

00:02:00
- Режимы исполнения сделок

00:02:45 - Первая группа

00:03:08 - Вторая группа

00:04:49
- Третья группа

00:05:13 - Реализация механизма установки отложенных ордеров

00:05:55
- Оформление параметров функции

00:09:14
- Экспирация отложенного ордера

00:13:44 - Политики заполнения объема в отложенных ордерах

00:15:48
- Еще немного про уровни FREEZE_LEVEL при модификации позиции

00:17:03
- Самостоятельная работа

00:17:20 - Заключение

MQL5 Первый робот. Готовим Include-файл. Окончание
MQL5 Первый робот. Готовим Include-файл. Окончание
  • 2022.02.21
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта. В этом видео-уроке заканчиваем совершенствовать функции в Include файл...
 

MQL5 Первый робот. Открываем позиции


MQL5 Первый робот. Открываем позиции

Всем привет! Продолжаем изучать MQL5 на примере создания торгового эксперта, и пришло время научить нашего робота входить в рынок и выходить из него. Пока что посредством установки стоп-приказа. Также обязательно нужно объяснить нашему роботу, на что он должен делать в том случае, если при попытке зайти в рынок или установить стоп-приказ что-то пошло не так. Это и есть тема данного видеоурока.

Итак, отлавливать свой сигнал робот уже обучен и даже может это доказать, но пока только в журнале. А нам нужны более, так сказать, осязаемые результаты. Так что вместо вызова функции printf будем вызывать функцию открытия позиций в зависимости от того, какой сигнал получен - на покупку или на продажу.

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

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

Итак, если переменная position меньше или равна нулю, нужно как можно быстрее обновить переменные программного окружения. Другими словами, нужно получить самые свежие рыночные данные. Наиболее простой и эффективный способ это сделать - дождаться нового тика от торгового сервера. Следовательно, нужно немедленно прекратить работу функций OnTick() и OnTimer(), поскольку функция OnTick() это типа вечный цикл, то достаточно просто оператора return. Но у нас и есть механизм определения новой свечи, и время последней открытой свечи уже зафиксировано значением глобальной переменной t. И если ее значение оставить как есть, то робот будет с полным основанием считать, что она вышла пришла на уже обработанной свече, и нечего делать до появления следующей. Не будет. Поэтому нужно обновить переменную t перед вызовом оператора Retina. Вот и весь механизм.

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

Поскольку при формировании торговой идеи для первого робота ничего подобное ситуации не сказано, признаем победу и идем дальше.

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

Для начала, не помешает получить цену открытия только что открытой позиции. Значение ticket позиции уже известен, так что создаем переменную для хранения цены открытия позиции и присваиваем ей значение свойства PositionGetDouble(POSITION_PRICE_OPEN). Не забываем предварительно загрузить данные позиции в память функцией PositionSelectByTicket(ticket).

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

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

Индексация всех массивов значений изначально осуществляется в виде time-series, но функция iLowest, как и ее коллега iHighest, которая ищет максимальное значение на тех же условиях, возвращает только индекс свечи, на которой обнаружено искомое значение.

Так что используем результат работы этой функции прямо в вызове функции iLow, а результат вызова функции iLowest в свою очередь присвоим временной переменной типа double. Остается только получить расстояние между уровнями открытия и Low в пунктах. Для этого у нас предусмотрена функция SymbolInfoInteger, результаты ее работы присваиваем переменной stol. Не забываем прибавить к полученному результату значение переменной настройки FilterStol.

Все намного проще с TakeProfit. По условиям задачи, нам нужна цена открытия первой свечи в сигнальной формации. С этим нам поможет функция iOpen. Тут можно опять использовать служебную переменную exit, присвоив ей новое значение, и опять функция SymbolInfoDouble. Все нужные расстояния получены.

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

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

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

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

Содержание:

00:00:00 - Введение

00:00:28
- Открытие позиции и действия на случай ошибки

00:03:24
- Вычисляем размеры стоп-приказов

00:04:04 - Функция iLowest()

00:05:06 - Применение функции iLowest()

00:06:07 - Выставляем стоп-приказы и действия на случай ошибки

00:06:36
- Сделка в противоположном направлении

00:07:00 - Заключение и план на следующий урок

MQL5 Первый робот. Открываем позиции
MQL5 Первый робот. Открываем позиции
  • 2022.02.26
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта. Пришла пора начать применять наш Include-файл в торговом роботе. В это...
 

MQL5 Первый робот. Считаем прибыль открытой позиции


MQL5 Первый робот. Считаем прибыль открытой позиции

Привет! Продолжаем написание первого торгового робота на языке MQL5. В прошлом видео мы научили нашего робота входить в рынок, а сейчас будем учить его разгребать результаты этого входа, в частности займемся подсчетом прибыли по отдельно взятой позиции. Этот механизм пригодится как нашему роботу, так и многим другим MQL5 программам, так что придется написать еще одну функцию. Какая неожиданность! Функция будет возвращать значение типа double и назовем ее просто и незатейливо "Прибыль".

У нее будет один формальный параметр, и покажу почему так, смотрите дальше. Параметр наш будет типа int и сообщит функции ticket позиции, прибыль по которой нужно посчитать. Для начала займемся рутиной, объявим переменную для хранения окончательного значения прибыли типа double, имя "total_profit". Тут же возвращаем эту переменную, чтобы не вызывать ошибки при промежуточных компиляциях.

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

Все просто, загружаем в память данные о позиции путем вызова функции PositionSelectByTicket. Далее вызываем функцию PositionGetDouble для переменной "profit", вызываем функцию PositionGetDouble для переменной "swap" и не забываем про объем. Переменную "volume" присваиваем результат вызова функции PositionGetDouble с параметром POSITION_VOLUME.

А вот с комиссиями все не так просто. Беда в том, что такого свойства, как комиссия, у позиции нет. И нет и все тут комиссии в экосистеме MetaTrader 5 разные. Казалось бы, что тут такого, тикет позиции известен, просматриваем все и сделки по этой позиции и считаем комиссию нарастающим итогом. Но они очень удобны, особенно если сравнивать с MetaTrader 4.

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

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

Брокеры относятся к нашему депозиту вполне однозначно. План действий такой: вытащить из торговой истории все сделки, имеющие отношение к нашей позиции. Для этого используем функцию HistorySelectByPosition. Передаем функции ticket позиции, и она загрузит в память списки всех ордеров и всех сделок, которые связаны с указанной при помощи ticket позиции.

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

Данные эти можно загрузить в память двумя путями: один мы только что видели, это функция HistorySelectByPosition, она загружает в память торговую историю по конкретной позиции. Во второй путь - загрузить историю за определенный период времени. Это делается функцией HistorySelect, при вызове этой функции в нее нужно передавать 2 аргумента типа datetime - начала исторического периода и его окончания.

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

Выбираем сделку в функции HistoryDealGetTicket, ее индекс в списке. Но функцию HistoryDealGetDouble не используем, потому что результаты и успешного выполнения и неудачи отброшенного вызова будут применены, результаты работы функции HistorySelectByPosition.

И вот теперь, нарастающим итогом, будем увеличивать переменные "total_profit", "swap" и "commission" при помощи вызова функции HistoryDealGetDouble с указанием соответствующего свойства. Если мы таким образом пройдемся по всем сделкам, которые приписаны указанной позиции, наша функция увидит и посчитает суммы, на которые изменился баланс торгового счета при изменении состояния самой позиции.

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

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

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

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

Теперь функция "profit" возвращает значение переменной "total_profit", которая показывает, на сколько изменится баланс торгового счета при закрытии позиции.

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

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

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

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

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

В описании под видео будет исходный код и файл с текстовым описанием кода. Там же можно найти ссылки на канал в телеграм и много другого интересного. Не поленитесь посмотреть. Всем пока!

Содержание:

00:00:00 - Введение

00:00:27
- Начинаем писать функцию

00:01:00
- Считаем прибыль открытой части позиции

00:01:46
- Особенности расчета комиссии и прибыли у Неттиноговых сделок

00:03:26
- Считаем значения из истории позиции

00:05:56 - Комиссия с закрывающей сделки

00:07:06 - Где узнать сумму комиссии

00:10:14 - Окончательное значение прибыли

00:11:23
- Если промежуточные итоги не нужны

00:11:41
- Краткое резюме

MQL5 Первый робот. Считаем прибыль открытой позиции
MQL5 Первый робот. Считаем прибыль открытой позиции
  • 2022.03.09
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта.В этом видео-уроке разберем такой важный и приятный момент, как подсчет...
 

MQL5 Первый робот. Закрытие позиций


MQL5 Первый робот. Закрытие позиций

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

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

Затем заполним переменные для имени торгового инструмента, идентификатора робота, объема позиции и направления. Далее начинаем формировать торговый приказ в зависимости от направления позиции. Для покупок это будет цена bid и сделка sell, для продаж - цена ask и сделка buy.

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

На следующем уроке мы применим эту функцию, а также функцию подсчета прибыли по открытой позиции, которую мы написали на предыдущем занятии.

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

Не забывайте ставить палец вверх, подписываться на канал и комментировать свое мнение об увиденном и услышанном. Всем пока!

MQL5 Первый робот. Закрытие позиций
MQL5 Первый робот. Закрытие позиций
  • 2022.03.22
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта.В этом видео-уроке напишем функцию, с помощьюкоторой наш первый робот с...
 

MQL5 Первый робот. Завершаем основную логику


MQL5 Первый робот. Завершаем основную логику

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

Для начала определим уникальный тикет позиции и время ее открытия, инициализируем переменные нулями. Затем загрузим данные позиции и присвоим переменным значения свойств этой позиции.

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

Затем проверяем прибыль позиции и, если она соответствует условиям закрытия, закрываем позицию с контролем на ошибки.

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

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

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

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

Для этого можно использовать переменную типа bool, называемую isCaused, которой сразу присвоить значение false. В блоке закрытия позиции, если функция ClosePosition вернула true, присвоим переменной isCaused значение true. Таким образом, все дальнейшие действия с позицией будут проводиться только в том случае, если переменная isCaused имеет значение true.

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

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

Ошибки при написании программ бывают синтаксическими и семантическими.

Синтаксические ошибки возникают при нарушении правил написания кода программы, такие как забытая точка с запятой, непарные фигурные или круглые скобки и тому подобное. Обычно они не являются серьезной проблемой, так как при их наличии программу собрать не получится, и вы получите соответствующее предупреждение от компилятора. В более продвинутых средах разработки, таких как Visual Studio от Microsoft, вам не только сразу же укажут место, где возникла ошибка, но могут даже предложить варианты ее исправления.

С семантическими ошибками сложнее. Они возникают, когда код программы написан синтаксически верно, но программа делает не то, что задумано. Это может проявляться нерегулярно, например, в 999 случаях все в порядке, а в 1000 происходит что-то непонятное. Это может привести к непредсказуемому поведению программы, что может быть критически важным в финансовых рынках.

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

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

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

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

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

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

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

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

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

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

Также, чтобы избежать проблем с изменением цены торгового инструмента за время открытия позиции, можно использовать относительные значения цены открытия свечи и текущей рыночной цены закрытия позиции.

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

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

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

Далее нам нужна такая же проверка на закрытие позиции, как и в случае со стоп-приказами. Только теперь мы проверяем, находится ли цена закрытия выше цены открытия позиции. Нам не нужно ничего делать с переменной is_caused.

Далее проделываем все операции для стоп-приказов для оппозиции на продажу. Не забываем внести правки, где нужно, в функции для определения цены закрытия позиции, стоп-приказа и тейк-профита.

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

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

На данный момент, если было познавательно и интересно, ставьте палец вверх, не забывайте подписаться на канал и нажать на колокол, если хотите обсудить увиденное и услышанное, или если у вас возникнут вопросы. Вашим услугам комментарии и телеграм-канал. Все ссылки находятся под видео, там же вы сможете скачать документированные исходники робота и include файла. До новых встреч, с прошедшим праздником и всем пока!

MQL5 Первый робот. Завершаем основную логику
MQL5 Первый робот. Завершаем основную логику
  • 2022.04.25
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта. В этом видео-уроке закончим написание основной логики торговли, а такж...
 

MQL5 Первый робот. Пошаговый разбор базовой логики программы


MQL5 Первый робот. Пошаговый разбор базовой логики программы

Видео закончилось на моменте, когда мы завершили написание основной торговой и сопроводительной логики нашего первого робота. Сегодня план действий такой: для начала пройдемся по всему коду программы и переведем язык программного кода на человеческий, попутно объяснив работу программы простыми словами. Кое-что подкорректируем и исправим ошибку из предыдущего видео.

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

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

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

Переменная "n" (фильтр) предотвращает случайное срабатывание стоп-приказа. Этот параметр необязательный, но его можно использовать для управления срабатыванием стоп-приказов.

Переменная "slippage" указывает максимально допустимое проскальзывание при проведении торговой операции. Это позволяет парировать малые движения цен котировок во время обработки торгового приказа.

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

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

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

Функция "OnInit" вызывается однажды, когда программа впервые запускается на графике котировки торгового инструмента. Если вы меняете входящие настройки уже запущенного эксперта без удаления его с графика, это тоже считается первым запуском программы. Также, смена таймфрейма графика или переход на другой торговый счет вызовет эту функцию.

Функция "OnTick" вызывается каждый раз, когда от торгового сервера приходит новый ценовой тип торгового инструмента на графике. Пока эта функция не завершит свою работу, все прочие входящие типа этому торговому инструменту будут проигнорированы.

Функция "OnTick" проверяет, соответствует ли значение переменной "t" значению, которое возвращает функция "iTime" в зависимости от аргументов, указанных при её вызове. Функция "iTime" возвращает время открытия текущей свечи на графике. Если текущая свеча еще не закрыта, она всегда имеет индекс 0 в массиве свечей на графике. Если значение переменной "t" и возвращаемое функцией "iTime" не совпадают, программа интерпретирует это как появление новой свечи на графике.

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

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

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

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

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

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

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

Для этого используем цикл, который перебирает свечи с индексами от 1 до 3. Инициализируем переменную "signalFlag" значением 0. В первой итерации цикла анализируем свечу с индексом 1. Если цена открытия свечи больше цены ее закрытия, значит свеча падающая и у нас есть сигнал на покупку. Присваиваем переменной "signalFlag" соответствующее значение. Если цена открытия меньше цены закрытия, значит свеча растущая и у нас есть сигнал на продажу. Присваиваем переменной "signalFlag" значение 1. В любом другом случае (если цена открытия совпадает с ценой закрытия), сигнала нет и переменная "signalFlag" остается равной 0.

После анализа всех свечей, выходим из цикла и переходим к блоку открытия позиции. Действия в этом блоке зависят от значения переменной "signalFlag". Если значение "signalFlag" больше 0, это означает, что получен сигнал на покупку или продажу. Вызываем соответствующую функцию для открытия позиции, и результат ее работы сохраняем в переменную "position". Если значение переменной "position" больше нуля, это означает, что позиция успешно открылась. Если значение переменной "position" равно нулю или меньше, это означает, что произошла ошибка при открытии позиции, и ее нет на торговом счете. Этот блок кода отвечает за обработку таких случаев.

Перезапускать всю логику робота, как если бы робот был только что установлен на график, делается следующим образом. Сначала сбрасываем значение глобальной переменной "т" на нулевое. Это означает, что следующий тик, который выйдет к торговому серверу, будет считаться первым тиком новой сессии. Затем выполняется оператор "return". Напомню, что этот оператор прекращает дальнейшее выполнение функций в теле, из которого был вызван, и возвращает результат работы функции в то место, откуда оно было вызвано.

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

Функция "antique" не имеет возвращаемого значения, определено типом "void", поэтому оператор "return" вызывается без дополнительных аргументов. Даже здесь перезагрузка всего продолжается.

Однако, идем дальше. Если функция открытия позиции отработала корректно, нужно выставить стоп-приказы для только что открытой позиции. Этим робот и будет заниматься в блоке кода "else".

Перед операцией модификации позиции, нам понадобится реальная цена открытия позиции. Для ее хранения мы создаем переменную "n3" и пока что инициализируем ее нулем. Дело в том, что нельзя просто так взять и считать свойства позиции или других объектов на торговом счете, просто запросив эти свойства у терминала. Сначала нужно указать программе, какой именно объект мы будем препарировать. Для этого мы вызываем функцию "PositionSelect", которая определяет объект на торговом счете по его уникальному "Ticket" и только после этого можем присвоить переменной "n3" нужное значение.

Функция "PositionSelect" возвращает значение типа "bool". Истину она возвращает в случае успеха копирования данных, а в случае неудачи - ложь. В нашем коде вызов "PositionSelect" не используется с проверкой возвращаемого значения, так как мы уже убедились в корректности открытия позиции на предыдущем этапе, до этого блока кода выполнение программы попросту не дойдет.

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

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

Таким образом, мы присваиваем переменной "r" значение, возвращаемое функцией iOpen. Эта функция аналогична функции "орлов", но работает с тикетом. Также есть аналогичные функции iHigh и iLow, которые ищут наибольшее и наименьшее значение соответственно. В остальном код остается неизменным.

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

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

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

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

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

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

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

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

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

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

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

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

Для этого нам понадобится индекс свечей на графике закрытие которого сформировало торговый сигнал. А значит, именно с опорой на эту свечу и еще две закрытые непосредственно перед ней должны были выставляться стоп-приказы при открытии позиции. Переменная "индекс" как раз нужна для того, чтобы запомнить это значение. Также, поскольку неизвестно, насколько глубоко нам придется уйти в историю, нужно узнать, сколько всего свечей доступно роботу для анализа на графике котировок. Для этого вызываем функцию iBars, которая возвращает количество свечей на графике указанного торгового инструмента и требуемого таймфрейма, и сохраняем полученный результат в соответствующую переменную. Сразу же можно проверить корректность возвращаемого функцией iBars значения.

Если при ее работе возникла какая-то ошибка, то она вернет значение -1, нулевое значение нас тоже не устраивает. Так что, если значение в переменной TotalBars бессмысленно в контексте логики выполнения программы, используем уже знакомый нам прием перезапуска с обнулением переменных. И если все переменные TotalBars в порядке, нужно перебрать по порядку все свечи на графике в поиске той самой, чтобы закрыть позицию. Для этого используем цикл. В нем переменная "i" отвечает за индекс свечи, увеличивая ее на один при каждой итерации цикла, мы будем идти в глубь истории котировки торгового инструмента, пока свечи на истории не закончатся. На самом деле, так глубока ли есть в историю не понадобится. И в этом месте мы подошли к той самой семантической ошибке, о которой я говорил в самом начале.

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

Я хотел сразу же искать последнюю сигнальную свечу, а нужно было делать это в два этапа. Найти свечу, на которой открылись позиции, а уже от нее искать сигнальную. Беда в том, что при вот таком условии переменная "индекс" всегда будет иметь значение 0. Ведь здесь мы пытаемся игнорировать свечи, формирование которых началось до открытия позиции, и таких свечей при данных обстоятельствах не будет. А значит, всегда будет работать условие "else", и стоп-приказы будут рассчитываться исходя из параметров последних трех свечей на графике, включая еще незакрывшиеся.

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

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

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

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

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

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

Теперь, в два этапа, вычислим расстояние до уровня стоп-лосс. Сначала найдем сам уровень, а затем посчитаем количество пунктов до него от цены открытия. Тут все аналогично расчету стоп-лосса для открытия позиций, стоит только разницей, что теперь отсчет массива свечей для функции вычисления ведется не со свечи с индексом 1, а со свечи с индексом "индекс".

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

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

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

Далее, при модификации позиции перед ее закрытием, обязательно проверяем счет на наличие позиции, уже при помощи встроенного функционала "md5". Эта проверка позволяет избежать необходимости проверки переменных "isClosed" и "closet" после попытки закрытия позиции по итогам расчета тейк-профита.

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

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


Тайм-коды, они же план занятия:

00:00:00 - Приветствие, вступление и план урока

00:00:33 - Небольшая просьба

00:00:55 - Препроцессорные директивы

00:01:37 - Внешние переменные

00:05:36 - Статические внешние переменные

00:06:25 - Самая важная глобальная переменная

00:06:53 - Функция OnInit.

00:07:49 - Начало работы и еще раз о функции OnTick

00:09:27 - Контроль открытия новой свечи

00:10:00 - Предпочтительная очередность действий торгового эксперта

00:10:28 - Определяем наличие позиций

00:12:06 - Действия при отсутствии позиции

00:12:27 - Переменная-флаг и зачем она нужна

00:13:00 - Организация перебора свечей в цикле

00:14:18 - Определение направления возможного сигнала

00:15:16 - Подтверждаем (или нет) сигнал

00:16:11 - Зависимость работы программы от значения переменной-флага

00:16:30 - Открытие позиции

00:17:20 - Что делать с ошибками торговых функций

00:18:42 - Покупки. Установка стоп-приказов. Подготовка

00:21:41 - Покупки. Установка стоп-приказов. Ценовой уровень стоп-лосс и необходимые функции

00:23:12 - Покупки. Установка стоп-приказов. Расчет величины стоп-лосс в пунктах

00:23:31 - Покупки. Установка стоп-приказов. Тейк-профит

00:24:03 - Покупки. Установка стоп-приказов. Модификация позиции

00:24:25 - Особенности установки стоп-приказов для Продаж.

00:25:00 - Обработка открытой позиции. Начало

00:25:24 - Подготовительный этап. И почему именно так

00:26:34 - Кеширование данных и организация этого процесса

00:28:03 - Очередность действий при обработке открытой позиции

00:29:22 - Функционал закрытия позиции. Условия

00:29:39 - Функционал закрытия позиции. Пришло ли время?

00:30:35 - Функционал закрытия позиции. Достаточно ли заработали?

00:31:17 - Функционал закрытия позиции. Само закрытие и связанные с этим нюансы

00:32:07 - Проверка правильности установки стоп-приказов

00:32:25 - Поиск опорных точек для выставления стоп-приказов

00:32:55 - Сколько свечей доступны для анализа торговой истории. Или насколько глубока кроличья нора?

00:33:43 - Ищем торговый сигнал. Перебор свечей в истории котировки

00:34:10 - Найденная ошибка. В чем причина

00:35:21 - Исправляем ошибку

00:36:02 - Как отловить такую ошибку, или о пользе старых программ. С демонстрацией

00:37:17 - Поиск торгового сигнала здорового человека

00:37:55 - Готовимся установить стоп-лосс

00:38:25 - Покупки. Расчет уровня и расстояния стоп-лосс и его особенности в ключе работы с открытой позицией

00:39:21 - Покупки. Если уровень стоп-лосс уже достигли

00:40:38 - Покупки. Расчет уровня и расстояния тейк-профит и его особенности в ключе работы с открытой позицией

00:40:51 - Покупки. Поиск опорной свечи для уровня тейк-профит

00:42:01 - Особенности модификации стоп-приказов для Продаж.

00:42:12 - Модификация ранее открытой позиции и связанные с этим особенности

00:42:40 - Завершение работы программы и функция OnDeinit

00:43:09 - Подводим итог

00:44:03 - О пользе безальтернативных ветвлений

00:45:08 - Заключение и план на следующее видео

MQL5 Первый робот. Пошаговый разбор базовой логики программы
MQL5 Первый робот. Пошаговый разбор базовой логики программы
  • 2022.06.10
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта. Этот видео-урок полностью посвящен пошаговому разбору готовой, на данн...
 

MQL5 Первый робот. Расширяем возможности


MQL5 Первый робот. Расширяем возможности

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

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

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

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

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

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

Теперь в коде нашего робота можно применить эту функцию. Вместо переменных "стоп" и "тейк" используем переменные "sloss" и "ttake" в соответствии с направлением позиции.

Для функции "calc_stop_loss" началом массива свечей будет переменная "start_index", а количество свечей для поиска наименьшего значения будет примерно равно "and_index - start_index".

Все, функция готова к использованию! Теперь наша программа будет рассчитывать стоп приказы и тейк профиты без ограничений заранее заданными константами, а значения подставляться при оптимизации в тестере стратегий.

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

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

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

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

То же самое делаем и при модификации позиции обратного направления.

Со входом в рынок разобрались, теперь модификацией ранее открытых позиций будет чуть сложнее, но ненамного.

До обнаружения переменной "индекс" менять ничего не будем, только удалим уже не нужные комментарии.

Теперь сразу вычислим переменную "and_index". Также понадобятся переменные "sltp", "stop_price", "pos_type", их не трогаем, все остальное до вызова функции модификации позиции комментируем, и вместо всего этого добра вызываем нашу новую функцию.

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

Но для начала удалим из комментариев все лишнее.

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

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

Теперь повторяем то же самое для продаж. Можно снимать комментарий и компилировать.

Удалим из кода оставшиеся комментарии и посмотрим на количество строк в функции "antique". Теперь их 227, а было 278. Неплохая экономия букв, и код стал более читаемым.

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

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

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

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

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

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

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

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

Поскольку наша функция имеет тип void, никакой return ей не нужен. Также, поскольку мы уже все посчитали заранее, переменной "account" уже тоже не нужно. Однако сама переменная типа int нам понадобится, так что просто задаем ей подходящий по смыслу идентификатор.

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

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

И вот, когда функция найдет позицию, свойства которой совпадают с указанными, вот тут и переходим к нашему массиву. Сохраняем тикет текущей позиции в элемент массива с индексом "index", а затем увеличиваем значение переменной "index" на один, тем самым, когда наш цикл найдет следующую подходящую позицию, то сохранит ее тикет уже в следующий элемент массива.

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

Также, чтобы избежать ошибки выхода за пределы массива, мы следим за значением переменной "index", и как только ее значение станет равным значению размера массива, останавливаем перебор позиций.

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

Теперь при успешном закрытии позиции, вместо того чтобы отслеживать значение из "clause", просто используем оператор "continue", принудительно отправляющий цикл на следующую итерацию.

При таком раскладе, проверка переменной "is_paused" тоже не нужна, потому что существует оператор "continue", который при закрытии позиции до этого места цикл попросту не доберется.

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

Теперь наш робот готов работать с хеджинговыми счетами без проблем.

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

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

Например, для переменной "lot" недопустимы отрицательные значения или значения не соответствующие ограничениям, которые установил брокер для объемов торговых операций.

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

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

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

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

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

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

И вот тут однозначных приемов нет, очень многое зависит от того, как именно конкретная внешняя переменная влияет на программу в целом.

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

Например, если переменной "lot" присвоить некорректные значения, то робот запустится, но при этом ничего не откроет, так как сервер не примет неверное значение объема рыночной операции.

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

И так, проблему обрисовали, теперь с ней разберемся. Без правильного значения переменной "lot" никак, так что, учитывая всю логику программы, будет достаточно создать глобальную переменную "lot" и затем функции "lot_norm", присвоить этой переменной значение при помощи функции "lot_norm".

Что это за функция и зачем она нужна, я рассказывал и показывал в одном из видео цикла приемов и примеров. А как она попала в include файл, догадаетесь сами.

Теперь, оба раза, где у нас в ходе кода используется внешняя переменная "lot", мы вместо нее будем использовать глобальную переменную "lot". Пока что с ней можно закончить, однако мы к ней вернемся в разрезе мульти-валютного алгоритма позже. А сейчас разберемся с переменными "signal_candles" и "target_profile".

Конечно, можно поступить по аналогии с переменной "lot", задав некое значение, ниже которого значение переменной быть не может, примерно вот так:

if (signal_candles < 5)
{
    signal_candles = 5;
}
Но я предпочитаю в данном случае действовать иначе:

if (signal_candles < 5)
{
    Console.WriteLine("Некорректное значение переменной signal_candles! Введите значение не меньше 5.");
    Environment.Exit(0);
}
То есть, принудительно завершить выполнение программы в случае логически неправильного значения переменной "signal_candles".

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

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

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

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

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

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

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

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


Тайм-коды, они же содержание:

00:00:00 - Приветствие и план урока

00:00:44 - Избавляемся от констант в торговом алгоритме

00:01:26 - Добавляем в код новые переменные

00:02:49 - Избавляемся от повторяющихся блоков кода

00:03:39 - Функция расчета стоп-приказов

00:06:42 - Применяем новую функцию. Блок открытия позиций

00:07:18 - Применяем новую функцию. Блок модификации позиций

00:07:58 - Применяем новую функцию. Сопутствующие изменения

00:09:20 - Результат применения новой функции

00:10:52 - Принуждение к сожительству

00:11:15 - Немного о функции PositionSelect()

00:12:06 - Подготовка к сожительству

00:12:41 - Функционал сожительства

00:14:47 - Выход за пределы массива

00:16:40 - Реализация сожительства и побочные эффекты

00:18:17 - Контроль пользовательского ввода. Причины и методы

00:20:59 - Виды контроля пользовательского ввода

00:21:56 - Корректировка значения настройки

00:22:41 - Принудительное завершение программы из-за неверных настроек

00:23:07 - В чем опасность корректировки / подмены значения настроек

00:23:41 - Итоговая картина входящих настроек

00:24:23 - Заключение

MQL5 Первый робот. Расширяем возможности
MQL5 Первый робот. Расширяем возможности
  • 2022.07.28
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта. В этом видео-уроке наш готовый алгоритм обрастет новым функционалом бе...
 

MQL5 Первый робот. Индикаторный фильтр


MQL5 Первый робот. Индикаторный фильтр

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

Сразу нужно сказать о некоторых деталях:

  1. Мы будем использовать один из индикаторов, который идет в составе торгового терминала MetaTrader 5. Это индикатор стохастик.
  2. Будем использовать handle для взаимодействия с индикатором. Handle - это цифровой код, представляющий ссылку на поток данных индикатора.

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

Handle объявляется как обычная переменная типа int на глобальном уровне. Для получения показаний индикатора, нужно присвоить handle значение, которое вернет функция инициализации индикатора. Аргументы, передаваемые в функцию инициализации, зависят от настроек самого индикатора.

Для стохастик используются следующие аргументы: символ и таймфрейм, числовые настройки (5, 3), и режим работы индикатора (простой метод усреднения и рассчет по ценам Close).

При вызове функции для получения handle индикатора, можно использовать переменные внешних настроек и передавать их значения в функцию.

Отсутствуют настройки отображения стиля и цвета линий для handle индикатора, так как они не нужны.

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

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

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

Далее программа считает и сохраняет значение стохастика на протяжении всей сигнальной формации в массиве. Для этого используется функция CopyBuffer(), которая скопирует данные индикатора через handle в массив.

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

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

В случае, если сигнал проходит фильтр, значение переменной "сигнал Flag" остается без изменений. Если же сигнал не проходит фильтр, значение "сигнал Flag" обнуляется.

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

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

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

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

А теперь пора дать ответ на вопрос: "А почему копируя индикаторный буфер мы копировали значение индикатора для нулевой свечи? Ведь она не участвует в формировании сигнала". Ответ на этот вопрос заодно является и ответом на вопрос: "Зачем мы все время плюсовали эту вот единичку".

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

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

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

Также понадобится одна служебная переменная, объявим ее перед входом в цикл:

int i, filter;

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

  • Первое условие: если "signal_flag" указывает на покупку, и стохастик на свече с индексом "i" находится на уровне перепроданности или ниже, сигнал прошел сквозь фильтр, изменяем значение "filter" на true и выходим из цикла.
  • Второе условие: если "signal_flag" указывает на продажу, и стохастик на свече с индексом "i" находится на уровне перекупленности или выше, сигнал проходит фильтр, опять же устанавливаем значение "filter" в true и выходим из цикла.
  • Если ни одно из условий не выполнено, просто не трогаем никаких значений и идем дальше.
  • Повторяем до тех пор, пока не будет выполнено одно из условий выхода из цикла, или пока не пройдем все сигнальные свечи.

Если по окончании цикла переменная "filter" все еще равна "false", значит сигнал не прошел через наш фильтр. Следовательно, обнуляем переменную "signal_flag".

В обратном случае просто не трогаем ее. Вот собственно и вся логика фильтрации торгового сигнала через показания технического индикатора.

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

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

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

Там же в комментариях пользователи с ником Роман обнаружил семантическую ошибку в торговом алгоритме, которую я сам пропустил. Все дело в том, что функции открытия позиций и установки отложенных ордеров возвращают отрицательное значение в случае неудач. А в самом алгоритме это значение принимает без знаковая переменная в тестере стратегии. Результат такого действия не увидеть. А вот на счете может произойти ошибка переполнения, и переменной "position" будет присвоено значение, которое позволит алгоритму продолжать работу, вместо того чтобы сбросить все переменные. Как бы то ни было, в исходниках под видео все будет исправлено.

Все будет в порядке, так что будьте бдительны, как Роман. Никто не безупречен, даже я. И вообще, активность в комментах помогает продвижению канала по алгоритмам YouTube. Так что не забывайте про них. Спасибо за внимание. Всем пока!


Содержание, они же тайм-коды:

00:00:00 - Приветствие и тема видео

00:01:03 - Доступ к данным индикатора через Indicator Handle

00:02:13 - Связывание Indicator Handle с потоком данных индикатора

00:05:41 - Описание работы вторичного фильтра и его место в коде программы

00:07:53 - Вход в блок фильтра и начальные проверки

00:08:51 - Считываем данные из потока индикатора и сопутствующие функции

00:13:42 - Алгоритм фильтрации

00:17:07 - Даем пользователю возможность вкл/выкл фильтр

00:18:37 - Заключение и планы на ближайшее будущее

00:19:40 - Благодарности и правки

MQL5 Первый робот. Индикаторный фильтр
MQL5 Первый робот. Индикаторный фильтр
  • 2022.12.28
  • www.youtube.com
Продолжаем изучать язык программирования MQL5 на примере написания торгового эксперта.В этом видео-уроке мы добавим вторичный фильтр торгового сигнала, основ...
Причина обращения: