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

 

MQL5 Для начинающих #4. Логические операции


MQL5 Для начинающих #4. Логические операции

Всем привет! Продолжаем изучать M5 и сегодня речь пойдёт об операциях сравнения и логических ветвлениях в программировании. Для чего это нужно? Представим себе ситуацию, что нам нужно открыть сделку на покупку, и мы пишем соответствующий скрипт, который откроет нам позицию "buy" по текущей рыночной цене. А если нам нужен "buy stop", значит пишем еще один скрипт, у которого уже будет 1 входящая настройка. Появляться на входе и нам самим нужно следить за тем, чтобы указанная нами цена была выше текущего. И для "buy limit" еще один скрипт. А потом нам тут понадобится войти в продаже, это еще три скрипта... Не долго во всем этом великолепии и запутаться!

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

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

  • Логическое равенство: это два знака равно "=="
  • Логическая неравенство: это "!="
  • Вообще "!" это всегда логическое отрицание, то есть "не"

Дальше:

  • Больше: соответствующий знак ">"
  • Меньше: логично вытекает из предыдущего знака, "<"
  • Больше либо равно: ">="
  • Меньше либо равно: "<="

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

Вот так! Теперь ставим фигурные скобки и между ними записываем все действия, которые будут сделаны, если условие "if" выполнено. В нашем случае, я просто напишу текст "Условие верно". А что делать в местных условиях, если оно не верно? Для этого существует ключевое слово "else". Опять же, открываем фигурные скобки и в них задаем действия на этот случай.

Надо сказать, что "else", если он есть, всегда должен идти вслед за "if". Их можно задать несколько, и давайте сейчас сделаем еще как минимум парочку.

Допустим, "а не равно b", "меньше б" и т.д.

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

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

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

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

Кстати, а вы уже заметили, что до сравнения "равенство а и b" мы не дойдем никогда? Почему? Потому что у нас есть два взаимоисключающих условия: "а меньше b" и "а больше либо равно b", которые проверяются перед проверкой "равенство а и b", и проверка на равенство а и b будет выполнено только если оба предыдущих условия верны, а такого быть не может.

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

Давайте покажу. Но в данном случае все проверки их будут выполнены в любом случае. И ведь можно сделать так, чтобы взаимоисключающие условия буквально исключали друг друга. Это делается сочетанием ключевых слов "else" и "if". Такая запись говорит о том, что если не выполнено условие предшествующего "if", то проверяется следующее условие "else if". И согласитесь сами, если есть условие для открытия "buy stop", но условий для открытия "моментом" точно быть не может. В нашем случае сработает 1-й "if".

Кстати, если мы сейчас скомпилируем наш скрипт, то обнаружим, что количество предупреждений уменьшилось с 3 до 1. И как первое условие будет верным, предупреждение касается только его, а остальное уже не прогреется.

Давайте посмотрим, как это выглядит на практике. Только для начала пропишем нашим проверкам действия. Здесь условие верно, здесь тоже верно, а в следующей проверке не верно. Еще раз компилируем, видим в терминале, и запускаем стрим. Как видите, журнале только 2 записи, потому что пошла первая проверка на неравенство, 2-я и 3-я не проверялись. А при проверке на равенство, сработал "else", так наши переменные а и b друг другу не равны.

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

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

Давайте также поставим наше приветствие меньше 1, или что мне делать 3. Собственно, вот и все. Можно компилировать.

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

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

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

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

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

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

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

MQL5 Для начинающих #4. Логические операции
MQL5 Для начинающих #4. Логические операции
  • 2021.09.06
  • www.youtube.com
В этом видео-уроке пойдет речь о том, как использовать инструменты логических сравнений и прописывать реакции на них в программе. Также немного затронем внеш...
 

MQL5 Для начинающих #5. Массивы


MQL5 Для начинающих #5. Массивы

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

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

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

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

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

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

А можно сделать еще и вот так:

int a[4] = {0};
Чуть-чуть подправим имя массива, указав явно его размерность и просто оставив в фигурных скобках 0. Таким образом, все элементы нашего второго массива будут инициализированы нулем.


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

Индексация массива всегда начинается с 0, то есть первый элемент массива всегда имеет индекс 0, а последний - это размер массива - 1.

В нашем конкретном случае это выглядит так:

int firstElement = a[0]; // 12
int secondElement = a[1]; // 5
int lastElement = a[3]; // 13
Давайте так и сделаем, используя уже знакомую нам функцию Print.


Готово! Теперь компилируем и запускаем на выполнение в терминале. Вот так это все выглядит. Мы поэлементно вывели оба наших массива, и все корректно работает.

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

И вот тут есть даже не тонкость, а опасность. Если я сделаю вот так:

int wrongElement = a[4];

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

Что же делать в таком случае? Есть два пути: либо удалить все обращения к несуществующим элементам массива, либо изменить размер нашего массива. Вернемся в код и продолжим. В нашем случае, первое решение подходит довольно просто. А для второго варианта существует встроенная в MQL5 функция ArrayResize. Записывается она вот так, и в скобках мы указываем сначала имя массива.

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

И казалось бы, все хорошо, можно раскомментировать последние Print и продолжить. Но теперь и ругается компилятор, а именно выдает предупреждение и предупреждает нас о том, что функция ArrayResize не применима к статическим массивам. Что такое статический массив? Это массив, количество элементов которого заранее задано, и далее в программе это количество нельзя изменить. То есть, создали массив на четыре элемента - он и будет именно на четыре элемента, ни больше, ни меньше.

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

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

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

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

Давайте, чтобы не много не набирать, вернем размерность четыре и раскомментируем нашу последнюю запись, чуть-чуть ее подправив. Скомпилируем и запустим на исполнение. Ошибка! Вышли за пределы массива.

Но теперь мы можем еще раз изменить размер нашего массива. Давайте это и сделаем. Перед обращением к несуществующему элементу, вернемся в редактор и перед нашим последним Print, там где ArrayResize(), заменим массив a на a1. Компилируем, переходим в терминал, все чистим и запускаем.

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

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

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

Также можно присваивать элементу массива какое-нибудь значение, так же, как и обращаться к ним. Например, пусть a[0] будет равно 127. Теперь компилируем и запускаем.

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

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

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

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

MQL5 Для начинающих #5. Массивы
MQL5 Для начинающих #5. Массивы
  • 2021.09.09
  • www.youtube.com
В этом видео-уроке состоится знакомство с массивами. Что они такое и для чего могут быть полезны. А также, как с их помощью можно легко испортить жизнь себе ...
 

MQL5 Для начинающих #6 Перечисления


MQL5 Для начинающих #6 Перечисления

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

Чтобы не переделывать скрипт тест, так как его содержимое нам понадобится в следующем занятии, которое будет посвящено циклам, я создам новый скрипт и заодно покажу маленькую хитрость. Если при создании скрипта вам лень прописывать директиву #property show_inputs, можно просто добавить любую переменную, не забыв указать имя, и нажать "Готово".

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

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

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

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

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

Откуда же взялись эти значения? Если мы не указываем их явно, то первому значению в перечислении присваивается 0, а все последующие рассчитываются с увеличением на 1.

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

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

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

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

Можно провести аналогию с планом, в котором есть пункты 1, 2, 3 и так далее. Например, распорядок дня: пункт 1 - открытие века, пункт 2 - открытие второго видео и так далее.

Кстати, если явно не присваивать значение ни одному из членов перечисления, то можно соответственно явно присвоить значение всем сразу.

Давайте, наконец, приведем наш пример с фильмами к адекватному состоянию. До 1 минуты у нас будет 1 минута, и соответственно 5 минут будет 5 минут, 1 час - через 60 и соответственно 4 часа будут 240. Вот и соответственно, последний раз запустим на выполнение, чтобы убедиться в том, что все верно.

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

Теперь можно скомбинировать и отправить на выполнение. Как видите, все работает правильно.

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

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

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

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

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

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

MQL5 Для начинающих #6 Перечисления
MQL5 Для начинающих #6 Перечисления
  • 2021.09.17
  • www.youtube.com
В этом видео-уроке я, как и обещал, возвращаюсь к перечислениям. Или типу данных enum. Почему, собственно, его еще называют именованным перечислением и как в...
 

MQL5 Для начинающих #7. Циклы


MQL5 Для начинающих #7. Циклы

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

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

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

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

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

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

Затем я использовал встроенную функцию MathRand для генерации псевдослучайных чисел в диапазоне от 0 до 30. Это псевдослучайные числа, потому что они генерируются на основе стартового числа, которое задается функцией MathSrand. Эта функция приводит генератор случайных чисел в стартовое состояние перед вызовом MathRand. Это необходимо, чтобы при каждом запуске программы генерировались разные значения.

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

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

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

В этой части кода я использовал еще одну встроенную функцию - GetTickCount, которая возвращает количество миллисекунд с момента старта операционной системы. Из-за особенностей хранения чисел в памяти компьютера, это число обновляется каждые 49 целых и 7 десятых дня, что позволяет функции Random выдавать более случайные числа.

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

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

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

Само объявление цикла for состоит из ключевого слова for, условия в круглых скобках и блока кода в фигурных скобках. Здесь у нас условие из трех выражений слева направо: инициализация цикла (то есть начальное значение счетчика), условие завершения цикла (то есть условие, при котором цикл будет выполняться) и выражение, которое будет выполняться после каждой итерации.

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

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

Мы также рассмотрели еще два вида циклов: do while и do until. В do while условие выполнения цикла проверяется перед выполнением операции, поэтому как минимум одна итерация будет выполнена. А в do until условие проверяется после выполнения операции.

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

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

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

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

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

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

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

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

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

MQL5 Для начинающих #7. Циклы
MQL5 Для начинающих #7. Циклы
  • 2021.09.22
  • www.youtube.com
В этом видео состоится знакомство с циклами - какие виды их есть, а также где, когда и как их уместно применять. С практическими примерами.Уголок компетентно...
 

MQL5 Для начинающих #8 Функции и кое что еще. Часть 1


MQL5 Для начинающих #8 Функции и кое что еще. Часть 1

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

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

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

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

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

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

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

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

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

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

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

MQL5 Для начинающих #8 Функции и кое что еще. Часть 1
MQL5 Для начинающих #8 Функции и кое что еще. Часть 1
  • 2021.09.30
  • www.youtube.com
Этот видео-урок будет посвящен функциям. Правда, не только им, но дальше будет двигаться сложно, если плохо представлять себе, что такое функция и с чем её е...
 

MQL5 Для начинающих #8 Функции и кое что еще. Часть 2


MQL5 Для начинающих #8 Функции и кое что еще. Часть 2

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

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

Для примера, создадим переменную "sum" и присвоим ей значение 0. Она является локальной переменной для функции "OnInit", и ее область видимости ограничена телом этой функции.

Теперь создадим блок "if" с условием "i < 5". Внутри этого блока объявим новую переменную "i_new" и присвоим ей значение 0. "i_new" является локальной переменной для блока "if", и ее область видимости ограничена этим блоком.

Тем не менее, мы можем обращаться к переменной "sum" как внутри блока "if", так и за его пределами, так как она объявлена перед объявлением "i_new".

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

Однако, если мы попробуем обратиться к переменной "i_new" за пределами блока "if", то компилятор выдаст ошибку, так как "i_new" видна только в пределах блока, в котором она была объявлена.

Чтобы исправить это, мы должны заново объявить переменную "i_new" за пределами блока "if". Теперь скрипт компилируется без ошибок и работает правильно.

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

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

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

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

На самом деле у нас уже есть две глобальные переменные - это наши входящие параметры "a" и "b", но помимо того, что они глобальные, они еще и внешние и разговора о них не будет отдельный, а сейчас я просто создам одну глобальную переменную "vogue" и чтобы долго не писать, сделаем это так:

int vogue = 1024;

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

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

И тут вы имеете полное право возмутиться: как же так, а что делать с правилом уникальности имен переменных? А я вам отвечу, что это правило распространяется только на переменные 1 уровня. Давайте даже это и покажу, просто создадим еще одну переменную с тем же именем "vogue" и присвоим ей значение 0.\

int vogue = 1024;
int vogue = 0;

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

Вернемся в зад, еще раз компилируем и отправим на выполнение.

Давайте на этот раз коротко пройдемся по произошедшему.

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

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

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

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

Для чего такие сложности? В основном из-за работы с памятью. Но для MQL5 это некритично.

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

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

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

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

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

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

А на данный момент всем пока!

MQL5 Для начинающих #8 Функции и кое что еще. Часть 2
MQL5 Для начинающих #8 Функции и кое что еще. Часть 2
  • 2021.10.01
  • www.youtube.com
Вторая часть занятия, посвященного функциям и всему, что с ними связано. И наконец-то рассмотрим сроки жизни и области видимости переменных.Уголок компетентн...
 

MQL5 Для начинающих #8 Функции и кое что еще. Часть 3


MQL5 Для начинающих #8 Функции и кое что еще. Часть 3

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

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

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

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

Для этого нужно объявить ее как статическую. Делается это при помощи ключевого слова "static" перед объявлением типа переменной.

static int counter = 0;

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

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

Давайте посмотрим, как это работает. Зададим нашей функции "func_2" тип возвращаемого значения "int". Принимать эта функция никаких аргументов (они же параметры, они же формальные параметры) не будет. А далее объявим цикл, в котором и будем работать с переменной "counter".

int func_2()
{
    static int counter = 0;
    for(int i = 0; i < 100; i++)
    {
        counter++;
    }
    Print("Number of iterations: ", counter);
    return counter;
}

Так как в условии цикла указано, что "i" должно быть не больше 98, то выйдем мы из цикла при "counter" равном 97. Но при этом считать итерации мы будем с единицы, как у людей, они с нуля. Как условие цикла для этого в Print указан вывод значения переменной "counter" с добавочной единичкой.

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

И так, можно компилировать и отправлять на выполнение. И это есть правильный результат. При первом вызове функции "func_2" локальная переменная "counter" равна нулю. И, увеличивая ее на единицу девяносто восемь раз, мы в итоге получили именно 98, а цикл сделал 98 итераций. Но переменная "counter" была объявлена как статическая, и после завершения работы функции "func_2" ее значение осталось в памяти. И как только мы еще раз вызвали выше названную функцию, значение переменной "counter" восстановилось из памяти, и на начало второго вызова оно было равно 98, а не нулю, как при объявлении. Соответственно, чтобы это значение стало равным сотни, его нужно увеличить на единичку всего два раза, то есть те самые две итерации цикла, после чего сработало наши условие досрочного выхода из цикла.

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

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

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

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

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

Прошлое видео я завершил на том, что когда мы передаем функции значение какой-либо переменной, это значение копируется, и для нашего примера, чтобы мы не делали с переменной "sum" функции "func_2", это никак не отразится на переменной "sum" функции "on_start". Это называется передача аргументов по значению.

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

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

void func(int &ref)
{
    ref = 100;
}
Теперь изменим значение нашей ссылки "sum":
func(sum);
Кстати, забыл сказать еще одно - у "void" нет возвращаемого значения, и оператор "return" для них не работает. В данном случае его можно использовать только для прерывания выполнения функции. Вот так это выглядит:
void func(int &ref)
{
    ref = 100;
    return;
}

Теперь все, что идет после оператора "return", выполняться не будет.

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

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

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

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

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

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

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

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

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

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

Кстати, соответствие сигнатуре проверяется именно по аргументам, так что можно сделать и вот так. И на всякий случай, что-нибудь из нее вернем, чтобы не было ошибки с "getLastError".

Теперь все, что идет после оператора "return", выполниться не будет.

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

Можно компилировать и отправлять на выполнение.

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

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

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

И да, мы можем менять тип возвращаемого значения, или, проще говоря, тип функции, но аргументы должны быть разными. Например, не прокатит вот это. В данном случае компилятор пытается вызвать вариант функции с принимаемым "int", но эта функция типа "void", ее значение нельзя присваивать чему-либо. За этим тоже надо следить.

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

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

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

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

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

Всем пока!

MQL5 Для начинающих #8 Функции и кое что еще. Часть 3
MQL5 Для начинающих #8 Функции и кое что еще. Часть 3
  • 2021.10.07
  • www.youtube.com
Финальная часть видео-урока о функциях и связанных с ними нюансами в языке программирования MQL5. А напоследок, немного про ООП. Совсем чуть-чуть.Уголок комп...
 

MQL5 Для начинающих #9 Строки


MQL5 Для начинающих #9 Строки

Всем привет! Продолжаем базовый курс MQL5 и в этом занятии подробнее рассмотрим такие типы данных как "string" и "datetime". Точнее, не сами типы, с ними сложностей в общем-то никаких, а приемы работы с ними и что они такое. Начну пожалуй с "string", и по традиции, немного истории.

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

Хронометраж: как я уже не раз говорил, MQL5 основан на C++ и сейчас нужно немного отвлечься на плюсы. Формально, все плюсы до сих пор нет строк. Чтобы работать с ними, нужно подключать соответствующий заголовочный файл. Это несложно, но все-таки лишние телодвижения. Зато в плюсах есть тип "char".

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

Например, в строке всегда на один символ больше, чем видно. Просто последний символ с максимальным индексом - это служебный символ конца строки, как пресловутый суслик. Его не видно, но он есть. И для обработки строк создан отдельный набор функций, который идет в комплекте со стоковым типом данных в MQL5. Вся возня с подключением и 1 символьным типом данных убрана. Что называется, "под капотом" строки, как и её функции для работы с ними, доступны без лишних телодвижений. Но вся суть строк сохранилась. Это все еще массив из символов в формате Unicode. А если копнуть чуть глубже, то каждый элемент строки содержит в себе подсимволы Unicode, и при выводе на экран этот код автоматически преобразуется в соответствующий ему символ, который мы и видим. Например, в журнале при вызове функции "Print".

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

А начать разбираться с форматом "string" лучше с форматирования самой текстовой строки. Давайте для начала эту самую строку создадим.

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

Давайте посмотрим, как это выглядит.

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

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

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

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

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

Эти три функции объединяют ряд общих правил. В качестве аргументов могут выступать константы, выражения и переменные массивы. Нужно выводить поэлементно. Количество аргументов в них не может быть больше 64. Чтобы обойти последнее ограничение и немного автоматизировать форматирование текста, есть продвинутые версии функции "Print". А именно, "PrintFormat" и "PrintFormatArray".

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

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

Также есть функция "MessageBox". Она выводит на экран стандартное окно сообщения Windows. Вызывается по имени "MessageBox". Вот тут с аргументами не все так просто. Первым аргументом идет само сообщение, в виде переменной типа "string" или текстовой константы. По вторым аргументом может идти заголовок окна. Его может и не быть, и тогда в заголовке мы увидим имя программы, открывшей это самое окно. А далее идут аргументы, которые будут описывать кнопки в окне сообщений. Их тоже можно не указывать, но у нас всегда будет кнопка "OK", которая закрывает окно сообщений.

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

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

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

Примерно вот так. Теперь скомпилируем и отправим выполняться.

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

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

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

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

string str = "нашу строку нужно немножко доработать и попробуем еще раз";
string delimiter = " ";
string result[];

StringSplit(str, delimiter, result);


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

Я бы хотел еще в этом видеоуроке вместить тип "datetime", но по таймеру видео не получится. Так что о нем следующем видеоуроке.

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

MQL5 Для начинающих #9 Строки
MQL5 Для начинающих #9 Строки
  • 2021.10.13
  • www.youtube.com
В этом видео-уроке речь пойдет о типе данных string.Также будут рассмотрены функции вывода информации в терминал для анализа и основные приемы работы со стро...
 

MQL5 Для начинающих #10 Тип datetime и математические функции


MQL5 Для начинающих #10 Тип datetime и математические функции

Всем привет! Продолжаем видеокурс по MetaTrader 5 для начинающих и сегодня речь пойдет о типе данных datetime. Также постараюсь рассказать о встроенных в MetaTrader 5 математических функциях.

Начну с того, что тип datetime технически является целочисленным, так как переменные этого типа хранят дату и время в формате количества секунд, прошедших с 0 часов 1 января 1970 года. Для этого подходит целое число. У такого подхода есть как достоинства, так и недостатки.

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

Недостатки у такого способа хранения дат и времени диалектические и неотделимы от достоинств. Один из недостатков - архиважность даты и времени, которые могут возникнуть из-за ограничения максимального значения целочисленных типов. Например, проблема 2000 года, когда все часы в мире автоматически переведутся на 0:00 1 января 1970 года. Но сейчас эта проблема решена, и максимальное ограничение по дате равно 31 декабря 3000 года.

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

Для этого в MQL5 есть функции TimeTradeServer() и TimeLocal(). Первая возвращает известное время торгового сервера, а вторая возвращает то же самое, но для операционной системы. Также, если вы заметили, даты выводятся в полноразмерном формате, но если нужно сократить запись, то используется функция TimeToString().

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

Давайте покажу, как это выглядит. Флаг "TIME_DATE" отвечает за вывод даты, флаг "TIME_MINUTES" за вывод часов и минут, а флаг "TIME_SECONDS" за вывод часов, минут и секунд. Их можно комбинировать в различных вариантах. Например, если нужна только дата, оставляем только флаг "TIME_DATE", а если нужны дата и часы с минутами, но без секунд, то ставим флаги "TIME_SECONDS". Если ни один флаг не указан, выводится комбинация флагов "TIME_SECONDS".

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

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

Также стоит знать, что функция TimeToString() применяется не только в MQL5, и термин "timeseries" используется не только здесь. Важно помнить, что для сортировки массива в виде timeseries, его нужно сначала отсортировать функцией ArraySort, иначе ничего не получится.

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

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

Потом можно создать два массива - один на 8 элементов и другой на 5 элементов. Теперь сортируем наш массив из 15 элементов функцией ArraySort и копируем первые три элемента в первый массив, а следующие пять - во второй массив.

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

Теперь давайте перейдем к типу данных datetime. Это целочисленный тип данных, который хранит дату и время в формате количества секунд, прошедших с 1 января 1970 года. Есть несколько функций, которые работают с этим типом данных. Например, функция TimeToString() преобразует datetime в строку, а функция StringToTime() преобразует строку обратно в datetime.

Также есть несколько математических функций, которые работают с числами типа double. Например, функция MathMax() принимает два числа и возвращает большее из них, а функция MathMin() возвращает меньшее. Функция MathAbs() принимает одно число и возвращает его абсолютное значение, то есть без знака.

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

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

MQL5 Для начинающих #10 Тип datetime и математические функции
MQL5 Для начинающих #10 Тип datetime и математические функции
  • 2021.10.16
  • www.youtube.com
В этом занятии подробно разберем тип данных datetime и приемы работы с ним. Также будет затронута тема математических функций и показаны наиболее часто испол...
 

MQL5 #11 Первый скрипт. Часть 1


MQL5 #11 Первый скрипт. Часть 1

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

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

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

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

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

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

Робот на графике может существовать только в одиночку, а вот индикаторы - и в любых количествах.

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

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

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

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

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

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

Чтобы сделать настройки более понятными, давайте добавим комментарии к каждой из них. Для этого перед объявлением входящего параметра мы объявим перечисление "Direction" и пропишем в нем два элемента - "BUY" и "SELL". Обратите внимание, строчные комментарии будут работать и в этом случае.

Ограничения на размеры имен и комментариев такие же, как и у внешних переменных. Теперь вместо переменной типа bool сделаем внешней переменную типа "Direction" с именем "inDirection" и пропишем соответствующий комментарий.

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

С массивами и более сложными типами данных такое не прокатит. Использовать их в качестве внешних переменных нельзя.

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

Согласитесь, совсем другое дело! Да, я немного пошутил с перечислением, но надеюсь, вы не прогуливали географию в школе и помните, как правильно читать географические карты.

Если у вас комментарии не заменяют имена переменных, нужно подключить директиву "strict". Мне это, как вы уже успели убедиться, не нужно, но на других операционных системах может помочь этот прием. Кстати, работает и в МТ4.

А вот если вместо вменяемого текста отображается что-то очень невменяемое или некоторые служебные значки на графиках выглядят неправильно, например, стрелочка, значит системе нет нужного шрифта. В операционках 7 издание Linux или Mac эта проблема актуальна. В описании под видео есть ссылка на Google Disk с нужным шрифтом, качайте и устанавливайте в систему.

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

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