English Español Deutsch 日本語 Português
preview
Введение в MQL5 (Часть 3): Изучаем основные элементы MQL5

Введение в MQL5 (Часть 3): Изучаем основные элементы MQL5

MetaTrader 5Эксперты | 7 июня 2024, 14:15
567 2
Israel Pelumi Abioye
Israel Pelumi Abioye

Введение

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

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

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

В этой статье мы рассмотрим следующие темы:

  • Массив
  • Пользовательские функции
  • Препроцессор
  • Обработка событий

Прежде чем мы приступим к практическому путешествию в глубины программирования MQL5 в части 3, давайте пройдемся по пройденному материалу. Ниже представлено небольшое видео, которое кратко суммирует ключевые понятия и основные моменты из Части 2 данной серии. Давайте вспомним, что мы уже изучили, и затем перейдем к новым темам.



1. Массив

Что такое массивы?

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

Аналогия

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

1.1. Как объявить массив

В MQL5 массив объявляется путем указания его типа данных и имени, а также размеров массива (если есть).

Основной синтаксис:

data_type array_name[array_size];

  • data_type — тип данных, которые будет содержать массив (int, double, string, и др.)
  • array_name — имя массива.
  • array_size — размер или длина массива, определяющая, сколько элементов он может содержать.
Пример:
void OnStart()
  {

   int closePrices[5]; // An integer array with a size of 5, means the array has 5 integer elements
   double prices[10];  // A double-precision floating-point array with a size of 10

  }

1.2. Присвоение значений массиву

В MQL5 значения массиву можно присваивать во время его объявления или отдельно после его объявления. Давайте рассмотрим оба способа:

1.2.1. Присвоение значений после объявления

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

Пример:

void OnStart()
  {

   int closePrices[5];
   closePrices[0] = 10;
   closePrices[1] = 20;
   closePrices[2] = 30;
   closePrices[3] = 40;
   closePrices[4] = 50;

  }

1.2.2. Присвоение значений во время объявления

При этом способе вы объявляете массив и указываете значения внутри фигурных скобок {}. Размер массива определяется количеством указанных значений. В этом примере массив closePrices объявлен с размером 5 и инициализируется значениями 10, 20, 30, 40 и 50. Оба способа допустимы, и вы можете выбрать тот, который нравится вам больше или же который больше подходит требованиям конкретной программы.

Пример:

void OnStart()
  {

   int closePrices[5] = {10, 20, 30, 40, 50};

  }

1.3. Как получить доступ к элементам массива

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

Рисунок 1. Доступ к элементам массива

Пример:

void OnStart()
  {
  
// Declare an integer array with a size of 5
   int closePrices[5];

// Assign values to array elements
   closePrices[0] = 10;
   closePrices[1] = 20;
   closePrices[2] = 30;
   closePrices[3] = 40;
   closePrices[4] = 50;
   
   // Access and print array elements
   Print("Element at index 0: ", closePrices[0]); // Output: 10
   Print("Element at index 1: ", closePrices[1]); // Output: 20
   Print("Element at index 2: ", closePrices[2]); // Output: 30
   Print("Element at index 3: ", closePrices[3]); // Output: 40
   Print("Element at index 4: ", closePrices[4]); // Output: 50

  }

Объяснение:

Объявление массива:

int closePrices[5];

В этой строке объявляется целочисленный массив с именем closePrices фиксированного размера из 5 элементов. В MQL5 массивы могут хранить элементы одного типа данных, а индексация начинается с 0, поэтому наш массив имеет индексы от 0 до 4.

Присвоение значений элементам массива:

   closePrices[0] = 10;
   closePrices[1] = 20;
   closePrices[2] = 30;
   closePrices[3] = 40;
   closePrices[4] = 50;

Здесь значения присваиваются каждому элементу массива с помощью индексов. Массив closePrices теперь содержит значения 10, 20, 30, 40 и 50 на соответствующих позициях.

Доступ и вывод элементов массива:

   Print("Element at index 0: ", closePrices[0]); // Output: 10
   Print("Element at index 1: ", closePrices[1]); // Output: 20
   Print("Element at index 2: ", closePrices[2]); // Output: 30
   Print("Element at index 3: ", closePrices[3]); // Output: 40
   Print("Element at index 4: ", closePrices[4]); // Output: 50
Выше на примере Print показано, как получить доступ и вывести значения, хранящиеся по определенным индексам в массиве. Здесь осуществляется доступ к индексам от 0 до 4, а соответствующие значения выводятся в консоль.

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


2. Пользовательские функции

Что такое общие функции?

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

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

Аналогия

Представьте, что вы находитесь на кухне и готовите вкусную еду (ваш код). Так вот функции подобны рецептам, предоставляющим пошаговые инструкции по созданию конкретных блюд. Теперь давайте взглянем на примеры пользовательских и общих функций. В пространстве кулинарии общие функции можно представить как всем известные рецепты, публикуемые в кулинарных книгах. Эти рецепты стандартны и часто используются, например, рецепт отварного риса или глазуньи. С точки зрения программирования такие функции, как Alert, Comment и Print подобны этим популярным рецептам. У них есть конкретные цели, они уже готовы, и их можно сразу использовать.

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

2.1. Как создать функцию

Создание функции в MQL5 включает в себя указание имени функции, определение ее параметров (если они есть), указание типа возвращаемого значения (если она возвращает значение) и предоставление блока кода, составляющего тело функции.

Шаг 1. Определение цели функции

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

Шаг 2. Объявление функции

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

Примечание. Обработчики событий в MQL5 — это функции, которые автоматически реагируют на определенные события, такие как изменения рыночной цены (OnTick), взаимодействие с графиками (OnChartEvent) и временные интервалы (OnTimer). Они оптимизируют выполнение кода на основе событий в реальном времени или по расписанию на платформе MetaTrader 5. Подробнее рассмотрим в будущем.

Синтаксис:

return_type FunctionName(parameter1_type parameter1, parameter2_type parameter2, ...) 
   {
    // Function body (code block)
    // Perform actions or calculations
    // Optionally, return a value using the 'return' keyword if the function has a return type
   }

Шаг 3. Определение тела функции

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

Пример:

double MyFunction(int param1, double param2)
     {
      // Function body: Calculate the product of the two parameters
      double result = param1 * param2;

      // Return the result
      return result;
     }

Объяснение:

“double MyFunction(int param1, double param2)”:

  • Эта строка определяет функцию с именем MyFunction. Она принимает два параметра: param1 типа int (целое число) и param2 типа double (число с плавающей запятой). Ожидается, что функция вернет значение типа double.

{:

  • Открывающая фигурная скобка «{» отмечает начало тела функции, где находится фактический код функции.

“double result = param1 * param2;”:

  • Эта строка вычисляет произведение значений param1 и param2 и сохраняет результат в переменной с именем result. Поскольку и param1, и param2 имеют числовые типы, операция умножения (*) приводит к результату типа double.

“return result;”:

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

“}”:

  • Закрывающая фигурная скобка «}» отмечает конец тела функции.

Шаг 4: Использование функции

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

Пример:
void OnStart()
  {

// Calling the custom function
   double result = MyFunction(5, 3.5);

// Printing the result
   Print("The result is: ", result); // The output will be 17.5

  }

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

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


3. Препроцессоры

Что такое препроцессоры?

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

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

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

Рисунок 2. Препроцессоры в MetaEditor

Примечание. При написании программ на MQL5 важно помнить, что после препроцессоров не ставится точка с запятой (;), как в обычных операторах, и они объявляются в глобальном пространстве вашего кода. Такие инструкции, например #define для макросов или #include для включения файлов, определяют формирование поведения и структуру программы. Итак, еще раз — при работе с препроцессорами не ставьте точку с запятой!

Аналогия

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

Пример:

#define MAGIC_NUMBER 10

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MAGIC_NUMBER + 5;
   Comment(result); // Output will be 15

  }

Объяснение:

“#define MAGIC_NUMBER 10”:

  • Здесь мы как бы говорим: «Препроцессор, всякий раз, когда ты видишь в моем коде слово MAGIC_NUMBER, заменяй его числом 10». Это похоже на создание специального слова, обозначающего определенное значение.

“void OnStart()”:

  • В этой части начинается основное действие. OnStart — это специальная функция в MQL5, которая выполняется при запуске вашего скрипта.

“int result = MAGIC_NUMBER + 5;”:

  • Здесь мы используем наше специальное слово MAGIC_NUMBER. Препроцессор видит это и заменяет MAGIC_NUMBER на 10. Итак, эта строка аналогична фразе "int result = 10 + 5;"

“Comment(result); // Output will be 15”:

  • Функция Comment — это как попросить робота что-то сказать. В данном случае мы просим робота назвать значение result, равное 15. Итак, когда вы запустите этот скрипт, он отобразит «15» в комментариях к вашему графику.

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


3.1. Категории препроцессоров

3.1.1. Макроподстановка (#define)

Макросы используются для создания констант. Константы — неизменяемые значения. С помощью макросов можно создавать специальные слова, обозначающие такие значения.

Пример:

#define MAGIC_NUMBER 10

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MAGIC_NUMBER + 5;

  }

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

3.1.1.1.  Параметризованные макросы

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

Пример:

#define MULTIPLY(x, y) (x * y)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MULTIPLY(3, 5); // Result is 15

  }

Здесь макрос MULTIPLY берет два ингредиента (числа) и умножает их. Это как функция в вашем коде.

Объяснение:

“#define MULTIPLY(x, y) (x * y)”:

  • Эта строка говорит компьютеру: «Всякий раз, когда я говорю MULTIPLY, нужно взять два числа (назовем их x и y) и перемножить их вместе. То есть используя волшебное слово мы создаем машину умножения.

“int result = MULTIPLY(3, 5);”:

  • Здесь используется специальное слово MULTIPLY. Компьютер видит это и знает, что это означает «возьми числа 3 и 5 и умножьте их». Итак, он заменяет MULTIPLY(3, 5) на (3 * 5).

“// Result is 15”:

  • Результат умножения 3 и 5 равен 15. Итак, после того, как компьютер выполнит расчет, значение переменной result станет 15.

По сути, этот код упрощает процесс умножения. Вместо того, чтобы писать «3 * 5» напрямую, мы используем слово MULTIPLY, чтобы сделать код более читабельным и простым для понимания. Очень удобный и полезный помощник.

3.1.1.2. Директива #undef

Директива #undef как бы говорит: «Забудь то, что я говорил ранее, начнем все сначала». В программировании на MQL5 это позволяет отменить определение или отменить создание ранее определенного макроса. Как будто мы стираем надписи на доске.

Пример:

#define MAGIC_NUMBER 10

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MAGIC_NUMBER + 5;
#undef MAGIC_NUMBER // Undefining MAGIC_NUMBER


  }

Объяснение:

“#define MAGIC_NUMBER 10”:

  • Сначала мы определяем макрос под названием MAGIC_NUMBER и устанавливаем его значение 10. Мы говорим: «Всякий раз, когда я использую MAGIC_NUMBER, я имею в виду число 10».

“int result = MAGIC_NUMBER + 5;”:

  • MAGIC_NUMBER используется в расчете, результатом будет 15.

“#undef  MAGIC_NUMBER”:

  • С помощью «#undef MAGIC_NUMBER» мы говорим: «Забудь, что раньше значило MAGIC_NUMBER». Тем самым мы стираем определение и делаем MAGIC_NUMBER неопределенным.
Если вы попытаетесь использовать MAGIC_NUMBER после строки #undef, это приведет к ошибке, поскольку компьютер больше не знает, что означает MAGIC_NUMBER. Это может быть полезно, если вы хотите переопределить или прекратить использование макроса в определенной части вашего кода. То есть какое-то время мы использовали специальное слово, но нам это больше не нужно, и мы можем перейти к чему-то другому.

3.1.2. Свойства программ (#property)

В мире программирования каждая программа имеет свои уникальные черты, как персонаж в истории. С помощью #property мы можем придать нашей программе особые функции и качества. Так мы говорим компьютеры, что есть определенные вещи, которые делают программу особенной. Представьте, что вы пишете книгу или песню и хотите рассказать людям, кто ее создал и когда она была создана. Примерно это же вы делаете в программах с помощью #property. Так мы добавляем небольшую заметку в начало кода: «Эта программа версии 1.0, и я написал ее в 2022 году.»

Пример:

#property copyright "crownsoyin"
#property version   "1.00"

Результат после запуска кода

Рисунок 3. Результат после запуска кода в MetaTrader 5

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

3.1.3 Директива включения (#include)

Что такое включаемый файл?

Вернемся к нашим рецептам. Представим, что у вас есть книга рецептов, но при этом некоторые рецепты хранятся в другой книге. Директива #include укажет «взять дополнительные рецепты из другой книги и объединить их в одну большую коллекцию рецептов». При написании кода это позволяет объединять внешние файлы с основным кодом, делая все доступным в одном месте. В данном случае программирование — как строительство дома с разными комнатами. Каждая комната имеет определенные функции, например, кухня или спальня. Директива #include позволяет повторно использовать эти помещения (функции и структуры) в других домах (программах). В строительстве это звучало бы как: «Я построил классную кухню и теперь могу использовать ее во всех своих домах, а не строить ее каждый раз с нуля».

Пример:

#include "extraRecipes.mqh" // Include external file with extra recipes

Объяснение:

“#include”:

  • Команда #include говорит: «Принеси то-то из другого места и добавь это сюда».

‘"extraRecipes.mqh"’:

  • Текст внутри двойных кавычек — это имя внешнего файла, который нужно включить. В данном случае это "extraRecipes.mqh". Этот файл содержит дополнительные рецепты (код), которые будут использованы в основной программе.

Итак, когда мы пишем «#include "extraRecipes.mqh"», это как будто мы открываем кулинарную книгу и говорим: «Добавим эти дополнительные рецепты к нашим основным инструкциям по приготовлению». Это помогает сохранить основной код чистым и организованным, сохраняя при этом доступ к дополнительным функциям из "extraRecipes.mqh". Когда вы используете #include для включения внешнего файла в свой код, любые функции или структуры, определенные в этом внешнем файле, становятся доступными и могут быть использованы в основном коде.

Примеры:

Внешний файл: “extraRecipes.mqh”

// extraRecipes.mqh
int MultiplyByTwo(int number) // function from an external file  
{
    return number * 2;
}

Основной код:

// Your main code
#include "extraRecipes.mqh" // Include external file with extra recipes
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MultiplyByTwo(5);  // Using the function from the included file
   Print("Result: ", result);      // Output: Result: 10

  }


В этом примере функция MultiplyByTwo определена во внешнем файле extraRecipes.mqh. Используя #include "extraRecipes.mqh" в основном коде, мы можем вызывать и использовать функцию MultiplyByTwo, как если бы она была определена непосредственно в основном коде.

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

3.1.4. Условная компиляции (#ifdef, #ifndef, #else, #endif)

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

3.1.4.1. Директива #ifdef

В MQL5 #ifdef — это директива препроцессора, проверяющая, определен ли конкретный символ. Если символ определен, во время компиляции включается блок кода, следующий за #ifdef. В противном случае включается блок кода, следующий за #else или #endif.

Пример:

#define MAGIC_NUMBER 10
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

#ifdef MAGIC_NUMBER
   Print(MAGIC_NUMBER);
#else
   Print(MAGIC_NUMBER);
#endif


  }

Объяснение:

  • В этом примере MAGIC_NUMBER определяется с помощью директивы #define.
  • Директива “#ifdef MAGIC_NUMBER” проверяет, определен ли MAGIC_NUMBER.
  • Поскольку он определен, включается блок кода, следующий за #ifdef, что приводит к компиляции первого оператора Print.
  • Если бы MAGIC_NUMBER не был определен, был бы включен блок кода, следующий за #else, и был бы скомпилирован второй оператор Print.

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

3.1.4.2. Директива #ifndef

В MQL5 #ifndef — это директива препроцессора, проверяющая отсутствие определения конкретного символа. Если макрос не определен, во время компиляции включается блок кода, следующий за #ifndef. в противном случае включается блок кода, следующий за #else или #endif.

Пример:

void OnStart()
  {

#ifndef MAGIC_NUMBER
   Print(MAGIC_NUMBER);
#else
   Print(MAGIC_NUMBER);
#endif


  }

Объяснение:

  • #ifndef MAGIC_NUMBER проверяет, отсутствует ли определение «MAGIC_NUMBER».
  • Если макрос MAGIC_NUMBER не определен, включается блок кода, следующий за #ifndef, и выводится сообщение, указывающее, что макрос MAGIC_NUMBER не определен.
  • Если MAGIC_NUMBER определен, включается блок кода, следующий за #else, и он выводит значение MAGIC_NUMBER.

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

Примечание. MAGIC_NUMBER не был определен в этом примере.

3.1.4.3. Директива «#endif»

Директива #endif в MQL5 отмечает конец условного блока кода, инициированного директивами #ifdef или #ifndef. Она служит сигналом препроцессору о том, что участок условной компиляции завершен и следует обработать последующий код для компиляции. Она не имеет никаких условий или параметров, ее цель — обозначить конец блока условной компиляции.

Пример:

#define MAGIC_NUMBER 10
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

#ifndef MAGIC_NUMBER
   Print(MAGIC_NUMBER);
#else
   Print(MAGIC_NUMBER);
#endif

  }

Объяснение:

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

Примечание. Всегда сочетайте #endif с открывающей условной директивой (#ifdef или #ifndef), чтобы сохранить правильный синтаксис и избежать ошибок компиляции.

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


4. Обработка событий

Что такое события?

Событие в программировании относится к конкретному происшествию или событию (event), которое происходит во время выполнения программы. События могут быть вызваны различными действиями, такими как взаимодействие с пользователем и изменения состояния системы. В контексте MQL5 события имеют решающее значение при разработке программ для алгоритмической торговли, которые динамически реагируют на рыночные условия.

Что такое обработчики событий?

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

Аналогия

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

4.1. Типы обработчиков событий

Рисунок 4. Типы обработчиков событий


4.1.1. OnInit

В MQL5 OnInit — это специальная функция, используемая в советниках для из инициализации при первой загрузке на график.

Аналогия:

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

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

Примеры:

// Declare global variables
double LotSize;
int TakeProfit;
int StopLoss;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
// OnInit function
int OnInit()
  {
// Set EA parameters
   LotSize = 0.1;
   TakeProfit = 50;
   StopLoss = 30;

// Display initialization message
   Print("EA is getting ready. Lot Size: ", LotSize, ", Take Profit: ", TakeProfit, ", Stop Loss: ", StopLoss);

   return(INIT_SUCCEEDED);
  }

Объяснение:

  • Мы объявляем некоторые глобальные переменные, которые может использовать советник.
  • Функция OnInit — это место, где мы инициализируем эти переменные и выполняем все необходимые настройки. В нашем примере мы устанавливаем значения LotSize, TakeProfit и StopLoss.
  • Оператор Print — сообщение, которое советник может отправить в консоль, чтобы сообщить о своей инициализации. Программа говорит: «Я готовлюсь, и вот мои настройки».

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

4.1.2. OnStart

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

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

Пример:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
// This script prints a message to the console when activated

// Entry point when the script is started
void OnStart()
  {
// Print a message to the console
   Print("Script Activated! Hello, Traders!");
  }

Объяснение:

  • Функция OnStart — это точка входа скрипта..
  • Функция Print предназначена для вывода сообщения на вкладке «Эксперты» окна терминала в MetaTrader.
  • Когда вы запустите скрипт на графике или в MetaEditor, вы увидите указанное сообщение в консоли. Вы можете изменить оператор Print и добавить более сложную логику в соответствии с требованиями вашего скрипта.

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

 

4.1.3. OnTick

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

Аналогия

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

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

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

Пример:
// Declare a variable to store the last tick's close price
double lastClose;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
// OnInit function
int OnInit()
  {

// Initialize the variable with the current close price
   lastClose = iClose(_Symbol, PERIOD_CURRENT, 0);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

// Get the current close price
   double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);

// Check if the close price has changed
   if(currentClose != lastClose)
     {
      // Print a message when the close price changes
      Print("Close price changed! New close price: ", currentClose);

      // Update the last close price
      lastClose = currentClose;
     }


  }

Объяснение:

Объявление переменной:

// Declare a variable to store the last tick's close price
double lastClose;

Мы объявили переменную lastClose типа double для хранения цены закрытия последнего тика.

Initialization (OnInit function):

int OnInit()
  {

// Initialize the variable with the current close price
   lastClose = iClose(_Symbol, PERIOD_CURRENT, 0);

   return(INIT_SUCCEEDED);
  }

В функции OnInit мы инициализируем lastClose ценой закрытия самой последней свечи, используя функцию iClose. Параметры _Symbol, PERIOD_CURRENT и 0 указывают текущий символ, текущий таймфрейм и самую последнюю свечу соответственно..

Функция OnTick:

void OnTick()
  {

// Get the current close price 
   double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);

// Check if the close price has changed
   if(currentClose != lastClose)
     {
      // Print a message when the close price changes
      Print("Close price changed! New close price: ", currentClose);

      // Update the last close price
      lastClose = currentClose;
     }


  }

  • В функции OnTick мы получаем текущую цену закрытия с помощью iClose и сохраняем ее в переменной currentClose.
  • Затем проверяем, отличается ли текущая цена закрытия от последней записанной цены закрытия (currentClose != LastClose).
  • Если есть изменение, выводим сообщение, указывающее на изменение, и обновляем переменную lastClose.

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


Заключение

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

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

Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Oluwatosin Mary Babalola
Oluwatosin Mary Babalola | 29 янв. 2024 в 09:33
Спасибо за эту замечательную статью о массивах, удобную для начинающих. Пожалуйста, включите в свои последующие статьи объяснение структур в mql5 для начинающих. С нетерпением жду этого! Спасибо!
Israel Pelumi Abioye
Israel Pelumi Abioye | 29 янв. 2024 в 14:28
Oluwatosin Babalola #:
Спасибо за эту замечательную статью о массивах, удобную для начинающих. Пожалуйста, включите в свои последующие статьи объяснение структур в mql5 для начинающих. С нетерпением жду этого! Спасибо!

Здравствуйте, Олуватосин,

Большое спасибо за ваши теплые слова! Я рад слышать, что вам понравилась статья. Ваша просьба была принята к сведению.

Наиболее известные модификации алгоритма искусственного кооперативного поиска (Artificial Cooperative Search, ACSm) Наиболее известные модификации алгоритма искусственного кооперативного поиска (Artificial Cooperative Search, ACSm)
В данной статье рассмотрим эволюцию алгоритма ACS: три модификации в направлении улучшения характеристик сходимости и результативности алгоритма. Трансформация одного из ведущих алгоритмов оптимизации. От модификаций матриц до революционных подходов к формированию популяций.
Разработка и тестирование торговых систем на основе Канала Кельтнера Разработка и тестирование торговых систем на основе Канала Кельтнера
В этой статье мы рассмотрим торговые системы, использующие очень важную концепцию финансового рынка — волатильность. Мы изучим торговую систему, основанную на канала Кельтнера (Keltner Channel), включая ее реализацию в коде и тестирование на различных активах.
Элементы корреляционного анализа в MQL5: Критерий независимости хи-квадрат Пирсона и корреляционное отношение Элементы корреляционного анализа в MQL5: Критерий независимости хи-квадрат Пирсона и корреляционное отношение
В статье рассматриваются классические инструменты корреляционного анализа. Даются краткие теоретические основы, а также практическая реализация критерия независимости хи-квадрат Пирсона и коэффициента корреляционного отношения.
Нейросети — это просто (Часть 93): Адаптивное прогнозирование в частотной и временной областях (Окончание) Нейросети — это просто (Часть 93): Адаптивное прогнозирование в частотной и временной областях (Окончание)
В данной статье мы продолжаем реализацию подходов ATFNet — модели, которая адаптивно объединяет результаты 2 блоков (частотного и временного) прогнозирования временных рядов