Русский 中文 Español Deutsch 日本語 Português
preview
Master MQL5 from beginner to pro (Part II): Basic data types and use of variable

Master MQL5 from beginner to pro (Part II): Basic data types and use of variable

MetaTrader 5Examples | 30 May 2024, 15:06
5 176 18
Oleh Fedorov
Oleh Fedorov

Introduction

In my previous article we looked at the main programs that are used by MQL5 programmers (and came to the conclusion that the MetaEditor IDE is well suited for beginner needs). In addition, we took a quick look at the concept of a function and created a simple script that prints a message in the system log. These messages can be viewed at the bottom of the terminal window in the Experts tab.

Let me remind you that a function is a description of the action.

We used only predefined functions: OnStart and Print. As for the first function, we filled it with the content. The second one, which displayed the information we needed, was used in a ready-made formed, and we just passed the parameters to it. Programmers can create their own custom functions required to solve their specific tasks.

Each function consists of very elementary steps-actions, which are called statements. Such actions are quite simple: compare two numbers, repeat a piece of code several times, glue two pieces of text together, call another function, etc. There are not many of them. We will consider some of the statements in this article.

A sequence of statements forms an algorithm.

An algorithm consists of clear and understandable instructions for the performer (in this case, the computer) to perform certain actions leading to the solution of a specific, more global, task. There are a huge number of algorithms, since the same problem, as a rule, can be solved in many different ways.

This, in particular, concerns trading. Entering a deal, exiting a deal, printing the corresponding logs and other actions can be implemented in different ways. We need to absolutely clearly explain to the computer what exactly you (or your customer) want in this particular case.

We can say that more complex algorithms consist of simpler ones, and each algorithm is implemented by some function, performing some actions. These actions are applied to data. This data can be Ask and Bid prices or deal volumes arriving several times per second. Other examples of data include points on the screen between which you can draw a straight line or a curve. It can also be a sound which should be played at the time a deal is executed. The data can be a text, such as a list of quotes for a certain period. There can be a lot of different example. I hope the idea is clear.

Now we come to the point that this data must be stored somewhere.

Today we will talk about how data is stored in RAM. The data can be stored in memory as variables or constants.

The differences are obvious:

  • Variables can vary, that is the program has the right to rewrite such data.
  • Constants remain constant (unchanged) throughout the program lifetime, and if the programmer tries to overwrite their values, a compilation error will be returned.

Otherwise, their meaning is absolutely similar: this is a certain area of RAM that stores data, not processor instructions. Usually, people provide meaningful names for these areas of memory in order to understand what they are used for.

The compiler will remove these names, but if you have access to the source code (the text file), you can always understand the purpose of the variable based on its name. Provided, of course, that they are described correctly.

Constants may not have names in some cases. The programmer simply writes what exactly should be processed (for example, the strings that we passed to the Print function). Such nameless constants are referred to as literals.

In this article, we will take a closer look at the basic data types, ways to describe variables and constants, and the basic statements that a programmer can use to create algorithms. This in turn will allow you to create more useful programs than just "Hello, World".


Basic code for testing all expressions from the article

In the previous article, we created a simple program: a script that prints data in the "Experts" tab in the bottom panel of the terminal. Here it is:

//+------------------------------------------------------------------+
//|                                                   HelloWorld.mq5 |
//|                                       Oleg Fedorov (aka certain) |
//|                                   mailto:coder.fedorov@gmail.com |
//+------------------------------------------------------------------+
#property copyright "Oleg Fedorov (aka certain)"
#property link      "mailto:coder.fedorov@gmail.com"
#property version   "1.00"
//#property script_show_inputs

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
  Print("Hello, MQL5 world!");
  }

//+------------------------------------------------------------------+

Example 1. Full text of the simplest script

Today we will work with this code: we will modify it by inserting the lines from the examples inside curly braces (unless the explanations for the examples explicitly indicate some other place for insertion).


Literals

Let's see how we can print data.

We will start with the string.

A string literal is enclosed in double quotes <">. However, not all symbols can be displayed in a straightforward way. Some of them have a special meaning in the language (the same quotes), some are not displayed at all, such as the line feed character.

There is a special convention for such characters: they are written using a backslash:

Print(
    "Symbol <\"> can't be placed"
    " without (\\).\n  And we can carrying over the string now."
  );

Example 2. Using backslashes to print special characters

In example 2, the quotation marks describing the output strings are highlighted in green, and special characters are highlighted in yellow. So, "\n" denotes a line break.

Example of string literal output

Figure 1. Example of string literal output

Please also pay attention to the following feature. In MQL5, very long strings can be split in any place (by placing each part in quotes). There is no need to add any other actions to merge these lines. If the compiler encounters two consecutive string literals (as in the example), it will automatically merge the strings into one large string, and then search for special characters in it.

A complete table of special characters, as well as a more detailed description of character constants, can be found in the official Help.

The second most frequently used type is numbers.

Integer numbers have a common form:

Print(5);

Example 3. Integer output

Decimals are also quite easy to output. The separator between the integer and the decimal part is a dot:

Print(5.5);

Example 4. Decimal output

For very large or very small numbers, you can use floating point notation (sometimes called exponential form):

Print(5.5e3);
Print(5.5e-3);

Example 5. Using floating point literals

The result of a script using all these number forms is shown in Figure 2.

Printing numbers using the Print function

Figure 2. The result of printing numbers by the Print function

Notice how the function transformed the data passed to it in the last two lines. This suggests that the data was recognized as numbers and processed adequately.

Sometimes programmers have to deal with hexadecimal numbers. Beginners rarely need this type, but sometimes it can be useful, for example, to describe color. Hexadecimal numbers consist of numbers (0..9) and/or letters (a..f).

In order for the program to understand that this is a hexadecimal literal, you need to add the "0x" at the beginning of the number (zero and letter x).

The representation is case insensitive.

Print(0xAF35);

Example 6. Using hexadecimal number

Hexadecimal number output

Figure 3. Hexadecimal number output result

The result of the script is shown in Figure 3. The number was converted (to a regular integer), which means that the computer understood it exactly as needed.

An MQL5 programmer very often has to use dates.

For a complete notation of date and time, start it with a capital letter "D", then put an apostrophe < ' >, write the desired date using dots or slashes, if necessary, indicate the time after a space, separating minutes and seconds with colons, and add another apostrophe:

  Print(D'24/12/23');
  Print(D'24.12');
  Print(D'24.12.2023 7:55:34');

Example 7. Using date and time

Note that in the case of the second line, we will receive a compiler warning, saying that the date is not complete. However, the file will be compiled, and all three lines will work correctly:

Incomplete literal warning

Figure 4. Warning (MetaEditor) about incomplete date literal

Results of the script (output of dates)

Figure 5. Results of the date output script (terminal)

Please note that when compiling the first line, the start time of the day was substituted, and when compiling the second line, the compilation time was substituted. Since the format was properly converted, we can conclude that the program understood it correctly.

On any literals, you can perform any actions that are permissible for a given data type.

For example, you can compare numbers, perform arithmetic operations, and pass them to functions as parameters.

Strings can be added (they are glued together) but cannot be subtracted.

Let's look at the following example:

Print( "This will be calculated: "+4+9 );
Print( "This will be calculated: "+(4+9) );

Example 8. Using parentheses when writing expressions.

Compiler warnings about using numbers in a string expression

Figure 6. Compiler warning about using numbers in a string expression

Calculation results

Figure 7. The function outputs the calculation result

Where there were no parentheses, the numbers are simply "glued" to the text. When we used parentheses, everything was calculated correctly. This is a rather elusive error, which is why the compiler warns us about it right away. There are special functions for converting numbers to strings explicitly. But for now, just remember: parentheses are important.


Defining constants using the #define preprocessor directive

If you don't want to get confused in your own code and you want to understand the purposes of the data used in your program, you should always try to set meaningful names for all constants. For this purpose, the #define preprocessor directive is typically used:
#define name value

Example 9. The #define directive

Let me remind you that the preprocessor language is like a "language within a language", and this language describes actions before compilation starts.

As a rule, the preprocessor aims to replace some code fragments with others. For example, the #define directive on the first pass instructs the compiler to replace "name" with "value" throughout our code and only after that start any syntax checks.

For example, we can write:

  #define MY_SUPER_CONSTANT 354
  Print(MY_SUPER_CONSTANT);

Example 10. The #define directive

The program outputs the value, not the name

Figure 8. The program outputs the value of the constant, not the name

When executed, the program will display exactly the number 354, and not the name.

Note that the literal describing the number is not followed by a semicolon.

When declaring constants using a preprocessor directive, a semicolon is not needed.

If we had added a semicolon, the preprocessor would have inserted this sign along with the number, inside the Print parenthesis, and we would have received a compilation error message.

So, remember that we name a constant and in expressions we use its name, not value.

By the way, names are very useful when the same constant is used several times in different places in the program or when several constants have the same value. If the value of a constant changes, it is much easier to change it in a single place, usually at the very beginning of the document or even in a separate file, rather than having to search and change it multiple places.


Description of variables

Remember: if you expect that the data in memory will change in the process of work, use variables.

Describing variables is also quite straightforward. Simply write down what exactly you want to store:

type variable_name;

Example 11. Variable declaration template

This entry means that the compiler must allocate a certain amount of memory for the data. We will see types and sizes in detail later.

This data can now be accessed by name (variable_name).


Identifier conventions

The variable name (often referred to as an identifier), as well as any name you create, must

  • be informative ("chartNumber" is better than "sss"),
  • consist of letters of the Latin alphabet, numbers and an underscore (_). 

The name must NOT

  • start with a number,
  • coincide with reserved words,
  • be longer than 63 characters.

Names are case sensitive. For example, myVariable and MyVariable are two different names. (Obviously, using both of these names in the same file is strongly not recommended).

If you accidentally mix up a case somewhere in the program text, the compiler will throw an error message: "Undeclared variable" so you can easily fix it. But if you describe both variables according to all the rules but use similar names differing only in the case, it will be too easy to get confused.

Otherwise, there are no restrictions. You can even name your variable after some built-in function if you want (hopefully not).


Assignment operator

To write data to a variable, use the assignments operation. Sometimes this is done at the time of declaration – this case is considered as initialization:

// Initialization (at creation)
int counter = 0;
// Normal assignment
counter = 10;

Example 12. Simple assignment

The word int indicates that the variable can only contain data of the integer type.

The sign "=" in this example means the assignment operator. In this case, an integer is written into the cell named 'counter'.

All data that was in the cell before the assignment is lost.

The data in this variable can be used anywhere in the program by name, for example, you can pass them as a parameter to any function or use in an expression (examples will be later in the article).


Assignment operation - specific features

The assignment can be very simple, as described in the previous section. But if you use this operation in expressions, then the following notes will help you use it more effectively.

  • The assignment operation has the lowest priority, so it is executed from right to left. So, there is an expression on the right, and on the left there is a variable where the data is written. First, the expression is:
a = b + c;

Example 13. The assignment has the lowest priority.

The expression above will first add b and c, and then write the result of this expression to the variable a.

  • As a consequence of the previous remark, the value of a variable can be used in an expression, and then the result can be written into the same variable:
a = a — c;

    Example 14. You can use the previous value of a variable in an expression

    • In MQL5, expressions where the same variable appears once on the right and left (as in the example above) can be simplified, by moving the expression sign to the left side of the operation:
    a -= c;

    Example 15. Shorthand use of assignment

    Such short use is valid for any binary (using two values, like a sum or multiplication) operators: multiplication, division, shift, etc. However, note that the variable in this expression should be easily isolated.

    For example, for the expression a = a*(1+1/a) this trick will no longer work if you don't open the brackets, but for a = a*(b+c) it's easy: a *= b+c.

    • If you need to increase or decrease an integer by 1, there is no need to use an assignment at all. Increment and decrement operations can be used instead:
    a++; // Increment. Increase a by 1
    b--; // Decrement. Decreases b by 1
    

    Example 16. Increment and Decrement

    These operations are unary, that is, they require only one variable to operate. Such operations have two forms of notation: prefix and postfix.

    When used in the prefix form, the action will be executed first, and then the result will be used in the expression.

    When used in the postfix form, the old value of the variable will be used first, and then the variable will change by 1:

    int a = 1;
    Print (++a); // 2, and a == 2
    Print (a++); // 2, but a == 3
    

      Example 17. Prefix and postfix forms of increment (decrement is used in exactly the same way)

      • You can use several assignment operators in a row with "cascading". The right-to-left sequence of actions is preserved.
      int a=1, c=3;
      a = c = a+c; // first a+c (4), then c = 4, then a = c (i.e. a = 4)
      

      Example 18. "Cascading" assignment


      Basic data types

      There are relatively many data types.

      Some of them are "simple" (or "basic"). These are types like strings, numbers, dates, colors, etc. Others are "complex"; the MQL5 program developer creates such types. As a rule, "complex" data types are a combination of simple ones, when several variables are combined into one block for greater convenience.

      This article will only cover the basic types. We will talk about complex ones in the next part.

      Integer types

      Integers are the main way a computer "thinks".

      Integer operations on a computer are simple and fast. But if the result of the operation exceeds a certain range of values, this may lead to data loss.

      The second important point: integers can be "signed" or "unsigned".

      If the number is "unsigned", then you can use numbers from 0 to maximum. For example, numbers that occupy 1 byte can grow from 0 to 28-1 = 255, which makes 256 values in total.

      If someone tries to write the number "256" or "-1" into such a variable, an "overflow" will occur. In this case, the result will be within the same limits [0..255], while the rest of the data will be lost. Sometimes this can be useful, but in the vast majority of cases it is better to use other methods for such transformations. For example, you can use the remainder operator (later in this article). It is better to use variables of the types that will accept all your data without loss.

      The names of the types that start with 0 (and actually describe natural numbers) are preceded with the letter "u" (for unsigned).

      "Signed" numbers use the same range of values, divided in half. The first half stores negative numbers, the second half stores positive numbers. Thus, the same single-byte numbers will be correct in the range [-128..127].

      Table 1. Integer data types.

      Name
      Size, bytes
       Minimum value
      Maximum value
      char
      1 (8 bit)
      -128
      127
      uchar
      1 (8 bit)
      0
      255
      short
      2 (16 bits)
      -32 768
      32 767
      ushort
      2 (16 bits)
      0
      65 535
      int
      4 (32 bits)
      -2 147 483 648
      2 147 483 647
      uint
      4 (32 bits)
      0
      4 294 967 295
      long
      8 (64 bits)
      -9 223 372 036 854 775 808
      9 223 372 036 854 775 807
      ulong
      8 (64 bits)
       0  18 446 744 073 709 551 615

      In practice, the most commonly used types are int (because this word is quick to write and data of this type is quite large) and long (the size is sufficient for the vast majority of tasks, the compiler can optimize the bytecode for the best performance on modern computers).

      However, other types are also useful.

      Boolean

      This type is specified by the bool keyword, occupies 1 byte of memory and can take only two values: true or false.

      If absolutely necessary, any number can be used as logical data. For example, if the number is equal to 0, then it is "false"; in all other cases it is "true". However, you should be very careful when using numbers for this purpose.

      Real numbers (aka floating point numbers)

      Table 2. Real data types

      Name
       Size, bytes Minimum positive value  Maximum value  
      float
      4 (32 bits)
      1.175494351e-38
      3.402823466e+38
      double
      8 (64 bits)
      2.2250738585072014e-308
      1.7976931348623158e+308

      The 'double' type is mainly used in modern practice. I haven't seen the 'float' type in other people's code for a very long time. It is probably used for compatibility with older versions. Although, in very large data sets it can be useful to save memory space.

      Real numbers can represent prices, the amount of currency, and other useful concepts.

      They cover a much larger range of values than integers.

      However, it is not very convenient for a computer to work with these types. First, operations with real numbers are slightly slower than with integers. Second, due to the format specifics, calculations are almost always obtained with errors in the last digits. Therefore, instead of 1.0 in some operations you can get 1.00000001 and in another cases 0.99999999.

      Therefore, if it is necessary to compare two real numbers, you should typically take their difference and compare it with some small value which is though definitely larger that the error. It's a more reliable way.

      Date and time

      The type is denoted by the word datetime; it occupies 8 bytes in memory.

      Each variable of this type contains the number of seconds that passed since January 1, 1970, until the required date. So, it is a regular integer.

      The last possible date is December 31, 3000. This is quite enough for our lifetime, so we should not fear the "Year 2000 problem" (if you remember).

      There are special predefined constants:

      • __DATE__ — compilation date
      • __DATETIME__ — compilation date and time
      • You can use the expression __DATETIME__- __DATE__ - it describes only the compilation time, without the date.

      When writing a literal, you can omit everything and write the value as D'' (D and two apostrophes). This notation is equivalent to __DATETIME__. However, this worsens the readability of the code.

      Colors

      Color in MQL5 is determined by a separate type color. When describing a color, you can use a literal:

        color myColor1=C'100,200,30';
        color myColor2=C'0xFF,0x00,0x5A';
      

      Example 19. Describing color using decimal or hexadecimal numbers

      You can also use constants predefined for web colors. Color constant names begin with clr (for example, clrBlue for blue). A complete list of constants can be viewed by typing clr in MetaEditor or visiting the official documentation.

      In example 12 you can see that each color description consists of three fragments. Each such fragment describes the intensity of red, green or blue light of a dot on the screen (Red, Green, Blue = RGB). Together they give a whole variety of colors.

      By mixing red and green, we get all the shades of yellow-brown-orange.

      Red and blue produce violet-pink tones.

      Green and blue give different variations of turquoise, cyan, etc.

      Mixing all three shades in equal proportions gives a range of gray shades: from black - when all components are “off”, that is, have an intensity of 0, to white where all have a maximum intensity of 255 (0xFF).

      If the proportions are unequal, that is, the intensity of each component is different from the others, all other shades on the monitor are obtained. Green usually lightens the overall color, while blue darkens it. Of course, in general, the brighter the component, the lighter it is (and the lighter the overall color).

      All these rules are illustrated in Table 3, where I simply colored the cells with the colors that are available in the editor.

      In practice, most often it is not necessary to know the numerical values for each color, while you can simply select color from a special palette or pass a predefined constant. Anyway, I believe that understanding how everything works is useful.

      Color data takes up 4 bytes in memory, although only 3 of them are used. This has happened historically, and this is a general agreement for any programs that can work with this color description model.

      Table 3. Color examples

      0, 0, 0 156, 15, 15 106, 0, 86 0, 49, 110 0, 110, 41 56, 37, 9 56, 37, 9
      51, 51, 51 191, 3, 3 133, 2, 108 0, 67, 138 0, 137, 44 243, 195, 0 87, 64, 30
      102, 102, 102 226, 8, 0 160, 39, 134 0, 87, 174 55, 164, 44 255, 221, 0 117, 81, 26
      153, 153, 153 232, 87, 82 177, 79, 154 44, 114, 199 119, 183, 83 255, 235, 85 143, 107, 50
      204, 204, 204 240, 134, 130 193, 115, 176 97, 147, 207 177, 210, 143 255, 242, 153 179, 146, 93
      255, 255, 255 249, 204, 202 232, 183, 215 164, 192, 228 216, 232, 194 255, 246, 200 222, 188, 133

      If desired, you can work with color in the same way as with ordinary integers.

      For example, here's the code:

        color a = C'255,0,0';
        color b = C'0,255,0';
        color d = a+b;
        Print(a," ",b," ",d);
      

      Example 20. Using colors in arithmetic expressions

      This will give the following result:

      The results of using colors in arithmetic expressions

      Figure 9. The results of using colors in arithmetic expressions

      Enumerations

      The last basic type is enumeration.

      There are situations when, according to the problem specifics, a variable should take only certain values. For example, a trend can be downward, upward or flat. Also, only the following Buy order types exist: Buy (buy at the market price), Buy Stop (pending until the price reaches a certain level for a breakout) and Buy Limit (pending, expecting a rebound). The days of the week are always the same. I think the principle is clear.

      In such cases we use enumerations.

      The description of enumerations consists of three steps.

      1. In the first step, you need to create the list itself and name it somehow. The resulting name is the type name for any variables or functions. The only difference between this name and the predefined types is that we came up with it ourselves.
      2. In the second step you create a variable of this type.
      3. And finally, in the third step, you can use this variable.
      Example 15 shows how to describe and use an enumeration. To illustrate, I took the direction description (DIRECTION), which can only use three values: "Upward", "Downward" and "Aside".

      //--- First step: creating a new list (new data type)
        enum ENUM_DIRECTION
         {
          Upward,
          Downward,
          Aside
         };
      
      //--- Second step: description (and, if necessary, initialization) of a variable of this type
        ENUM_DIRECTION next=Upward;
      
      //--- Third step: using the variable
        Print(next);
      

      Example 21. Enumeration description and usage

      Typically, an enumeration list is created at the very beginning of a file, immediately after the preprocessor directives. In this case, it will be available to all functions of our application globally.

      Although you can describe it locally, inside some function. Then this enumeration will not be visible from other functions. Most often, this doesn't make much sense, but you never know what tasks you will face.

      Enumeration element names are specified in curly brackets separated by commas.

      After the closing curly brace of any type description (including the enumeration) a semicolon is required. This may not be the case for other blocks.

      Enumerations predefined in the language have names written in capital letters, and these names begin with the prefix ENUM_. You can name your enumerations whatever you want (within the limits), but it is good practice to adhere to the same standards.

      The internal representation for enumerations is a signed integer that occupies 4 bytes in memory.

      If we try to execute the code from example 21, we will see the number 0. This means that when we let MQL5 select numbers for enumeration elements on its own, it starts from scratch.

      But you can specify some other number, you just need to set it explicitly:

      //--- First step: creating a new list (new data type)
        enum DIRECTION
         {
          Upward = 1,
          Downward = -1,
          Aside = 0
         };
      

      Example 22. Setting enumeration values explicitly

      There is no need to specify all values.

      If some values are specified and some are not, MQL5 will select the numbers itself based on the order of the elements and the last highest number. This means that if in example 15 we set Upward = 1 and remove all other initializations, then Downward will be equal to 2, and Aside will be equal to 3. You should check this yourself.


      Expressions and simple operators

      When we work with data, it is important to be able to compare it, perform mathematical operations, etc. Different expressions can be used for different data types.

      Comparison operators

      These operators are meaningful for all data types.

      The comparison result is logical.

      The following comparison operators exist:

      • Greater than ( > ), 
      • Less than ( < ), 
      • Greater than or equal ( >= ), 
      • Less than or equal ( <= ), 
      • equal ( == ), 
      • not equal ( != )

      The priority of all these operations is the same.

      When comparing strings, the computer will follow the arrangement of characters in the encoding. For example, the uppercase "A" comes before the lowercase "a", so it will be smaller. Therefore:

      "An apple" < "a pal" //true

      Example 23. Compare strings. Uppercase is less than lowercase

      If there are several identical symbols in a row, the first unequal symbol is selected for comparison.

      "An apple" > "A pal" //true

      Example 24. The first letters are the same

      The expression in example 24 is correct because the space in the encoding comes before the alphabetic characters, and the first letters are the same.

      If one line has already ended, and the second continues, while the beginning is identical, then the one that has ended is considered less. For example:

      "An apple" < "An apple was found" //true

      Example 25. Different line lengths

      Arithmetic operations

      The result is determined by the data type used in the expression.

      You can perform arithmetic operation on numbers:

      • sign before the number ( -3) (sometimes referred to as the "unary" minus);
      • multiplication ( * ), division ( / ) (for integers rounds down), remainder of division ( % ) (only for integers, 5%2 == 1);
      • addition ( + ), subtraction ( - );
      • increment ( ++ ), decrement ( -- )

      The list is given in order of priority.

      However, it is better not to mix increment and decrement in ordinary expressions with other arithmetic operators, since there may be situations where the result is not defined.

      Operation ( + ) is also defined for strings , but here it means gluing (make one long string from two short strings).

      Bitwise operations

      The result is an integer.

      For integers there is also the following bitwise operations:

      • bitwise negation (~ )
      • shift right (>> )
      • shift left (<< )
      • bitwise "and" (& )
      • bitwise "or" (| )
      • exclusive "or" (^ )

      If you need them, then you are definitely no longer a beginner and will be able to find the required information in the language documentation. :-)

      The list is given in order of priority.

      Logical operators

      The result is logical.

      • logical negation ( ! )
      • logical multiplication - logical "and" ( && );
      • logical addition - logical "or" ( || ).

      The list is given in order of priority.

      There are other operations, which will be discussed in other articles. Also, in other articles we will discuss examples of using all of the above operators in more detail. For now, you can use what you understand or check the language documentation. There shouldn't be any special difficulties.


      Type casting

      Sometimes an arithmetic expression involves multiple data types. For example, using the Print function we constantly print strings along with numbers, and in this article we even came across colors.

      What type will the result be? Here we can choose from two options: either we ourselves determine what we end up with, or we trust the compiler.

      The compiler, of course, is smart and can figure it out. But not always.

      Therefore, let's see what the compiler does and what can be done "manually" so as not to lose important data, not to receive warnings during compilation, and to be confident in the result.

      What does the compiler do?

      First, if the expression uses data of the same type, then the result will be of the same type. This is the easiest case.

      If different types are involved, the compiler will try to expand the result to the most accurate one. For example, if we try to add a 4-byte integer ( int ) with a date ( datetime ), we will get a date (since its range is wider).

      An integer literal is of type int, a floating-point literal is usually of type double unless terminated by a lowercase "f":

      5 + 3.4f + 4.25 // The result is double, since 5 is first converted to float, and then 4.25 sets double precision

      Example 26. Type casting when using literals

      The help documentation includes the type casting priority scheme:

      Type casting priorities

      Figure 10. Type casting priorities

      It should be noted that conversions of signed and unsigned types to each other can potentially lead to data loss, and conversions to the float type can lead to loss of precision.

      Therefore, if you are not sure how the compiler will convert the data, you can think about manually specifying what should be converted to what.

      If you need to convert a result (or a specific value) to a specific type, you can:

      • Write the result into a variable of a certain type. This method is essentially a variant of automatic casting, so it must be used with caution.
      • Use special data conversion functions;
      • Use the short form of type casting.
      (int)a+b // converts a to an integer. b remains unchanged
      double (c+d) // absolutely similar to the previous one. In this case, the result of the summation is converted
      
      // and so on - you can use any suitable type
      
      

      Example 27. Short form of type casting

      Do not forget that parentheses allow you to change the order of operations, since they have the highest priority. If you're not sure, use parentheses.


      Conclusion

      We have covered a large piece of theory about basic data types, variables and expressions. If you understand the material of this and the next article, then you will almost cease to be a beginner, and will move to a higher level. If you understand how variables (the material of this article) and functions (will be in the next one) work, then you can confidently proceed to more complex topics, like OOP.

      OOP, object-oriented programming, is considered complex material. Well, there are more ideological difficulties than technical

      For those who did not understand some points, I recommend re-reading the article (one or more times) very slowly, one concept at a time, checking everything said in the code.

      For those who understand everything and don't want to wait until I'm ready with the next article, I recommend brightening up the wait by writing your own script that will display useful information about your working symbols and balance. Much of this information can be obtained from the AccountInfo and SymbolInfo function families.

      Try finding the full names for each function in these families using MetaEditor, and then look up their descriptions in the help. With these descriptions and material covered in this article, creating this script won't require too much effort on your part.

      P.S. An example of such a script is available in the standard library. If you do not want to write your own script, try to analyze the ready-made one. If you manage to write your own one, compare it with the one from the standard library.

      Translated from Russian by MetaQuotes Ltd.
      Original article: https://www.mql5.com/ru/articles/13749

      Last comments | Go to discussion (18)
      Stanislav Korotky
      Stanislav Korotky | 12 Jun 2024 at 12:34
      Oleh Fedorov #:

      Regarding the code "to think about"... Since I am not familiar with C (both of them), this code looks like a game to me. In the help, the enum type is referred to integer types, and I didn't even think that it is a structure at all. Its meaning is quite different... But thanks, now I'll know - although it's of no use to me personally, I won't use it :-).

      This is the game. A person must have written from the ceiling and mixed up enum and union. His example does not compile. Enum is not a structure but a 4-byte integer. If you take union, it works perfectly well in C++ and MQL.

      PS. Regarding the first example from Jun's interview and the question about how often it happens - of course it doesn't happen in pure form, because the example is refined, but very often signed and unsigned integers are used in code mixed together, and problems are quite probable here.
      Vladimir Simakov
      Vladimir Simakov | 12 Jun 2024 at 13:03
      Stanislav Korotky #:

      This is nonsense. The person must have written from the ceiling and mixed up enum and union. His example does not compile. Enum is not a structure but a 4-byte integer. If you take union, it works perfectly well in C++ and MQL.

      PS. Regarding the first example from Jun's interview and the question about how often it happens - of course it doesn't happen in its pure form, because the example is refined, but very often signed and unsigned integers are used in code mixed together, and here problems are quite likely.

      Naturally union)))))

      About the rest: https: //en.cppreference.com/w/cpp/language/union

      It is undefined behavior to read from the member of the union that wasn't most recently written. 

      And yes, I know what it says next

      Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.

      Except that msvs, from whose help it is taken, doesn't claim the second one, though nobody has come across it yet. g++ and clang haven't looked at what they say about it, but it's hardly different.

      If you want to use it that way, use it - it's your right and your pain, if anything happens))).

      Stanislav Korotky
      Stanislav Korotky | 12 Jun 2024 at 20:25
      Vladimir Simakov #:

      Naturally))))

      About the rest: https: //en.cppreference.com/w/cpp/language/union

      cppreference is a useful resource, but due to its reference nature it cannot contain all the nuances that can be found only in language specifications. To generalise them, it is easier to look at stackoverflow, and as a summary - for primitive types, bitwise "transfer" of values is guaranteed when reading a field, even if it has not been written to.

      Oleh Fedorov
      Oleh Fedorov | 12 Jun 2024 at 22:34
      Stanislav Korotky #:

      This is nonsense. A person must have written from the ceiling and mixed up enum and union. His example does not compile. Enum is not a structure but a 4-byte integer. If you take union, it works perfectly well in C++ and MQL.

      PS. Regarding the first example from Jun's interview and the question about how often it happens - of course it doesn't happen in its pure form, because the example is refined, but very often signed and unsigned integers are used in code mixed together, and problems are quite probable here.

      Phew, the world picture is restored! :-) I'm just finishing about unions and other complex types (hopefully, if there are no more blunders, it should be out next week).

      P.S. MetaEditor does not highlight literals with suffixes `u` and `ull`, but compiles them. At the same time, it highlights `f` but reports an error.... I wonder if this is a bug or a hint? :-)

      Oleh Fedorov
      Oleh Fedorov | 12 Jun 2024 at 23:42

      If you are a beginner and are confused by our debate, let me explain that in C (and also C++) you can add suffixes to numeric literals that change the data type. For example, the suffix `u` turns an ordinary integer(int) into an unsigned integer(uint).

      Try to execute a slightly modified script suggested by Vladimir Simakov (pay attention to the absence of spaces between numbers and letters, it is important):

      void OnStart()
        {
      //---
          
         Print(typename(1));
         Print(typename(-1));
         Print(typename(1 ll));
         
         Print(typename(1 u));
         Print(typename(1 ull));
         
         Print(typename(-1*1 ull));
         
         Print(typename(NULL));
         
         
         Print(-1<1 u); 
         Print(-1*1 ull);
        }

      This script in the first lines outputs the type names of some literals that are compiled in MQL5.

      I may not have compiled all working suffixes, I hope C specialists will correct me. Just try to understand the logic - based on the material of the article, the script output and our discussion, and if everything is not clear at all - ask questions.

      MQL5 Wizard Techniques you should know (Part 21): Testing with Economic Calendar Data MQL5 Wizard Techniques you should know (Part 21): Testing with Economic Calendar Data
      Economic Calendar Data is not available for testing with Expert Advisors within Strategy Tester, by default. We look at how Databases could help in providing a work around this limitation. So, for this article we explore how SQLite databases can be used to archive Economic Calendar news such that wizard assembled Expert Advisors can use this to generate trade signals.
      Data Science and Machine Learning (Part 23): Why LightGBM and XGBoost outperform a lot of AI models? Data Science and Machine Learning (Part 23): Why LightGBM and XGBoost outperform a lot of AI models?
      These advanced gradient-boosted decision tree techniques offer superior performance and flexibility, making them ideal for financial modeling and algorithmic trading. Learn how to leverage these tools to optimize your trading strategies, improve predictive accuracy, and gain a competitive edge in the financial markets.
      Developing a multi-currency Expert Advisor (Part 2): Transition to virtual positions of trading strategies Developing a multi-currency Expert Advisor (Part 2): Transition to virtual positions of trading strategies
      Let's continue developing a multi-currency EA with several strategies working in parallel. Let's try to move all the work associated with opening market positions from the strategy level to the level of the EA managing the strategies. The strategies themselves will trade only virtually, without opening market positions.
      Population optimization algorithms: Artificial Multi-Social Search Objects (MSO) Population optimization algorithms: Artificial Multi-Social Search Objects (MSO)
      This is a continuation of the previous article considering the idea of social groups. The article explores the evolution of social groups using movement and memory algorithms. The results will help to understand the evolution of social systems and apply them in optimization and search for solutions.