Русский 中文 Español Deutsch 日本語 Português
preview
Structures in MQL5 and methods for printing their data

Structures in MQL5 and methods for printing their data

MetaTrader 5Examples | 19 October 2023, 13:57
4 215 0
Artyom Trishkin
Artyom Trishkin

Contents


Introduction

The structure is a convenient tool for storing, recording and retrieving logically related data belonging to any definition from a single variable.

MQL5 features 12 predefined structures intended for storing and transmitting service information:

The MqlParam and MqlTradeRequest structures transmit technical information for creating indicators and sending trading requests to the server. We fill in the required fields of the structures in accordance with the required result of sending data in the completed structure. In other words, these structures do not particularly need to print the data the fields of these structures were filled with by a programmer.
But the remaining structures return query results, and each field is filled in either by the terminal subsystem or by the trade server. Obtaining data from these structures, analyzing programmatically filled fields of structures, or printing them to a log for subsequent manual analysis is very convenient and necessary both for making decisions programmatically and for understanding and finding the location of a logical error.

In order to print all the fields of a structure, there is a standard ArrayPrint() function, which displays the data contained in the array with the type of the handled structure in a convenient tabular format. But sometimes we need to print data from the structure in another format, which may be more convenient than a tabular representation. For example, we may need to display all the fields of the structure in one line - with headers and corresponding data. This may be more convenient for analyzing large amounts of data. At the same time, sometimes we need to see a more detailed view - with a description of the structure fields and a different representation of the corresponding data.

In the current article, we will look at standard tools for viewing structure data and create custom functions for different presentation of structure data in the journal.


MqlDateTime structure

The data structure contains eight fields of int type.

struct MqlDateTime
  {
   int year;           // year
   int mon;            // month
   int day;            // day
   int hour;           // hour
   int min;            // minutes
   int sec;            // seconds
   int day_of_week;    // day of the week (0-Sunday, 1-Monday, ... ,6-Saturday)
   int day_of_year;    // number of a day in the year (1st of January has number 0)
  };

The standard functions TimeCurrent(), TimeGMT(), TimeLocal(), TimeTradeServer() and TimeToStruct() are used to fill in the structure fields.

The first four functions, in addition to returning the current date, GMT date, local computer time and trade server date, each have one overload. In the formal parameters of the function, you can pass a date structure to it by reference. After executing the function, the structure fields passed to the function will be filled with the date data returned by the function.

The TimeToStruct() function is specifically designed to fill a structure from the datetime value type (number of seconds since 01.01.1970) into the MqlDateTime structure type variable.

bool  TimeToStruct(
   datetime      dt,            // date and time
   MqlDateTime&  dt_struct      // structure for accepting values 
   );

Return true if successful, otherwise - false. After its operation, the date structure will be filled with time data passed in the first parameter in a variable of datetime type.

Our date structure is complete, but how can we print it? There is a standard TimeToString() function converting a value containing the time in seconds since 01.01.1970 into a string of the "yyyy.mm.dd hh:min:sec" format.

But the function does not work with the MqlDateTime structure, but with a datetime date. So, should we convert the structure back into a time value? Of course, not. Each of the functions is intended for its own purposes. We can separately take any component of the date and time from the date structure - a year, a month, an hour, a minute, a day of the week, etc... But how can we display all the structure data?
The ArrayPrint() function, which logs an array of a simple type or a simple structure, is suitable for this task. It displays data in the form of a table, where the columns are the fields of the structure, and the rows represent array cells. In other words, to display the structure of just one date, we need an array of 1. For a trading week, the array size will usually be 5 trading days provided that data is obtained from D1 chart.


MqlDateTime, printing methods

Such a script prints a date structure obtained from the current time in the journal using ArrayPrint():

void OnStart()
  {
//--- Declare a date structure variable
   MqlDateTime  time;
//--- Get the current time and at the same time fill in the date structure
   TimeCurrent(time);
//--- Declare an array with the MqlDateTime type and write the data of the filled structure into it

   MqlDateTime array[1];
   array[0]=time;
//--- Display the header and time using the standard ArrayPrint()
   Print("Time current (ArrayPrint):");
   ArrayPrint(array);
   /* Sample output:
      Time current (ArrayPrint):
          [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
      [0]   2023     7    17     12     8    37             1           197
   */
  }

Respectively, the function that takes a date structure and prints it in the journal will look like this:

//+------------------------------------------------------------------+
//| Take a date structure and display its data to the journal.       |
//| Use ArrayPrint() for display                                     |
//+------------------------------------------------------------------+
void MqlDateTimePrint(const MqlDateTime& time_struct)
  {
//--- Declare an array with the MqlDateTime type and write the data of the obtained structure into it
   MqlDateTime array[1];
   array[0]=time_struct;
//--- Print the array
   ArrayPrint(array);
   /* Sample output:
      Time current (ArrayPrint):
          [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
      [0]   2023     7    17     12     8    37             1           197
   */
  }

The function allows printing in the journal one date passed to it in the time_struct variable.

The script that logs a single date structure using the function presented above:

void OnStart()
  {
//--- Declare a date structure variable
   MqlDateTime  time;
//--- Get the current time and at the same time fill in the date structure
   TimeCurrent(time);
//--- Display the header and time using the standard ArrayPrint()
   Print("Time current (ArrayPrint):");
   MqlDateTimePrint(time);
   /* Sample output:
      Time current (ArrayPrint):
          [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
      [0]   2023     7    17     12     8    37             1           197
   */
  }


If we need to print an array of dates (which is the main objective of ArrayPrint() after all), then we need to pass the data array to the datetime function, fill in the MqlDateTime array and print it.

The function that accepts the datetime array and prints the MqlDateTime array:

//+------------------------------------------------------------------+
//| Accept the datetime array, convert it into MqlDateTime and       |
//| display converted data into the journal.                         |
//| Use ArrayPrint() for display                                     |
//+------------------------------------------------------------------+
void MqlDateTimePrint(const datetime& array_time[])
  {
//--- Declare a dynamic array of the MqlDateTime type
   MqlDateTime array_struct[];
//--- Get the size of the array passed to the function
   int total=(int)array_time.Size();
//--- If an empty array is passed, report on that and leave the function
   if(total==0)
     {
      PrintFormat("%s: Error. Empty array.",__FUNCTION__);
      return;
     }
//--- Change the size of the MqlDateTime array to match the size of the datetime array
   ResetLastError();
   if(ArrayResize(array_struct,total)!=total)
     {
      PrintFormat("%s: ArrayResize() failed. Error %s",__FUNCTION__,(string)GetLastError());
      return;
     }
//--- Convert dates from the datetime array into the date structure in the MqlDateTime array
   for(int i=0;i<total;i++)
     {
      ResetLastError();
      if(!TimeToStruct(array_time[i],array_struct[i]))
         PrintFormat("%s: [%s] TimeToStruct() failed. Error %s",__FUNCTION__,(string)i,(string)GetLastError());
     }
//--- Print the filled MqlDateTime array
   ArrayPrint(array_struct);
   /* Sample output:
      Time data of the last 10 bars GBPUSD H1 (ArrayPrint):
          [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
      [0]   2023     7    17      7     0     0             1           197
      [1]   2023     7    17      8     0     0             1           197
      [2]   2023     7    17      9     0     0             1           197
      [3]   2023     7    17     10     0     0             1           197
      [4]   2023     7    17     11     0     0             1           197
      [5]   2023     7    17     12     0     0             1           197
      [6]   2023     7    17     13     0     0             1           197
      [7]   2023     7    17     14     0     0             1           197
      [8]   2023     7    17     15     0     0             1           197
      [9]   2023     7    17     16     0     0             1           197
   */
  }


Accordingly, the script using the above function to print out the datetime array in the journal will look like this:

void OnStart()
  {
//--- Declare a time array
   datetime array[];
//--- Copy the time of the last 10 bars to the array
   ResetLastError();
   if(CopyTime(Symbol(),Period(),0,10,array)<0)
     {
      PrintFormat("CopyTime() failed. Error %s",(string)GetLastError());
      return;
     }
//--- Display the header and the time data array of the last 10 bars using the standard ArrayPrint()
   PrintFormat("Time data of the last 10 bars %s %s (ArrayPrint):",Symbol(),StringSubstr(EnumToString(Period()),7));
   MqlDateTimePrint(array);
   /* Sample output:
      Time data of the last 10 bars GBPUSD H1 (ArrayPrint):
          [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
      [0]   2023     7    17      7     0     0             1           197
      [1]   2023     7    17      8     0     0             1           197
      [2]   2023     7    17      9     0     0             1           197
      [3]   2023     7    17     10     0     0             1           197
      [4]   2023     7    17     11     0     0             1           197
      [5]   2023     7    17     12     0     0             1           197
      [6]   2023     7    17     13     0     0             1           197
      [7]   2023     7    17     14     0     0             1           197
      [8]   2023     7    17     15     0     0             1           197
      [9]   2023     7    17     16     0     0             1           197
   */
  }


Functions for working with MqlDateTime structure data.

Everything tested above is convenient, practical and concise. But sometimes more complete information is required and, preferably, in the same concise presentation. Or, on the contrary, we might need a more detailed description by reducing the brevity and dryness of the data presentation. For example, a number of a day alone may be confusing. But if it is written “Thurs.”, then we immediately understand that we are talking about Thursday. The same goes for a month number - sometimes it is better to see “07 (July)” than to recall which month comes seventh... This might be an exaggeration of course, but still such small improvements add convenience. These small conveniences add up to a very tangible time gain when analyzing large volumes of entries in the program logs.

To add such conveniences, we will have to write your own function that returns a description of date in the MqlDateTime format.

The function will:

  1. display data in a short format (Day of week, Month, Day, Year, time);
  2. display data in a table view (Data header value);

Before we start creating functions to return descriptions of structure fields, we will create auxiliary functions that return the names of week days and months.

Auxiliary functions

To get the name of a week day, let's write a simple function that returns the text of the day of the week depending on the value located in the structure field that stores the day of the week in numeric format:

//+------------------------------------------------------------------+
//| Return the name of a week day                                    |
//+------------------------------------------------------------------+
string DayWeek(MqlDateTime &date_time)
  {
//--- Define a week day name
   string dw=EnumToString((ENUM_DAY_OF_WEEK)date_time.day_of_week);
//--- Convert all obtained symbols to lower case and replace the first letter from small to capital
   if(dw.Lower())
      dw.SetChar(0,ushort(dw.GetChar(0)-0x20));
//--- Return a resulting string
   return dw;
   /* Sample output:
      Wednesday
   */
  }

Since MQL5 has listing with week day names, then here we use the string representation of the enumeration constant. To ensure that the text appears correctly, we convert all characters in the line to lower case, with the exception of the first letter of the word.

To get the name of a month, let's write a simple function that returns the text of a month depending on the value located in the structure field that stores the month in numeric format:

//+------------------------------------------------------------------+
//| Return a month name                                              |
//+------------------------------------------------------------------+
string Month(MqlDateTime &date_time)
  {
//--- Define a month name
   switch(date_time.mon)
     {
      case  1  :  return "January";
      case  2  :  return "February";
      case  3  :  return "March";
      case  4  :  return "April";
      case  5  :  return "May";
      case  6  :  return "June";
      case  7  :  return "July";
      case  8  :  return "August";
      case  9  :  return "September";
      case 10  :  return "October";
      case 11  :  return "November";
      case 12  :  return "December";
      default  :  return "Undefined";
     }
   /* Sample output:
      July
   */
  }

MQL5 has no enumeration for selecting a month, so here we have to use the switch operator to select and return the text of the month name depending on the digital value written in the structure field.

These functions will be useful to us for displaying descriptions of the week days and months. Also, they may be useful in the future for our subsequent projects.

The functions that return descriptions of the MqlDateTime structure fields are to have the format adopted in the article "StringFormat(). Review and ready-made examples". Each row returned by the function will have a header and data. The header can be indented from the left edge and can be given a width for the tabular view of the returned record. We will pass the indentation and width values as parameters to the functions. The default values for these options are zero, which means no padding and a width equal to the length of the header text + 1.


Year in the MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return the year as a string from MqlDateTime                     |
//+------------------------------------------------------------------+
string MqlDateTimeYear(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Year:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-lu",indent,"",w,header,date_time.year);
   /* Sample output:
      Year: 2023
   */
  }


Month in the MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return the month as a string from MqlDateTime                    |
//+------------------------------------------------------------------+
string MqlDateTimeMonth(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Month:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get a month name
   string mn=Month(date_time);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%02lu (%s)",indent,"",w,header,date_time.mon,mn);
   /* Sample output:
      Month: 07 (July)
   */
  }


Day in the MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return the day as a string from the MqlDateTime structure        |
//+------------------------------------------------------------------+
string MqlDateTimeDay(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Day:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.day);
   /* Sample output:
      Day: 19
   */
  }



Hours in MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return hours as a string from the MqlDateTime structure          |
//+------------------------------------------------------------------+
string MqlDateTimeHour(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Hour:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.hour);
   /* Sample output:
      Hour: 08
   */
  }


Minutes in the MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return minutes as a string from MqlDateTime                      |
//+------------------------------------------------------------------+
string MqlDateTimeMin(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Minutes:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.min);
   /* Sample output:
      Minutes: 41
   */
  }


Seconds in the MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return seconds as a string from MqlDateTime                      |
//+------------------------------------------------------------------+
string MqlDateTimeSec(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Seconds:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.sec);
   /* Sample output:
      Seconds: 23
   */
  }


Day of the week in the MqlDateTime structure:

//+------------------------------------------------------------------+
//| Return a week day as a string from the MqlDateTime structure     |
//+------------------------------------------------------------------+
string MqlDateTimeDayWeek(MqlDateTime &date_time,const uint header_width=0,const uint indent=0,bool descr=true)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Day of week:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get a week day name
   string dw=DayWeek(date_time);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s (%-lu)",indent,"",w,header,dw,date_time.day_of_week);
   /* Sample output:
      Day of week: Wednesday (3)
   */
  }


Number of a day in a year in the MqlDateTime structure:

//+------------------------------------------------------------------+
//|Return a number of a day in a year from MqlDateTime               |
//+------------------------------------------------------------------+
string MqlDateTimeDayYear(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Day of year:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-lu",indent,"",w,header,date_time.day_of_year);
   /* Sample output:
      Day of year: 199
   */
  }


Sample use:

To display a short record of the date-time from the MqlDateTime structure, we will write a function that returns the date in the "DW, Month DD, YYYY, HH:MM:SS" format:

//+------------------------------------------------------------------+
//| Return the date as a string from the MqlDateTime structure       |
//| in the DW, Month DD, YYYY, HH:MM:SS format                       |
//+------------------------------------------------------------------+
string DateTime(MqlDateTime &date_time)
  {
//--- Get the month and the first three characters of a week day
   string mn=Month(date_time);
   string dw=StringSubstr(DayWeek(date_time),0,3);
//--- Return a string in the DW, Month DD, YYYY, HH:MM:SS format
   return StringFormat("%s, %s %02lu, %lu, %02lu:%02lu:%02lu",dw,mn,date_time.day,date_time.year,date_time.hour,date_time.min,date_time.sec);
   /* Sample output:
      Wed, July 19, 2023, 08:41:23
   */
  }

This is a one-line summary of all structure data except the day number of the year. The function is convenient, for example, for displaying a certain number of bars in the journal:

void OnStart()
  {
   datetime array[];
   MqlDateTime adt[];
   if(CopyTime(Symbol(),PERIOD_CURRENT,0,10,array)==10)
     {
      int total=(int)array.Size();
      if(ArrayResize(adt,total)==total)
        {
         for(int i=0;i<total;i++)
           {
            ResetLastError();
            if(!TimeToStruct(array[i],adt[i]))
               Print("TimeToStruct failed. Error: ",GetLastError());
            PrintFormat("%s %s [%02u] %s",Symbol(),StringSubstr(EnumToString(Period()),7),i,DateTime(adt[i]));
           }
        }
     }
   /* Sample output:
      GBPUSD H1 [00] Wed, July 19, 2023, 02:00:00
      GBPUSD H1 [01] Wed, July 19, 2023, 03:00:00
      GBPUSD H1 [02] Wed, July 19, 2023, 04:00:00
      GBPUSD H1 [03] Wed, July 19, 2023, 05:00:00
      GBPUSD H1 [04] Wed, July 19, 2023, 06:00:00
      GBPUSD H1 [05] Wed, July 19, 2023, 07:00:00
      GBPUSD H1 [06] Wed, July 19, 2023, 08:00:00
      GBPUSD H1 [07] Wed, July 19, 2023, 09:00:00
      GBPUSD H1 [08] Wed, July 19, 2023, 10:00:00
      GBPUSD H1 [09] Wed, July 19, 2023, 11:00:00
   */
  }


Let's implement the following function, to log all fields of the structure in short and tabular formats:

//+------------------------------------------------------------------+
//| Logs descriptions of all fields of the MqlDateTime structure     |
//+------------------------------------------------------------------+
void MqlDateTimePrint(MqlDateTime &date_time,const bool short_entry=true,const uint header_width=0,const uint indent=0)
  {
//--- If it is a short entry, log the date and time in the DW, Month DD, YYYY, HH:MM:SS format
   if(short_entry)
      Print(DateTime(date_time));
   /* Sample output:
      Wed, July 19, 2023, 08:41:23
   */
//--- Otherwise
   else
     {
      //--- create a string describing all the data of the structure with indents and a given width of the header field 
      string res=StringFormat("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
                              MqlDateTimeYear(date_time,header_width,indent),
                              MqlDateTimeMonth(date_time,header_width,indent),
                              MqlDateTimeDay(date_time,header_width,indent),
                              MqlDateTimeHour(date_time,header_width,indent),
                              MqlDateTimeMin(date_time,header_width,indent),
                              MqlDateTimeSec(date_time,header_width,indent),
                              MqlDateTimeDayWeek(date_time,header_width,indent),
                              MqlDateTimeDayYear(date_time,header_width,indent)
                             );
      //--- Display the obtained string in the journal
      Print(res);
     }
   /* Sample output:
      Year: 2023
      Month: 07 (July)
      Day: 19
      Hour: 09
      Minutes: 32
      Seconds: 25
      Day of week: Wednesday (3)
      Day of year: 199
   */
  }

A script with an example of handling the function. First, display a short entry in the journal followed by the tabular form with field headers indented and field widths of 2 and 14 characters, respectively:

void OnStart()
  {
   MqlDateTime dt;
   TimeCurrent(dt);
   MqlDateTimePrint(dt,true);
   MqlDateTimePrint(dt,false,14,2);
   /* Sample output:
      Wed, July 19, 2023, 09:33:56
        Year:         2023
        Month:        07 (July)
        Day:          19
        Hour:         09
        Minutes:      33
        Seconds:      56
        Day of week:  Wednesday (3)
        Day of year:  199
   */
  }

All functions for working with fields of the MqlDateTime structure and auxiliary ones presented above can be used "as is" in your programs, or can be modified in accordance with your vision and needs.


MqlTick structure

Structure for storing the last prices by symbol. The structure is designed for obtaining the most needed data on the current prices in a timely manner.

struct MqlTick
  {
   datetime     time;          // Last price update time
   double       bid;           // Current Bid price
   double       ask;           // Current Ask price
   double       last;          // Current price of the last trade (Last)
   ulong        volume;        // Volume for the current Last price
   long         time_msc;      // Last price update time in milliseconds
   uint         flags;         // Tick flags
   double       volume_real;   // Volume for the current Last price
  };

A variable of the MqlTick type allows getting Ask, Bid, Last and Volume values, as well as time in milliseconds in one call of the SymbolInfoTick() function.

The parameters of each tick are filled in regardless of whether there are changes compared to the previous tick. Thus, it is possible to find out a correct price for any moment in the past without the need to search for previous values at the tick history. For example, even if only a Bid price changes during a tick arrival, the structure still contains other parameters as well, including the previous Ask price, volume, etc.

Analyze the tick flags to find out which data has changed:

  • TICK_FLAG_BID — the tick has changed the bid price
  • TICK_FLAG_ASK — the tick has changed the ask price
  • TICK_FLAG_LAST — the tick has changed the last deal price
  • TICK_FLAG_VOLUME — the tick has changed the volume
  • TICK_FLAG_BUY — the tick is a result of a buy deal
  • TICK_FLAG_SELL — the tick is a result of a sell deal

    MqlTick, printing methods

    The ArrayPrint() function is suitable for displaying the structure to the journal in the same way as for MqlDateTime:

    void OnStart()
      {
    //--- Declare a variable with the MqlTick type
       MqlTick  tick;
    //--- If failed to get the last tick, display the error message and exit the method
       if(!SymbolInfoTick(Symbol(),tick))
         {
          Print("SymbolInfoTick failed, error: ",(string)GetLastError());
          return;
         }
    //--- Display the tick using standard ArrayPrint()
    //--- To do this, declare an array of dimension 1 with type MqlTick,
    //--- enter the value of the 'tick' variable into it and print it
       MqlTick array[1];
       array[0]=tick;
       Print("Last tick (ArrayPrint):");
       ArrayPrint(array);
       /* Sample output:
          Last tick (ArrayPrint):
                           [time]   [bid]   [ask] [last] [volume]    [time_msc] [flags] [volume_real]
          [0] 2023.07.19 17:02:49 1.28992 1.28996 0.0000        0 1689786169589       6       0.00000
       */
      }
    
    

    It is logical that to print an array, you can fill the array with a range of ticks:

    void OnStart()
      {
    //--- Declare a dynamic array of the MqlTick type
       MqlTick  array[];
    //--- If failed to get the last 10 ticks, display the error message and exit the method
       if(CopyTicks(Symbol(),array,COPY_TICKS_ALL,0,10)!=10)
         {
          Print("CopyTicks failed, error: ",(string)GetLastError());
          return;
         }
       Print("Last 10 tick (ArrayPrint):");
       ArrayPrint(array);
       /* Sample output:
          Last 10 tick (ArrayPrint):
                           [time]   [bid]   [ask] [last] [volume]    [time_msc] [flags] [volume_real]
          [0] 2023.07.19 17:24:38 1.28804 1.28808 0.0000        0 1689787478461       6       0.00000
          [1] 2023.07.19 17:24:38 1.28806 1.28810 0.0000        0 1689787478602       6       0.00000
          [2] 2023.07.19 17:24:38 1.28804 1.28808 0.0000        0 1689787478932       6       0.00000
          [3] 2023.07.19 17:24:39 1.28806 1.28810 0.0000        0 1689787479210       6       0.00000
          [4] 2023.07.19 17:24:39 1.28807 1.28811 0.0000        0 1689787479765       6       0.00000
          [5] 2023.07.19 17:24:39 1.28808 1.28812 0.0000        0 1689787479801       6       0.00000
          [6] 2023.07.19 17:24:40 1.28809 1.28813 0.0000        0 1689787480240       6       0.00000
          [7] 2023.07.19 17:24:40 1.28807 1.28811 0.0000        0 1689787480288       6       0.00000
          [8] 2023.07.19 17:24:40 1.28809 1.28813 0.0000        0 1689787480369       6       0.00000
          [9] 2023.07.19 17:24:40 1.28810 1.28814 0.0000        0 1689787480399       6       0.00000
       */
      }
    
    

    Again, we need a more meaningful display of values. For example, time in milliseconds and flags. It is probably more convenient to see them in the usual way - time in the time format, and flags in the format of enumeration constants.


    Functions for working with MqlTick structure data.

    Let's implement the functions for working with MqlTick structure data. Just like all already created functions for working with the MqlDateTime structure, functions for working with MqlTick will return a formatted string. The line format will include the left margin of the text and the width of the header field. By default, the padding and margin width values will be zero, meaning there is no padding and the margin width is equal to the length of the header text + 1.


    Auxiliary functions

    We have already created this function to return time in milliseconds as a string. Here it is useful for returning the tick time in milliseconds. Let's apply it:

    //+------------------------------------------------------------------+
    //| Accept a date in ms, return time in Date Time.Msc format         |
    //+------------------------------------------------------------------+
    string TimeMSC(const long time_msc)
      {
       return StringFormat("%s.%.3hu",string((datetime)time_msc / 1000),time_msc % 1000);
       /* Sample output:
          2023.07.13 09:31:58.177
       */
      }
    
    


    Time in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the time of the last price update as a string             |
    //+------------------------------------------------------------------+
    string MqlTickTime(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Time:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-s",indent,"",w,header,(string)tick.time);
       /* Sample output:
          Time: 2023.07.19 20:58:00
       */
      }
    
    


    Bid price in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the Bid price as a string                                 |
    //+------------------------------------------------------------------+
    string MqlTickBid(const string symbol,const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Bid:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,tick.bid);
       /* Sample output:
          Bid: 1.29237
       */
      }
    
    


    Ask price in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the Ask price as a string                                 |
    //+------------------------------------------------------------------+
    string MqlTickAsk(const string symbol,const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Ask:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,tick.ask);
       /* Sample output:
          Ask: 1.29231
       */
      }
    
    


    Last trade price in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the Last price as a string                                |
    //+------------------------------------------------------------------+
    string MqlTickLast(const string symbol,const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Last:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,tick.last);
       /* Sample output:
          Last: 0.00000
       */
      }
    
    


    Last price volume in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the volume for the Last price as a string                 |
    //+------------------------------------------------------------------+
    string MqlTickVolume(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-I64u",indent,"",w,header,tick.volume);
       /* Sample output:
          Volume: 0
       */
      }
    
    


    Time in milliseconds in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the time in milliseconds as a string                      |
    //+------------------------------------------------------------------+
    string MqlTickTimeMSC(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Time msc:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-s",indent,"",w,header,TimeMSC(tick.time_msc));
       /* Sample output:
          Time msc: 2023.07.19 21:21:09.732
       */
      }
    
    


    Tick flags in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return tick flags as a string                                    |
    //+------------------------------------------------------------------+
    string MqlTickFlags(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Flags:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Define a variable to describe tick flags
       string flags="";
    //--- Parse tick flags into components
       if((tick.flags & TICK_FLAG_BID)==TICK_FLAG_BID)
          flags+=(flags.Length()>0 ? "|" : "")+"BID";
       if((tick.flags & TICK_FLAG_ASK)==TICK_FLAG_ASK)
          flags+=(flags.Length()>0 ? "|" : "")+"ASK";
       if((tick.flags & TICK_FLAG_LAST)==TICK_FLAG_LAST)
          flags+=(flags.Length()>0 ? "|" : "")+"LAST";
       if((tick.flags & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME)
          flags+=(flags.Length()>0 ? "|" : "")+"VOLUME";
       if((tick.flags & TICK_FLAG_BUY)==TICK_FLAG_BUY)
          flags+=(flags.Length()>0 ? "|" : "")+"BUY";
       if((tick.flags & TICK_FLAG_SELL)==TICK_FLAG_SELL)
          flags+=(flags.Length()>0 ? "|" : "")+"SELL";
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-s",indent,"",w,header,flags);
       /* Sample output:
          Flags: BID|ASK
       */
      }
    
    


    Last price volume with increased accuracy in the MqlTick structure:

    //+------------------------------------------------------------------+
    //| Return the volume for the Last price as a string                 |
    //+------------------------------------------------------------------+
    string MqlTickVolumeReal(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Volume Real:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.2f",indent,"",w,header,tick.volume_real);
       /* Sample output:
          Volume Real: 0.00
       */
      }
    
    


    Examples of use

    Write the function to display tick data to the journal. Pass the name of the symbol to the function to know with what accuracy the price values are to be displayed in the journal. Since the Volume and Volume Real fields contain the volumes of the Last price, if Last is zero (not translated), then it makes no sense to display the volumes - they are also zero. To be able to specify and print the index of a tick taken from an array of ticks, we will pass this index in the input parameters of the function. By default its value is -1, and at this value the index is not printed.

    //+------------------------------------------------------------------+
    //| Logs descriptions of all fields of the MqlTick structure         |
    //| If Last==0, Last, Volume and Volume Real fields are not displayed|
    //+------------------------------------------------------------------+
    void MqlTickPrint(const string symbol,const MqlTick &tick,const bool short_entry=true,const uint header_width=0,const uint indent=0,int index=WRONG_VALUE)
      {
    //--- Declare the variable for storing the result
       string res="";
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
       string num=(index==WRONG_VALUE ? "" : StringFormat("[%ld] ",index));
    //--- If it is a short entry, log the tick data in the Symbol TimeMSC, Bid, Ask, Last, Vol/VolR, Flags format
       if(short_entry)
         {
          //--- If Last is not zero, display Last, Volume and Volume Real, otherwise they are all zero and there is no point in displaying them
          string last=(tick.last!=0 ? StringFormat(", Last: %.*f, Vol: %I64u/%.2f",dg,tick.last,tick.volume,tick.volume_real) : "");
          res=StringFormat("%sTick %s Time: %s, Bid: %.*f, Ask: %.*f%s, %s",num,symbol,TimeMSC(tick.time_msc),dg,tick.bid,dg,tick.ask,last,MqlTickFlags(tick));
          Print(res);
         }
       /* Sample output (if Last is not zero):
          Tick GBPUSD Time: 2023.07.20 13:57:31.376, Bid: 1.28947, Ask: 1.28951, Last: 1.28947, Vol: 33/33.45, Flags: BID|ASK
          Sample output (if Last is zero):
          Tick GBPUSD Time: 2023.07.20 13:59:33.274, Bid: 1.28956, Ask: 1.28960, Flags: BID|ASK
       */
    //--- Otherwise
       else
         {
          //--- create a string describing all the data of the structure with indents and a given width of the header field 
          res=StringFormat("%s\n%s\n%s%s%s\n%s\n%s%s",
                           MqlTickTime(tick,header_width,indent),
                           MqlTickBid(symbol,tick,header_width,indent),
                           MqlTickAsk(symbol,tick,header_width,indent),
                           (tick.last!=0 ? "\n"+MqlTickLast(symbol,tick,header_width,indent) : ""),
                           (tick.last!=0 ? "\n"+MqlTickVolume(tick,header_width,indent) : ""),
                           MqlTickTimeMSC(tick,header_width,indent),
                           MqlTickFlags(tick,header_width,indent),
                           (tick.last!=0 ? "\n"+MqlTickVolumeReal(tick,header_width,indent) : "")
                          );
          //--- Display the obtained string in the journal
          Print(res);
         }
       /* Sample output (if Last is not zero):
          Time:         2023.07.20 14:42:33
          Bid:          1.28958
          Ask:          1.28962
          Last:         1.28947
          Volume:       33
          Time msc:     2023.07.20 14:42:33.401
          Flags:        BID|ASK
          Volume Real:  33.45
          
          Sample output (if Last is zero):
          Time:         2023.07.20 14:42:33
          Bid:          1.28958
          Ask:          1.28962
          Time msc:     2023.07.20 14:42:33.401
          Flags:        BID|ASK
       */
      }
    
    

    A script that prints the last 10 ticks into the journal in a short form, indicating the tick indices from the array:

    void OnStart()
      {
    //--- Declare a dynamic array of the MqlTick type
       MqlTick  array[];
    //--- If failed to get the last 10 ticks, display the error message and exit the method
       if(CopyTicks(Symbol(),array,COPY_TICKS_ALL,0,10)!=10)
         {
          Print("CopyTicks failed, error: ",(string)GetLastError());
          return;
         }
       Print("Last 10 tick (MqlTickPrint):");
       for(int i=0;i<(int)array.Size();i++)
          MqlTickPrint(Symbol(),array[i],true,0,0,i);
    
       /* Sample output:
          Last 10 tick (MqlTickPrint):
          [0] Tick GBPUSD Time: 2023.07.20 15:36:29.941, Bid: 1.28686, Ask: 1.28690, Flags: BID|ASK
          [1] Tick GBPUSD Time: 2023.07.20 15:36:29.970, Bid: 1.28688, Ask: 1.28692, Flags: BID|ASK
          [2] Tick GBPUSD Time: 2023.07.20 15:36:30.061, Bid: 1.28689, Ask: 1.28693, Flags: BID|ASK
          [3] Tick GBPUSD Time: 2023.07.20 15:36:30.212, Bid: 1.28688, Ask: 1.28692, Flags: BID|ASK
          [4] Tick GBPUSD Time: 2023.07.20 15:36:30.259, Bid: 1.28689, Ask: 1.28693, Flags: BID|ASK
          [5] Tick GBPUSD Time: 2023.07.20 15:36:30.467, Bid: 1.28682, Ask: 1.28686, Flags: BID|ASK
          [6] Tick GBPUSD Time: 2023.07.20 15:36:30.522, Bid: 1.28681, Ask: 1.28685, Flags: BID|ASK
          [7] Tick GBPUSD Time: 2023.07.20 15:36:30.572, Bid: 1.28673, Ask: 1.28677, Flags: BID|ASK
          [8] Tick GBPUSD Time: 2023.07.20 15:36:30.574, Bid: 1.28672, Ask: 1.28676, Flags: BID|ASK
          [9] Tick GBPUSD Time: 2023.07.20 15:36:30.669, Bid: 1.28674, Ask: 1.28678, Flags: BID|ASK
       */
      }
    
    

    A script that prints the last 4 ticks from an array in the log with a left indentation of 2 characters and a header field width of 14 characters:

    void OnStart()
      {
    //--- Declare a dynamic array of the MqlTick type
       MqlTick  array[];
    //--- If the last 4 ticks are not received in the array, display an error message and leave
       if(CopyTicks(Symbol(),array,COPY_TICKS_ALL,0,4)!=4)
         {
          Print("CopyTicks failed, error: ",(string)GetLastError());
          return;
         }
       Print("Last 4 tick (MqlTickPrint):");
       for(int i=0;i<(int)array.Size();i++)
         {
          PrintFormat("Tick[%lu] %s:",i,Symbol());
          MqlTickPrint(Symbol(),array[i],false,14,2);
         }
    
       /* Sample output:
          Last 4 tick (MqlTickPrint):
          Tick[0] GBPUSD:
            Time:         2023.07.20 17:04:51
            Bid:          1.28776
            Ask:          1.28780
            Time msc:     2023.07.20 17:04:51.203
            Flags:        BID|ASK
          Tick[1] GBPUSD:
            Time:         2023.07.20 17:04:51
            Bid:          1.28772
            Ask:          1.28776
            Time msc:     2023.07.20 17:04:51.331
            Flags:        BID|ASK
          Tick[2] GBPUSD:
            Time:         2023.07.20 17:04:51
            Bid:          1.28771
            Ask:          1.28775
            Time msc:     2023.07.20 17:04:51.378
            Flags:        BID|ASK
          Tick[3] GBPUSD:
            Time:         2023.07.20 17:04:51
            Bid:          1.28772
            Ask:          1.28776
            Time msc:     2023.07.20 17:04:51.680
            Flags:        BID|ASK
       */
      }
    
    


    MqlRates structure

    Structure for storing information about prices, volumes and spread.

    struct MqlRates
      {
       datetime time;         // period start time
       double   open;         // open price
       double   high;         // high price for the period
       double   low;          // low price for the period
       double   close;        // close price
       long     tick_volume;  // tick volume
       int      spread;       // spread
       long     real_volume;  // exchange volume
      };
    

    MqlRates, printing methods

    MqlRates — structure for storing data on a single bar of historical data. The structure can be filled using the CopyRates() function. To obtain the data of the current bar, you can use the first form of the function call indicating the index 0 and the number of copied bars equal to 1. In any case, this function fills an array with the MqlRates type. This leads to the conclusion that it is convenient to print this array to the journal using ArrayPrint():

    void OnStart()
      {
    //---
       MqlRates array[];
       if(CopyRates(Symbol(),PERIOD_CURRENT,0,1,array)!=1)
         {
          Print("CopyRates failed, error: ",(string)GetLastError());
          return;
         }
       Print("Current bar ",Symbol()," ",StringSubstr(EnumToString(Period()),7)," (ArrayPrint):");
       ArrayPrint(array);
       /* Sample output:
          Current bar GBPUSD H1 (ArrayPrint):
                           [time]  [open]  [high]   [low] [close] [tick_volume] [spread] [real_volume]
          [0] 2023.07.21 04:00:00 1.28763 1.28765 1.28663 1.28748          2083        7             0
       */
      }
    
    

    Accordingly, to copy the last ten bars, you just need to specify the number of data to be copied equal to 10:

    void OnStart()
      {
    //---
       MqlRates array[];
       if(CopyRates(Symbol(),PERIOD_CURRENT,0,10,array)!=10)
         {
          Print("CopyRates failed, error: ",(string)GetLastError());
          return;
         }
       Print("Data of the last 10 bars: ",Symbol()," ",StringSubstr(EnumToString(Period()),7)," (ArrayPrint):");
       ArrayPrint(array);
       /* Sample output:
          Data of the last 10 bars: GBPUSD H1 (ArrayPrint):
                           [time]  [open]  [high]   [low] [close] [tick_volume] [spread] [real_volume]
          [0] 2023.07.20 20:00:00 1.28530 1.28676 1.28512 1.28641          2699        4             0
          [1] 2023.07.20 21:00:00 1.28641 1.28652 1.28557 1.28587          1726        3             0
          [2] 2023.07.20 22:00:00 1.28587 1.28681 1.28572 1.28648          2432        3             0
          [3] 2023.07.20 23:00:00 1.28648 1.28683 1.28632 1.28665           768        4             0
          [4] 2023.07.21 00:00:00 1.28663 1.28685 1.28613 1.28682           396        1             0
          [5] 2023.07.21 01:00:00 1.28684 1.28732 1.28680 1.28714           543        8             0
          [6] 2023.07.21 02:00:00 1.28714 1.28740 1.28690 1.28721           814        2             0
          [7] 2023.07.21 03:00:00 1.28721 1.28774 1.28685 1.28761          2058        5             0
          [8] 2023.07.21 04:00:00 1.28763 1.28791 1.28663 1.28774          3480        7             0
          [9] 2023.07.21 05:00:00 1.28774 1.28776 1.28769 1.28774            18        7             0
       */
      }
    
    

    Everything is the same here — there is a long list of data in the journal. If the table header is hidden behind the top edge of the journal window, then it is not clear what all the presented numbers refer to.

    Let's write our own functions to return descriptions of the structure fields and print this data in the terminal journal.


    Functions for working with MqlRates structure data.

    Our custom functions will return a text description of each field in the structure. Each description will have a header and actual data. For the string returned from the function, we can set the indent from the left edge and the width of the header field.


    Time:

    Time — period start time. In other words, the bar opening time on the symbol and chart period, from which the data written to the structure was requested.

    //+------------------------------------------------------------------+
    //| Return the bar opening time as a string                          |
    //+------------------------------------------------------------------+
    string MqlRatesTime(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Time:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-s",indent,"",w,header,(string)rates.time);
       /* Sample output:
          Time: 2023.07.21 06:00:00
       */
      }
    
    


    Open:

    The bar opening price on the symbol and chart period, from which the data written to the structure was requested.

    //+------------------------------------------------------------------+
    //| Return the bar open price as a string                            |
    //+------------------------------------------------------------------+
    string MqlRatesOpen(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Open:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.open);
       /* Sample output:
          Open: 1.28812
       */
      }
    
    


    High:

    High price - the highest price of the bar on the symbol and chart period, from which the data written to the structure was requested.

    //+------------------------------------------------------------------+
    //| Return the High bar price as a string                            |
    //+------------------------------------------------------------------+
    string MqlRatesHigh(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="High:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.high);
       /* Sample output:
          High: 1.28859
       */
      }
    
    


    Low:

    Low price - the lowest price of the bar on the symbol and chart period, from which the data written to the structure was requested.

    //+------------------------------------------------------------------+
    //| Return the bar Low price as a string                             |
    //+------------------------------------------------------------------+
    string MqlRatesLow(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Low:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.low);
       /* Sample output:
          Low: 1.28757
       */
      }
    
    


    Close:

    Close price — the close price of the bar on the symbol and chart period, from which the data written to the structure was requested.
    For the current bar, the close price is equal to the current Bid or Last price, depending on what price the chart is based on.

    //+------------------------------------------------------------------+
    //| Return the bar close price as a string                           |
    //+------------------------------------------------------------------+
    string MqlRatesClose(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Close:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.close);
       /* Sample output:
          Close: 1.28770
       */
      }
    
    


    TickVolume:

    Bar tick volume.

    //+------------------------------------------------------------------+
    //| Return the tick volume of a bar as a string                      |
    //+------------------------------------------------------------------+
    string MqlRatesTickVolume(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Tick Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-lld",indent,"",w,header,rates.tick_volume);
       /* Sample output:
          Tick Volume: 963
       */
      }
    
    


    Spread:

    Bar spread.

    //+------------------------------------------------------------------+
    //| Return the bar spread as a string                                |
    //+------------------------------------------------------------------+
    string MqlRatesSpread(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Spread:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-ld",indent,"",w,header,rates.spread);
       /* Sample output:
          Spread: 4
       */
      }
    
    


    RealVolume:

    Bar exchange volume.

    //+------------------------------------------------------------------+
    //| Return the bar exchange volume as a string                       |
    //+------------------------------------------------------------------+
    string MqlRatesRealVolume(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Real Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-lld",indent,"",w,header,rates.real_volume);
       /* Sample output:
          Real Volume: 0
       */
      }
    
    


    Examples of use

    Let's create the following script to print the data of the last 10 bars in the journal:

    void OnStart()
      {
    //--- Copy the last 10 data bars to the MqlRates array
       MqlRates array[];
       if(CopyRates(Symbol(),PERIOD_CURRENT,0,10,array)!=10)
         {
          Print("CopyRates failed, error: ",(string)GetLastError());
          return;
         }
    
    //--- Set the indexing of the array like a timeseries
       ArraySetAsSeries(array,true);
    //--- Print short entries in the journal in a loop through the array with the received bar data
       for(int i=0;i<(int)array.Size();i++)
          MqlRatesPrint(Symbol(),PERIOD_CURRENT,array[i],true,0,0,i);
       
       /* Sample output:
          GBPUSD H1[0]: 2023.07.21 14:00:00, O: 1.28451, H: 1.28541, L: 1.28451, C: 1.28501, S:  4, V:   821, RV: 0
          GBPUSD H1[1]: 2023.07.21 13:00:00, O: 1.28678, H: 1.28685, L: 1.28418, C: 1.28452, S:  1, V:  3602, RV: 0
          GBPUSD H1[2]: 2023.07.21 12:00:00, O: 1.28581, H: 1.28696, L: 1.28557, C: 1.28678, S:  1, V:  4807, RV: 0
          GBPUSD H1[3]: 2023.07.21 11:00:00, O: 1.28695, H: 1.28745, L: 1.28401, C: 1.28581, S:  1, V:  7440, RV: 0
          GBPUSD H1[4]: 2023.07.21 10:00:00, O: 1.28933, H: 1.28960, L: 1.28651, C: 1.28696, S:  1, V:  8883, RV: 0
          GBPUSD H1[5]: 2023.07.21 09:00:00, O: 1.28788, H: 1.29040, L: 1.28753, C: 1.28934, S:  1, V:  5474, RV: 0
          GBPUSD H1[6]: 2023.07.21 08:00:00, O: 1.28794, H: 1.28848, L: 1.28713, C: 1.28787, S:  1, V:  1885, RV: 0
          GBPUSD H1[7]: 2023.07.21 07:00:00, O: 1.28762, H: 1.28808, L: 1.28744, C: 1.28794, S:  4, V:   878, RV: 0
          GBPUSD H1[8]: 2023.07.21 06:00:00, O: 1.28812, H: 1.28859, L: 1.28743, C: 1.28760, S:  3, V:  1112, RV: 0
          GBPUSD H1[9]: 2023.07.21 05:00:00, O: 1.28774, H: 1.28820, L: 1.28747, C: 1.28812, S:  7, V:  1671, RV: 0
       */
      }
    
    

    After copying the required amount of data into the array, ndex it like a timeseries, so that the data is displayed in the same way as chart bars in the terminal. Data with a zero index corresponds to the current bar.

    This script will print the last 4 bars in the journal in tabular form with the header field indented two characters to the left and the title field width of 14 characters:

    void OnStart()
      {
    //--- Copy the last 4 data bars to the MqlRates array
       MqlRates array[];
       if(CopyRates(Symbol(),PERIOD_CURRENT,0,4,array)!=4)
         {
          Print("CopyRates failed, error: ",(string)GetLastError());
          return;
         }
    
    //--- Set the indexing of the array like a timeseries
       ArraySetAsSeries(array,true);
    //--- Print short entries in the journal in a loop through the array with the received bar data
       for(int i=0;i<(int)array.Size();i++)
          MqlRatesPrint(Symbol(),PERIOD_CURRENT,array[i],false,14,2,i);
       
       /* Sample output:
          GBPUSD H1[0]:
            Time:         2023.07.21 14:00:00
            Open:         1.28451
            High:         1.28541
            Low:          1.28451
            Close:        1.28491
            Tick Volume:  1098
            Spread:       4
            Real Volume:  0
          GBPUSD H1[1]:
            Time:         2023.07.21 13:00:00
            Open:         1.28678
            High:         1.28685
            Low:          1.28418
            Close:        1.28452
            Tick Volume:  3602
            Spread:       1
            Real Volume:  0
          GBPUSD H1[2]:
            Time:         2023.07.21 12:00:00
            Open:         1.28581
            High:         1.28696
            Low:          1.28557
            Close:        1.28678
            Tick Volume:  4807
            Spread:       1
            Real Volume:  0
          GBPUSD H1[3]:
            Time:         2023.07.21 11:00:00
            Open:         1.28695
            High:         1.28745
            Low:          1.28401
            Close:        1.28581
            Tick Volume:  7440
            Spread:       1
            Real Volume:  0
       */
      }
    
    


    MqlBookInfo structure

    A structure that provides the market depth data.

    struct MqlBookInfo
      {
       ENUM_BOOK_TYPE   type;            // order type from ENUM_BOOK_TYPE enumeration
       double           price;           // price
       long             volume;          // volume
       double           volume_real;     // volume with increased accuracy
      };
    

    To use the structure, it is enough to declare a variable of this type. Depth of market is not available for all financial instruments. Before receiving market depth data, we should subscribe to it using MarketBookAdd(). Upon completion, we need to unsubscribe from the market depth: MarketBookRelease(). The EA program should contain the OnBookEvent() void function to handle incoming notifications.


    MqlBookInfo, printing methods

    Each receipt of the market depth involves receiving a list of orders in it. This is a data array. Therefore, we can print one snapshot of the market depth using ArrayPrint():

    void OnStart()
      {
    //--- Declare an array to store a snapshot of the market depth
       MqlBookInfo array[];
    //--- If unable to open the market depth and subscribe to its events, inform of that and leave
       if(!MarketBookAdd(Symbol()))
         {
          Print("MarketBookAdd failed, error: ",(string)GetLastError());
          return;
         }
    //--- If unable to obtain the market depth entries, inform of that and leave
       if(!MarketBookGet(Symbol(),array))
         {
          Print("MarketBookGet failed, error: ",(string)GetLastError());
          return;
         }
    //--- Print the header in the journal and the market depth snapshot from the array below
       Print("MarketBookInfo by ",Symbol(),":");
       ArrayPrint(array);
    //--- If unable to unsubscribe from the market depth, send an error message to the journal
       if(!MarketBookRelease(Symbol()))
          Print("MarketBookRelease failed, error: ",(string)GetLastError());
       /* Sample output:
          MarketBookInfo by GBPUSD:
              [type] [price] [volume] [volume_real]
          [0]      1 1.28280      100     100.00000
          [1]      1 1.28276       50      50.00000
          [2]      1 1.28275       20      20.00000
          [3]      1 1.28273       10      10.00000
          [4]      2 1.28268       10      10.00000
          [5]      2 1.28266       20      20.00000
          [6]      2 1.28265       50      50.00000
          [7]      2 1.28260      100     100.00000
       */
      }
    
    

    As you can see, the types of requests here are expressed in numerical values, and this is not convenient for perception. Let's write functions to return descriptions of the depth of market fields in the already accepted style for the other structures discussed above.


    Functions for working with MqlBookInfo structure data.

    All functions that return a string representation of the fields of the MqlBookInfo structure will be in the same style as the functions described above for describing the fields of the corresponding structures. Let's consider these methods.


    Order type in the MqlBookInfo market depth structure:

    //+------------------------------------------------------------------+
    //| Return the order type in the market depth as a string            |
    //+------------------------------------------------------------------+
    string MqlBookInfoType(const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Get the value of the order type
       ENUM_BOOK_TYPE book_type=book.type;
    //--- "Cut out" the type from the string obtained from enum
       string type=StringSubstr(EnumToString(book_type),10);
    //--- Convert all obtained symbols to lower case and replace the first letter from small to capital
       if(type.Lower())
          type.SetChar(0,ushort(type.GetChar(0)-0x20));
    //--- Replace all underscore characters with space in the resulting line
       StringReplace(type,"_"," ");
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Type:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-s",indent,"",w,header,type);
       /* Sample output:
          Type: Sell
       */
      }
    
    


    Order price in the MqlBookInfo market depth structure:

    //+------------------------------------------------------------------+
    //| Return the order price in the market depth as a string           |
    //+------------------------------------------------------------------+
    string MqlBookInfoPrice(const string symbol,const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Price:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Get the number of decimal places
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,book.price);
       /* Sample output:
          Price: 1.28498
       */
      }
    
    


    Order volume in the MqlBookInfo market depth structure:

    //+------------------------------------------------------------------+
    //| Return the order volume in the market depth as a string          |
    //+------------------------------------------------------------------+
    string MqlBookInfoVolume(const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-lld",indent,"",w,header,book.volume);
       /* Sample output:
          Volume: 100
       */
      }
    
    


    Volume with increased accuracy:

    //+------------------------------------------------------------------+
    //| Return the order volume with increased accuracy as a string      |
    //+------------------------------------------------------------------+
    string MqlBookInfoVolumeReal(const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Define the header text and the width of the header field
    //--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
       string header="Volume Real:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Return the property value with a header having the required width and indentation
       return StringFormat("%*s%-*s%-.2f",indent,"",w,header,book.volume_real);
       /* Sample output:
          Volume Real: 100.00
       */
      }
    
    


    Examples of use:

    Now let's write a function that prints all the data in the MqlBookInfo structure to the journal. It will be possible to print it in two modes: in one line and in tabular form:

    //+------------------------------------------------------------------+
    //| Logs a description of all fields of the MqlRates structure       |
    //+------------------------------------------------------------------+
    void MqlBookInfoPrint(const string symbol,const MqlBookInfo &book,
                          const bool short_entry=true,const uint header_width=0,const uint indent=0,int index=WRONG_VALUE)
      {
    //--- Declare the variable for storing the result
       string res="";
    //--- Get the number of decimal places and the string index value
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
       string num=(index==WRONG_VALUE ? "" : StringFormat("[%02ld]",index));
       
    //--- "Cut out" the type from the order type name string obtained from enum
       string type=StringSubstr(EnumToString(book.type),10);
    //--- Convert all obtained symbols to lower case and replace the first letter from small to capital
       if(type.Lower())
          type.SetChar(0,ushort(type.GetChar(0)-0x20));
    //--- Replace all underscore characters with space in the resulting line
       StringReplace(type,"_"," ");
       
    //--- If it is a short entry, log the market depth data in the [index] Type Price V VR format
       if(short_entry)
         {
          res=StringFormat("%-8s%-11s%- *.*f Volume%- 5lld Real%- 8.2f",
                           num,type,dg+4,dg,book.price,book.volume,book.volume_real);
          Print(res);
         }
       /* Sample output:
          [00]    Sell        1.28598  Volume 100  Real 100.00 
       */
    //--- Otherwise
       else
         {
          //--- create a string describing all the data of the structure with indents and a given width of the header field 
          res=StringFormat("Market Book by %s %s:\n%s\n%s\n%s\n%s",symbol,num,
                           MqlBookInfoType(book,header_width,indent),
                           MqlBookInfoPrice(symbol,book,header_width,indent),
                           MqlBookInfoVolume(book,header_width,indent),
                           MqlBookInfoVolumeReal(book,header_width,indent)
                          );
          //--- Display the obtained string in the journal
          Print(res);
         }
       /* Sample output
          BoolInfo by GBPUSD [00]:
            Type:         Sell
            Price:        1.28588
            Volume:       100
            Volume Real:  100.00
       */
      }
    
    

    The main mode here should be the output to the journal in one line, since the market depth is not a single order, and we receive a list of these orders in the array. Accordingly, we can print this array to the journal using the following function:

    //+----------------------------------------------------------------------+
    //| Display the market depth entries in the journal in the short format  |
    //+----------------------------------------------------------------------+
    void MqlBookInfoPrintShort(const string symbol,const MqlBookInfo &book_array[])
      {
       PrintFormat("Market Book by %s:",symbol);
       for(int i=0;i<(int)book_array.Size();i++)
          MqlBookInfoPrint(symbol,book_array[i],true,0,0,i);
      }
    
    

    The function receives an array of orders for the market depth and, in a loop through the array, prints all the market depth data into the journal using the short output.

    A script, demonstrating how to use this function, and the result:

    void OnStart()
      {
    //--- Declare an array to store a snapshot of the market depth
       MqlBookInfo array[];
    //--- If unable to open the market depth and subscribe to its events, inform of that and leave
       if(!MarketBookAdd(Symbol()))
         {
          Print("MarketBookAdd failed, error: ",(string)GetLastError());
          return;
         }
    //--- If unable to obtain the market depth entries, inform of that and leave
       if(!MarketBookGet(Symbol(),array))
         {
          Print("MarketBookGet failed, error: ",(string)GetLastError());
          return;
         }
    //--- Print in the journal a snapshot of the market depth from the array in the form of strings
       MqlBookInfoPrintShort(Symbol(),array);
    
    //--- If unable to unsubscribe from the market depth, send an error message to the journal
       if(!MarketBookRelease(Symbol()))
          Print("MarketBookRelease failed, error: ",(string)GetLastError());
       
       /* Sample output:
          Market Book by GBPUSD:
          [00]    Sell        1.28674  Volume 100  Real 100.00 
          [01]    Sell        1.28668  Volume 50   Real 50.00  
          [02]    Sell        1.28666  Volume 20   Real 20.00  
          [03]    Sell        1.28664  Volume 10   Real 10.00  
          [04]    Buy         1.28657  Volume 10   Real 10.00  
          [05]    Buy         1.28654  Volume 20   Real 20.00  
          [06]    Buy         1.28653  Volume 50   Real 50.00  
          [07]    Buy         1.28646  Volume 100  Real 100.00 
       */
      }
    
    

    But sometimes we may need to display the same data, but in tabular form. To do this, we can use the following function:

    //+------------------------------------------------------------------------+
    //| Display the market depth entries in the journal in the tabular format  |
    //+------------------------------------------------------------------------+
    void MqlBookInfoPrintTable(const string symbol,const MqlBookInfo &book_array[],const uint header_width=0,const uint indent=0)
      {
       for(int i=0;i<(int)book_array.Size();i++)
          MqlBookInfoPrint(symbol,book_array[i],false,header_width,indent,i);
      }
    
    

    A script, demonstrating how to use this function, and the result:

    void OnStart()
      {
    //--- Declare an array to store a snapshot of the market depth
       MqlBookInfo array[];
    //--- If unable to open the market depth and subscribe to its events, inform of that and leave
       if(!MarketBookAdd(Symbol()))
         {
          Print("MarketBookAdd failed, error: ",(string)GetLastError());
          return;
         }
    //--- If unable to obtain the market depth entries, inform of that and leave
       if(!MarketBookGet(Symbol(),array))
         {
          Print("MarketBookGet failed, error: ",(string)GetLastError());
          return;
         }
    //--- Print in the journal a snapshot of the market depth from the array in the form of strings
       MqlBookInfoPrintTable(Symbol(),array,14,2);
    
    //--- If unable to unsubscribe from the market depth, send an error message to the journal
       if(!MarketBookRelease(Symbol()))
          Print("MarketBookRelease failed, error: ",(string)GetLastError());
       
       /* Sample output:
          Market Book by GBPUSD [00]:
            Type:         Sell
            Price:        1.28627
            Volume:       100
            Volume Real:  100.00
          Market Book by GBPUSD [01]:
            Type:         Sell
            Price:        1.28620
            Volume:       50
            Volume Real:  50.00
          Market Book by GBPUSD [02]:
            Type:         Sell
            Price:        1.28618
            Volume:       20
            Volume Real:  20.00
          Market Book by GBPUSD [03]:
            Type:         Sell
            Price:        1.28615
            Volume:       10
            Volume Real:  10.00
          Market Book by GBPUSD [04]:
            Type:         Buy
            Price:        1.28610
            Volume:       10
            Volume Real:  10.00
          Market Book by GBPUSD [05]:
            Type:         Buy
            Price:        1.28606
            Volume:       20
            Volume Real:  20.00
          Market Book by GBPUSD [06]:
            Type:         Buy
            Price:        1.28605
            Volume:       50
            Volume Real:  50.00
          Market Book by GBPUSD [07]:
            Type:         Buy
            Price:        1.28599
            Volume:       100
            Volume Real:  100.00
       */
      }
    
    


    Conclusion

    We have considered printing the fields of the four structures: MqlDateTime, MqlTick, MqlRates and MqlBookInfo. The created functions return a description of the fields of each structure in the "Header-Data" format as a string that can be printed or used inside another function. All functions are self-reliant, ready-to-use, and can be used "as is" in custom programs. The next step is description and display of trade structures.


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

    Neural networks made easy (Part 40): Using Go-Explore on large amounts of data Neural networks made easy (Part 40): Using Go-Explore on large amounts of data
    This article discusses the use of the Go-Explore algorithm over a long training period, since the random action selection strategy may not lead to a profitable pass as training time increases.
    Discrete Hartley transform Discrete Hartley transform
    In this article, we will consider one of the methods of spectral analysis and signal processing - the discrete Hartley transform. It allows filtering signals, analyzing their spectrum and much more. The capabilities of DHT are no less than those of the discrete Fourier transform. However, unlike DFT, DHT uses only real numbers, which makes it more convenient for implementation in practice, and the results of its application are more visual.
    Neural networks made easy (Part 41): Hierarchical models Neural networks made easy (Part 41): Hierarchical models
    The article describes hierarchical training models that offer an effective approach to solving complex machine learning problems. Hierarchical models consist of several levels, each of which is responsible for different aspects of the task.
    Integrate Your Own LLM into EA (Part 1): Hardware and Environment Deployment Integrate Your Own LLM into EA (Part 1): Hardware and Environment Deployment
    With the rapid development of artificial intelligence today, language models (LLMs) are an important part of artificial intelligence, so we should think about how to integrate powerful LLMs into our algorithmic trading. For most people, it is difficult to fine-tune these powerful models according to their needs, deploy them locally, and then apply them to algorithmic trading. This series of articles will take a step-by-step approach to achieve this goal.