English 中文 Español Deutsch 日本語 Português
preview
Все, что вам нужно знать о структуре программы MQL5

Все, что вам нужно знать о структуре программы MQL5

MetaTrader 5Трейдинг | 20 октября 2023, 13:26
1 455 2
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Введение

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

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

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


Препроцессор

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

Все директивы препроцессора начинаются с (#). Эти директивы не считаются языковыми утверждениями. То есть они не должны заканчиваться точкой с запятой (;). Включение точки с запятой в конце директивы препроцессора может привести к ошибкам в зависимости от типа директивы.

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

  • Макроподстановка (#define)
  • Свойства программы (#property)
  • Включение файлов (#include)
  • Импорт функций (#import)
  • Условная компиляция (#ifdef, #ifndef, #else, #endif)

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

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

#define identifier replacement-value

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

#define identifier (param1, param2,... param5)

Те же правила переменных управляют идентификатором констант:

  • Значение может быть любого типа, например integer, double или string.
  • Выражение может состоять из нескольких токенов. Оно заканчивается, когда заканчивается строка, и не может быть перенесено на следующую строку кода.

Ниже приведен пример:

//Parameter-free format
#define INTEGER               10                                     //int
#define DOUBLE                10.50                                  //double
#define STRING_VALUE      "MetaQuotes Software Corp."                //str
#define INCOMPLETE_VALUE INTEGER+DOUBLE                              //Incomlete
#define COMPLETE_VALUE (INTEGER+DOUBLE)                              //complete
//Parametic format
#define A 2+3
#define B 5-1
#define MUL(a, b) ((a)*(b))
double c=MUL(A,B);
//function to print values
void defValues()
  {

   Print("INTEGER Value, ",INTEGER);         //result: INTEGER Value, 10
   Print("DOUBLE Value, ",DOUBLE);           //result: DOUBLE Value, 10.50
   Print("STRING Value, ",STRING_VALUE);     //result: STRING Value, MetaQuotes Software Corp.
   Print("INCOMPLETE Value, ",INCOMPLETE_VALUE*2);     //result: INCOMPLETE Value, 31
   Print("COMPLETE Value, ",COMPLETE_VALUE*2);     //result: STRING Value, 41
   Print("c= ",c);                                  //result: c= 41
  }

Существует также директива препроцессора (#undef), которая отменяет то, что было объявлено или определено ранее.

Свойства программы (#property):

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

Ниже приведен пример директивы препроцессора этого типа:

#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "Property preprocessor"

Эти значения мы можем увидеть в окне программы:

property

Как мы видим на предыдущем рисунке, у нас есть определенные свойства, которые нам нужны на общей вкладке при запуске советника. Текст Copyright 2023, MetaQuotes Ltd. - это гиперссылка. При наведении на нее мы видим, куда она ведет (свойство link).

Включение файлов (#include):

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

Существует два формата включения файлов с помощью директивы #include:

#include <File_Name.mqh>
#include "File_Name.mqh"

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

Импорт функций (#import):

Директива #import используется для импорта функций в программу из скомпилированных модулей MQL5 (файлы *.ex5) и из модулей операционной системы (файлы *.dll). Функция должна быть полностью описана, а ее формат должен быть таким же, как следующий:

#import "File_Name"
    func1 define;
    func2 define;
    ...
    funcN define;
#import

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

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

#ifdef identifier
   //If the identifier has been defined, the code here will be compiled.
#endif
#ifndef identifier
   // If the identifier is not defined, the code here will be compiled.
#endif

Как я уже говорил, если мы перейдем на новую строку, директивы препроцессора не будут продолжены, но в данном случае за директивой этого типа может следовать любое количество строк при использовании #else и #endif. При true строки между этими #else и #endif будут игнорироваться, но если условие не выполнено, игнорируются строки между проверкой и #else (или #endif, если первый отсутствует).

Вы можете узнать больше о препроцессоре в MQL5 из документации.


Входные и глобальные переменные

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

Ниже приведен формат входных переменных:

input int            MA_Period=20;
input int            MA_Shift=0;
input ENUM_MA_METHOD MA_Method=MODE_SMA;

После этого мы можем найти окно ввода, определяемое пользователем:

inputs

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

input int            MA_Period=20;        //Moving Average Period 
input int            MA_Shift=0;          //Moving Average Shift
input ENUM_MA_METHOD MA_Method=MODE_SMA;  //Moving Average Type

На вкладке "Входные параметры" мы можем найти следующие параметры:

inputs1

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

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

int Globalvar;   // Global variable before or outside the event handler and functions
int OnInit()
  {
   ...
  }

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

Вы можете узнать больше о глобальных переменных в MQL5 из документации.


Функции, классы

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

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

Формат функций следующий:

returnedDataType functionName(param1, param2)
{
        bodyOfFunction
}

Формат классов следующий:

class Cobject
{
   int var1;       // variable1
   double var2;    // variable1
   void method1(); // Method or function1
};

Вы можете узнать больше о функциях и классах в MQL5 из документации.


Обработчики событий

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

В зависимости от типа программы существуют разные обработчики событий:

Обработчик события Описание Формат 
OnStart Можно использовать в программах типа скриптов для вызова функции при возникновении события запуска.
  • Версия с возвращаемым значением: 
int  OnStart(void);
  • Версия без возвращаемого значения:
void  OnStart(void);
OnInit Можно использовать в программах советников и индикаторов для вызова функции при инициализации программы.
  •  Версия с возвращаемым значением:
int  OnInit(void);
  • Версия без возвращаемого значения:
void  OnInit(void);

OnDeinit Можно использовать в программах советников и индикаторов для вызова функции при деинициализации программы
void  OnDeinit(
   const int  reason         // deinitialization reason code
   );
OnTick Можно использоваться в советниках и индикаторах для вызова функции при получении новых котировок.
void  OnTick(void);
OnCalculate Можно использовать в индикаторах для вызова функции при отправке события Init и при любом изменении ценовых данных.
  • Расчеты на основе массива данных
int  OnCalculate(
   const int        rates_total,       // price[] array size
   const int        prev_calculated,   // number of handled bars at the previous call
   const int        begin,             // index number in the price[] array meaningful data starts from
   const double&    price[]            // array of values for calculation
   );
  • Расчеты на основе временного ряда текущего таймфрейма
int  OnCalculate(
   const int        rates_total,       // size of input time series
   const int        prev_calculated,   // number of handled bars at the previous call
   const datetime&  time{},            // Time array
   const double&    open[],            // Open array
   const double&    high[],            // High array
   const double&    low[],             // Low array
   const double&    close[],           // Close array
   const long&      tick_volume[],     // Tick Volume array
   const long&      volume[],          // Real Volume array
   const int&       spread[]           // Spread array
   );
OnTimer Можно использовать в советниках и индикаторах для вызова функции при событии таймера (Timer).
void  OnTimer(void);
OnTrade Можно использовать в советниках для вызова функции при завершении торговой операции на торговом сервере.
void  OnTrade(void);
OnTradeTransaction Можно использоваться в советниках для вызова функции при совершении определенных действий на торговом счете.
void  OnTradeTransaction()
   const MqlTradeTransaction&    trans,     // trade transaction structure
   const MqlTradeRequest&        request,   // request structure
   const MqlTradeResult&         result     // response structure
   );
OnBookEvent Можно использовать в советниках для вызова функции при изменении глубины рынка.
void  OnBookEvent(
   const string&  symbol         // symbol
   );
OnChartEvent Можно использовать в индикаторах для вызова функции, когда пользователь работает с графиком.
void  OnChartEvent()
   const int       id,       // event ID 
   const long&     lparam,   // long type event parameter
   const double&   dparam,   // double type event parameter
   const string&   sparam    // string type event parameter
   );
OnTester Можно использовать в советниках для вызова функции по окончании тестирования советника на исторических данных.
double  OnTester(void);
OnTesterInit Можно использовать в советниках для вызова функции с началом оптимизации в тестере стратегий до первого прохода оптимизации
  • Версия с возвращаемым значением
int  OnTesterInit(void);
  • Версия без возвращаемого значения
void  OnTesterInit(void);
OnTesterDeinit Можно использовать для вызова функции после окончания оптимизации советника в тестере стратегий.
void  OnTesterDeinit(void);
OnTesterPass Можно использовать в советниках для вызова функции при получении новой порции данных.
void  OnTesterPass(void);

Вы можете узнать больше об обработке событий из документации по MQL5.


Пример программы на MQL5

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

Скрипт:

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

//+------------------------------------------------------------------+
//|                                       Script program example.mq5 |
//|                                   Copyright 2023, MetaQuotes Ltd.|
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//property preprocessor
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
//inputs
input int userEntryNum1;
input int userEntryNum2;
//global variable
int result;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
//event handler
void OnStart()
  {
   result=userEntryNum1+userEntryNum2;
   Print("Result: ", result);
  }
//+------------------------------------------------------------------+

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

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

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


Заключение

Мы разобрали структуру MQL5-программы и можем определить, что нам нужно в качестве компонентов:

  • Препроцессор
    • Макроподстановка(#define)
    • Свойства программ(#property)
    • Включение файлов(#include)
    • Импорт функций(#import)
    • Условная компиляция (#ifdef, #ifndef, #else, #endif)
  • Входные и глобальные переменные
  • Функции и классы
  • Обработчики событий
    • OnStart
    • OnInit
    • OnDeinit
    • OnTick
    • OnCalculate
    • OnTimer
    • OnTrade
    • OnTradeTransaction
    • OnBookEvent
    • OnChartEvent
    • OnTester
    • OnTesterInit
    • OnTesterDeinit
    • OnTesterPass

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

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

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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Valeriy Yastremskiy
Valeriy Yastremskiy | 20 окт. 2023 в 14:05
Хотелось бы поменьше таких перепечаток  документации без объяснений. И про структуру ни слова) 
Aleksandr Slavskii
Aleksandr Slavskii | 20 окт. 2023 в 15:31
Valeriy Yastremskiy #:
Хотелось бы поменьше таких перепечаток  документации без объяснений.

+

Нейросети — это просто (Часть 60): Онлайн Трансформер решений (Online Decision Transformer—ODT) Нейросети — это просто (Часть 60): Онлайн Трансформер решений (Online Decision Transformer—ODT)
Последние 2 статьи были посвящены методу Decision Transformer, который моделирует последовательности действий в контексте авторегрессионной модели желаемых вознаграждений. В данной статье мы рассмотрим ещё один алгоритм оптимизации данного метода.
Сезонность на рынке форекс и возможности ее использования Сезонность на рынке форекс и возможности ее использования
Каждый современный человек знаком с понятием сезонности, например, все мы привыкли к росту цен свежих овощей в зимний период или подорожанию топлива в сильные морозы, но мало кто знает, что подобные закономерности существуют и на рынке форекс.
Эксперименты с нейросетями (Часть 7): Передаем индикаторы Эксперименты с нейросетями (Часть 7): Передаем индикаторы
Примеры передачи индикаторов в перцептрон. В статье даются общие понятия, представлен простейший готовый советник, результаты его оптимизации и форвард тестирования.
Запускаем MetaTrader VPS впервые — пошаговая инструкция Запускаем MetaTrader VPS впервые — пошаговая инструкция
Всем, кто использует торговые советники или подписки на сигналы, рано или поздно понадобится надежный круглосуточный хостинг для торговой платформы. Мы рекомендуем использовать MetaTrader VPS по целому ряду причин. Платить и управлять сервисом можно через аккаунт MQL5.community. Если у вас еще нет аккаунта на MQL5.com — зарегистрируйтесь и укажите его в настройках платформы.