Все, что вам нужно знать о структуре программы MQL5
Введение
Каждая программа на любом языке программирования имеет структуру. Усвоив ее, мы можем беспрепятственно создавать и улучшать наши собственные программы. Программы на языке MQL5 также имеют свою структуру, и разработчик должен ее понимать, чтобы обеспечить плавное и эффективное достижение целей своего проекта. Мы изучим структуру программы на MQL5, рассмотрев следующие темы:
- Препроцессор
- Макроподстановка(#define)
- Свойства программ(#property)
- Включение файлов(#include)
- Импорт функций(#import)
- Условная компиляция (#ifdef, #ifndef, #else, #endif)
- Входные и глобальные переменные
- Функции, классы
- Обработчики событий
- Примеры программ 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"
Эти значения мы можем увидеть в окне программы:
Как мы видим на предыдущем рисунке, у нас есть определенные свойства, которые нам нужны на общей вкладке при запуске советника. Текст 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;
После этого мы можем найти окно ввода, определяемое пользователем:
Как видим, мы можем установить период, сдвиг и тип скользящей средней. Мы также можем определить, как выглядят входные параметры на вкладке "Входные параметры", поместив комментарий с тем, что нам нужно видеть в окне, так же, как показано ниже для того же предыдущего примера:
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
На вкладке "Входные параметры" мы можем найти следующие параметры:
Как мы видим, параметры выглядят иначе, чем на предыдущем рисунке. Вы можете узнать больше об 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
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована статья Все, что вам нужно знать о структуре программы MQL5:
Автор: Mohamed Abdelmaaboud
Хотелось бы поменьше таких перепечаток документации без объяснений.
+