Русский 中文 Español Deutsch 日本語 Português
preview
Everything you need to learn about the MQL5 program structure

Everything you need to learn about the MQL5 program structure

MetaTrader 5Trading | 3 August 2023, 12:29
7 593 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Introduction

Every software in any programming language has a structure, after understanding this structure we can create or develop our software smoothly. MQL5 language's programs are the same as any other programming language has their structure and it is supposed to be understood by the developer to achieve the objectives of his project smoothly and effectively. In this article, we will provide information in this context to try to deliver its content easily as possible. We will learn the structure of any MQL5 program by covering the following topics:

After the previous topics, it is supposed that you will be understanding the structure of any MQL5 program very well and you can create or develop any software based on this structure smoothly and effectively.

Disclaimer: All information provided 'as is' only for educational purposes and is not prepared for trading purposes or advice. The information does not guarantee any kind of result. If you choose to use these materials on any of your trading accounts, you will do that at your own risk and you will be the only responsible.


Preprocessor

In this part, we will learn about the preprocessor in detail as a programming concept. The preprocessor is a crucial step in the compilation process. It occurs before the actual compilation of a program. During the preprocessing step, various actions are performed, such as including files, determining software properties, defining constants, and importing functions.

All preprocessor directives start with (#).  These directives are not considered language statements. As a result, they should not be ended with a semicolon (;). Including a semicolon at the end of a preprocessor directive can lead to errors based on the type of directive.

In other words, we can say that the preprocessor is meant to preparation of the program source code before the process of compilation. There are many types of preprocessor directives based on parameters that we need to determine in the MQL5 program the same as the following:

  • Macro substitution (#define)
  • Program Properties (#property)
  • Including Files (#include)
  • Importing Functions (#import)
  • Conditional Compilation (#ifdef, #ifndef, #else, #endif)

Macro substitution (#define):

The #define preprocessor directive can be used to create symbolic constants or to define constants to be used in the program. If you do not know what is a constant, it is an identifier that has a value that does not change. We can say also that the #define directive can be used to assign mnemonic names to constants as we will use a replacement value for a specific identifier. The first format of this preprocessor directive is the same as the following:

#define identifier replacement-value

So, we have this line of code in our program which means that the identifier will be replaced by a replacement value before compiling the program. This format is the #define directive without parameters or parameter-free format and there is another format in MQL5 which is the parametric format with a maximum allowed eight parameters that can be used with the #define directive the same as the following:

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

The same rules of the variables governed the constants identifier:

  • The value may be any type like integer, double, or string
  • The expression can be several tokens and it ends when the line is ended and cannot be moved to the next line of code

The following is an example of that:

//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
  }

There is the (#undef) preprocessor directive also which cancels what was declared or defined before.

Program Properties (#property):

When we create our software we may find ourselves that we need to specify additional parameters, we can do that by using #property. These properties must be specified in the main mql5 file not in the include file and those that are specified in include files will be ignored. So, we can say that the #property directive specifies additional properties for the program if you ask about what we need to specify in this context we can answer this question that we have many things like for example, indicator, script, descriptive information, and library properties. The same as other preprocessor directives the #property will be specified at the top part of the source code and they will be displayed on the common tab in the program window when executing it.

The following is an example of this type of preprocessor directive:

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

We can see these values in the program window the same in the following picture:

property

As we can see in the previous picture we have our defined properties as we need in the common tab when executing the EA and the text of copyright 2023, MetaQuotes Ltd. is a hyperlink when hovering it we can see and when pressing it will lead to the link of the link property.

Including Files (#include):

As usual, all #include directives will be placed at the beginning of the program. It specifies an included file to be included in a specific software which means that the included file became a part of the software and we can use its content like variables, functions, and classes.

There are two formats to include files by the #include directive:

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

The difference between these two formats is the location where we need the compiler to look for the file to be included, the first one is letting the compiler look for the file in the Include folder of MetaTrader 5 installation or the standard library header file and the second one is letting the compiler to look for the file in the same directory as the program file.

Importing Functions (#import):

The #import directive is used to import functions to the software from compiled MQL5 modules (*.ex5 files) and from operating system modules (*.dll files). the function must be fully described and its format the same as the following:

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

Conditional Compilation (#ifdef, #ifndef, #else, #endif):

The conditional compilation allows us to control the executions of preprocessing directives in addition to the compilation of the program. It enables us to control compiling or skipping a part of the program code based on a specific condition that can be one of the following formats:

#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

As we mentioned before that if we moved to a new line preprocessor directives will not continue but here this type of directive can be followed by any number of lines by using the #else and #endif. If the condition is true, lines between these two #else and #endif will be ignored but if the condition is not fulfilled, lines between checking and the #else (or #endif if the former is absent) are ignored.

You can learn more about the Preprocessor in MQL5 from the MQL reference.


Input and Global Variables

In this part, we will identify other components of the structure of the MQL5 program after preprocessor directives which are input and global variables. We'll start with the input variables, which define the external variable after writing the input modifier we specify the data type. So, we have the input modifier and values of the input variable, the input modifier can not be modified inside the mql5 program but values can be changed only by the user of the program from the Inputs window or tab of program properties. When we define these external variables by the input modifier are always reinitialized before the OnInIt() is called.

The following is for the format of the input variables:

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

After that, we can find the window of input to be determined by the user the same as in the following picture:

inputs

As we can see that we can define the MA period, MA shift, and the type of MA. We can also determine how the input parameters look in the Inputs tab by placing a comment with what we need to see in the window the same as the following for the same previous example:

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

We can find parameters in the Inputs tab the same as the following:

inputs1

As we can see the parameters look different than what we saw in the previous picture. You can learn more about the Input Variables from the MQL5 reference.

Global variables must be created outside event handlers or created functions at the same level of functions and if we want to see an example of these global variables we can see that the same as the following:

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

So, we can say that the global variables scope is the entire program and they are accessible from all functions in the program, initialized once when the program is loaded and before the OnInit event handling or OnStart() event handling and we will talk about event handlers later but here I mention them to present position of global variables in the MQL5 program structure.

You can learn more about Global Variables from the MQL5 reference.


Functions, Classes

In this part, we will talk about other components of the MQL5 program structure after preprocessors, input, and global variables which are functions and classes. There is a previous article about functions in detail you can read it to learn about these interesting topics in detail through the article of Understanding functions in MQL5 with applications. If you need to read something about classes in the context of understanding Object-Oriented-Programming (OOP) in MQL5 also you can read my previous article about that through the article of Understanding MQL5 Object-Oriented Programming (OOP) I hope you find them useful.

Here, we will mention the position of this important component in any software the same as custom classes as we can define them anywhere in the software and it can be defined in include files that can be included by using the #include directive the same as we mentioned in the preprocessor topic. they can be placed before or after event handlers and below input and global variables.

The format of functions is the same as the following:

returnedDataType functionName(param1, param2)
{
        bodyOfFunction
}

The format of classes is the same as the following:

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

You can learn more about Functions and Classes from the MQL5 reference.


Event Handlers

In this part, we will share information about event handlers which are very important components in the mql5 program. The event handler is an executable function when a specific event occurs like when receiving a new price quote which is the new tick event occurs by the expert advisor then the OnTick() event handler will be executable as this event handler has the body of code that can be run when receiving a new price or tick occurs.

Based on the type of the MQL5 program, there are different event handlers and the following is for these event handlers:

Event handler Description Format 
OnStart This handler can be used in script-type programs to call a function when the start event occurs.
  • Version with a returning value: 
int  OnStart(void);
  • Version without a returning value:
void  OnStart(void);
OnInit It can be used in EAs and indicators programs to call a function when initializing the program
  •  Version with a returning value:
int  OnInit(void);
  • Version without a returning value:
void  OnInit(void);

OnDeinit It can be used in EAs and indicators programs to call a function when de-initializing the program
void  OnDeinit(
   const int  reason         // deinitialization reason code
   );
OnTick It can be used in EAs and indicators to call the function when receiving new quotes
void  OnTick(void);
OnCalculate It can be used in the indicators to call a function when the Init event is sent and at any change of price data
  • Calculations based on the data array
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
   );
  • Calculations based on the current timeframe time-series
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 It can be used in the EAs and indicators to call a function when the Timer periodic event occurs by the trading terminal
void  OnTimer(void);
OnTrade It can be used in the EAs to call a function when a trade operation is completed on a trade server
void  OnTrade(void);
OnTradeTransaction It can be used in the EAs to call a function when performing some definite actions on a trade account
void  OnTradeTransaction()
   const MqlTradeTransaction&    trans,     // trade transaction structure
   const MqlTradeRequest&        request,   // request structure
   const MqlTradeResult&         result     // response structure
   );
OnBookEvent It can be used in the EAs to call a function when the depth of the market is changed
void  OnBookEvent(
   const string&  symbol         // symbol
   );
OnChartEvent It can be used in the indicators to call a function when the user is working with a chart
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 It can be used in the EAs to call a function when testing of an Expert Advisor on history data is over
double  OnTester(void);
OnTesterInit It can be used in the EAs to call a function with the start of optimization in the strategy tester before the first optimization pass
  • Version with a returning value
int  OnTesterInit(void);
  • Version without a returning value
void  OnTesterInit(void);
OnTesterDeinit It can be used in the EAs to call a function after the end of optimization of an Expert Advisor in the strategy tester
void  OnTesterDeinit(void);
OnTesterPass It can be used in the EAs to call a function when a new data frame is received
void  OnTesterPass(void);

You can learn more about Event Handling from the MQL5 reference.


MQL5 Program example

In this part, we will apply what we learned now to create a simple application using the right MQL5 structure. We mentioned that we can use components of the MQL5 structure based on the type of program and the needed task because there are no obligations to use some of these components like for example using the #include preprocessor because it might be no need for including any external file the same as the #property because it is an optional to use it or not in addition to it may or may not need to create custom classes or functions in your program. Anyway, you will use what is necessary for your program and the following are some simple applications to present all needed structure components based on different program types.

Script type:

The following is a simple example of a script MQL5 program that is able to calculate and add two numbers entered by the user by using inputs and print the result in the Expert tab by using the Print function. What I need to mention here is that here in this script program we will add a #property that allows showing script inputs to enter numbers by the user.

//+------------------------------------------------------------------+
//|                                       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);
  }
//+------------------------------------------------------------------+

If we want to create another EA or indicator program, we need to use different event handlers based on the program type. For example, an EA program can execute an action when receiving a new tick by using the OnTick() event handler.

Now that we have identified the structure of the MQL5 program, we see that certain components vary depending on the program type and its objectives or tasks. This understanding helps us identify the position of each component in the software.

To apply this knowledge, we can start with a simple script program, as mentioned earlier.


Conclusion

After what we mentioned through topics of this article, it is supposed that you understood the structure of any MQL5 program and you are able to identify what you need as components to create your MQL5 software based on its type as we learned what we need to learn to build out MQL5 structure which is the same as the following sequence:

  • The Preprocessor
    • Macro substitution(#define)
    • Program Properties(#property)
    • Including Files(#include)
    • Importing Functions(#import)
    • Conditional Compilation (#ifdef, #ifndef, #else, #endif)
  • Input and Global Variables
  • Functions and classes
  • Event Handlers
    • OnStart
    • OnInit
    • OnDeinit
    • OnTick
    • OnCalculate
    • OnTimer
    • OnTrade
    • OnTradeTransaction
    • OnBookEvent
    • OnChartEvent
    • OnTester
    • OnTesterInit
    • OnTesterDeinit
    • OnTesterPass

I hope that you found this article helpful in building your MQL5 program. It is essential to understand its context for a smooth and effective process. If you want to learn more about creating a trading system using popular technical indicators, you can refer to my previous articles published on this topic.

Additionally, I have written about creating and using custom indicators in any EA and other essential topics in MQL5 programming, such as Object-Oriented Programming (OOP) and functions. I believe these articles will be valuable in your learning and trading journey.

Attached files |
Developing a Replay System — Market simulation (Part 04): adjusting the settings (II) Developing a Replay System — Market simulation (Part 04): adjusting the settings (II)
Let's continue creating the system and controls. Without the ability to control the service, it is difficult to move forward and improve the system.
Trading strategy based on the improved Doji candlestick pattern recognition indicator Trading strategy based on the improved Doji candlestick pattern recognition indicator
The metabar-based indicator detected more candles than the conventional one. Let's check if this provides real benefit in the automated trading.
The RSI Deep Three Move Trading Technique The RSI Deep Three Move Trading Technique
Presenting the RSI Deep Three Move Trading Technique in MetaTrader 5. This article is based on a new series of studies that showcase a few trading techniques based on the RSI, a technical analysis indicator used to measure the strength and momentum of a security, such as a stock, currency, or commodity.
Category Theory in MQL5 (Part 15) : Functors with Graphs Category Theory in MQL5 (Part 15) : Functors with Graphs
This article on Category Theory implementation in MQL5, continues the series by looking at Functors but this time as a bridge between Graphs and a set. We revisit calendar data, and despite its limitations in Strategy Tester use, make the case using functors in forecasting volatility with the help of correlation.