MQL5中的结构及其数据打印方法

Artyom Trishkin | 15 二月, 2024

目录


概述

结构是一种方便的工具,用于存储、记录和检索属于单个变量的任何定义的逻辑相关数据。

MQL5具有12个预定义结构,用于存储和传输服务信息:

MqlParamMqlTradeRequest结构传输用于创建指标和向服务器发送交易请求的技术信息。我们根据完成的结构中发送数据的要求结果填写结构的要求字段。换句话说,这些结构并不特别需要打印这些结构的字段由程序员填充的数据
但剩下的结构返回查询结果,每个字段由终端子系统或交易服务器填写。从这些结构中获取数据,以编程方式分析结构的填充字段,或将其打印到日志中进行后续手动分析,对于以编程方式做出决策以及理解和查找逻辑错误的位置来说,都是非常方便和必要的。

为了打印结构的所有字段,有一个标准的ArrayPrint()函数,该函数以方便的表格格式显示数组中包含的数据以及处理的结构的类型。但有时我们需要以另一种格式打印结构中的数据,这可能比表格表示更方便。例如,我们可能需要在一行中显示结构的所有字段,包括标题和相应的数据。这对于分析大量数据可能更方便。同时,有时我们需要看到一个更详细的视图,包括结构字段的描述和相应数据的不同表示。

在当前的文章中,我们将研究用于查看结构数据的标准工具,并为日志中不同的结构数据表示创建自定义函数。


MqlDateTime 结构

数据结构包含八个int类型的字段。

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

标准函数TimeCurrent()TimeGMT()TimeLocal()TimeTradeServer()TimeToStruct()用于填充结构字段。

前四个函数,除了返回当前日期、GMT日期、本地计算机时间和交易服务器日期外,每个函数都有一个重载函数。在函数的形式参数中,可以通过引用将日期结构传递给它。执行函数后,传递给函数的结构字段将填充函数返回的日期数据。

TimeToStruct()函数专门用于将datetime值类型(自1970年1月1日起的秒数)中的结构填充到MqlDateTime结构类型变量中。

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

如果成功,返回true,否则-false。操作后,日期结构将填充在datetime类型的变量中第一个参数中传递的时间数据。

我们的日期结构已经完整了,但我们如何打印它?有一个标准的TimeToString()函数,它将包含自1970年1月1日以来以秒为单位的时间的值转换为“yyyy.mm.dd hh:min:sec”格式的字符串。

但是该函数不适用于MqlDateTime结构,而是适用于datetime日期。那么,我们应该将结构转换回时间值吗?当然不要。每个函数都有其自身的用途。我们可以从日期结构中单独提取日期和时间的任何组成部分——一年、一个月、一小时、一分钟、一周中的一天等等。。。但是我们怎样才能显示所有的结构数据呢?
ArrayPrint()函数适用于此任务,该函数记录简单类型或简单结构的数组。它以表的形式显示数据,其中列是结构的字段,行表示数组单元。换句话说,为了只显示一个日期的结构,我们需要一个1的数组。对于一个交易周,如果数据来自D1图表,数组大小通常为5个交易日。


MqlDateTime, 打印方法

这样的脚本使用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
   */
  }

相应地,采用日期结构并将其打印在日志中的函数将如下所示:

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

该函数允许在日志中打印time_struct变量中传递给它的一个日期。

使用上述函数记录单个日期结构的脚本:

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


如果我们需要打印一个日期数组(毕竟这是ArrayPrint()的主要目标),那么我们需要将数据数组传递给datetime函数,填写MqlDateTime数组并打印它。

接受日期时间数组并打印MqlDateTime数组的函数:

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


因此,使用上述函数打印日志中datetime数组的脚本将如下所示:

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


用于处理MqlDateTime结构数据的函数。

上面测试的一切都是方便、实用和简洁的。但有时需要更完整的信息,最好是在同样简洁的陈述中。或者,相反,我们可能需要更详细的描述,减少数据呈现的简洁和枯燥。例如,仅一天的数字就可能令人困惑。但如果写的是“星期四”,那么我们马上就明白我们说的是星期四。一个月的数字也是如此——有时看到“07(七月)”比回忆哪个月是第七个要好。。。当然,这可能有些夸张,但这些小的改进仍然增加了便利性。当分析程序日志中的大量条目时,这些小便利加起来会带来非常明显的时间增益。

为了增加这些便利性,我们必须编写您自己的函数,以MqlDateTime格式返回日期描述。

该函数将:

  1. 以短格式显示数据(星期几、月、日、年、时间);
  2. 在表格视图中显示数据(数据头值);

在我们开始创建返回结构字段描述的函数之前,我们将创建返回周、日和月名称的辅助函数。

辅助函数

要获得一星期中某一天的名称,让我们编写一个简单的函数,根据以数字格式存储星期天的结构字段中的值返回星期天的文本:

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

由于MQL5具有列表和星期日名称,因此在这里我们使用枚举常量的字符串表示。为了确保文本正确显示,我们将行中的所有字符转换为小写,单词的第一个字母除外。

要获得月份的名称,让我们编写一个简单的函数,根据以数字格式存储月份的结构字段中的值返回月份的文本:

//+------------------------------------------------------------------+
//| 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没有用于选择月份的枚举,因此在这里我们必须使用switch运算符来选择并返回月份名称的文本,具体取决于写入结构字段中的数字值。

这些函数对我们显示周、日和月的描述非常有用。此外,它们可能在未来对我们的后续项目有用。

返回MqlDateTime结构字段描述的函数将采用文章“StringFormat()回顾和现成的例子”中采用的格式。函数返回的每一行都将有一个标题和数据。标题可以从左边缘缩进,并可以为返回记录的表格视图指定宽度。我们将把缩进值和宽度值作为参数传递给函数。这些选项的默认值为零,这意味着没有填充,宽度等于标题文本的长度+1。


MqlDateTime结构中的年份:

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


MqlDateTime结构中的月份:

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


MqlDateTime结构中的日期:

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



MqlDateTime结构中的小时数:

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


MqlDateTime结构中的分钟数:

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


MqlDateTime结构中的秒数:

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


MqlDateTime结构中的星期几:

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


MqlDateTime结构中一年中的天数:

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


用法示例:

要从MqlDateTime结构中显示日期时间的短记录,我们将编写一个函数,以“DW,Month DD,YYYY,HH:MM:SS”格式返回日期:

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

这是除一年中的日期之外的所有结构数据的一行摘要。该函数很方便,例如,用于在日志中显示一定数量的柱形:

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


让我们实现以下函数,以短格式和表格格式记录结构的所有字段:

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

带有处理函数示例的脚本。首先,在日志中显示一个简短的条目,然后显示表格形式,字段标题缩进,字段宽度分别为2个和14个字符:

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

所有用于处理MqlDateTime结构字段的函数和上面提供的辅助函数都可以在程序中“按原样”使用,也可以根据您的愿景和需求进行修改。


MqlTick结构

用于按交易品种存储最后价格的结构。该结构旨在及时获得最需要的当前价格数据。

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

MqlTick类型的变量允许在SymbolInfoTick()函数的一次调用中获得Ask、Bid、Last和Volume值,以及以毫秒为单位的时间。

无论与上一个分时相比是否有变化,都会填写每个分时的参数。因此,可以找出过去任何时刻的正确价格,而无需在分时历史中搜索以前的值。例如,即使在分时到达期间只有卖家出价发生变化,该结构仍然包含其他参数,包括以前的要价、成交量等。

分析分时标志以找出哪些数据已更改:


MqlTick打印方法

ArrayPrint()函数适用于以与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
   */
  }

逻辑上,要打印数组,可以在数组中填充一系列分时信息:

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

同样,我们需要更有意义的方式来展示数值。例如,对于以毫秒为单位的时间和标志,以通常的方式查看它们可能更方便——时间格式的时间和枚举常量格式的标志。


用于处理MqlTick结构数据的函数。

让我们实现使用MqlTick结构数据的函数。就像所有已经创建的使用MqlDateTime结构的函数一样,使用MqlTick的函数将返回一个格式化的字符串。行格式将包括文本的左边距和标题字段的宽度。默认情况下,填充和页边宽度值将为零,这意味着没有填充,并且页边宽度等于页眉文本的长度+1。


辅助函数

我们已经创建了这个函数,以字符串形式返回以毫秒为单位的时间。在这里,它用于返回以毫秒为单位的分时时间。让我们应用它:

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


MqlTick结构中的时间:

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


MqlTick结构中的卖家报价:

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


MqlTick结构中的买家报价:

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


MqlTick结构中的最后交易价格:

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


MqlTick结构中的最后价格交易量:

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


MqlTick结构中的时间(以毫秒为单位):

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


MqlTick结构中的分时标志:

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


MqlTick结构中精度提高的最后价格交易量:

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


使用示例

编写将分时数据显示到日志中的函数。将交易品种的名称传递给函数,以了解日志中显示的价格值的准确性。因为 Volume 和 Volume Real 字段包含了最后价格交易量,如果 Last 字段为零(没有交易),则没有必要显示交易量 - 它们也是零。为了能够指定和打印从记号数组中提取的记号的索引,我们将在函数的输入参数中传递该索引。默认情况下,其值为-1,在该值下不会打印索引。

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

一个脚本,以短格式将最后10个分时打印到日志中,指示数组中的分时索引:

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

一个脚本,用于打印日志中数组的最后4个分时,左缩进为2个字符,标题字段宽度为14个字符:

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 结构

用于存储有关价格、交易量和点差的信息的结构

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 的打印方法

MqlRates——用于在历史数据的单柱上存储数据的结构。可以使用CopyRates()函数填充该结构。要获取当前柱的数据,可以使用函数调用的第一种形式,指示索引0和复制的条形图数量等于1。在任何情况下,此函数都会使用MqlRates类型填充数组。这导致了这样一个结论,即使用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
   */
  }

因此,要复制最后十条,只需指定要复制的数据数等于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
   */
  }

这里的一切都是一样的——日志上有一长串数据。如果表格标题隐藏在日记窗口的上边缘后面,则不清楚所有显示的数字指的是什么。

让我们编写自己的函数来返回结构字段的描述,并将这些数据打印在终端日志中。


用于处理MqlRates结构数据的函数。

我们的自定义函数将返回结构中每个字段的文本描述。每个描述都有一个标题和实际数据。对于函数返回的字符串,我们可以设置从左边缘开始的缩进和头字段的宽度。


Time:

时间 — 时段开始时间. 换句话说,交易品种和图表周期上的柱形开始时间,从中请求写入结构的数据。

//+------------------------------------------------------------------+
//| 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:

交易品种和图表周期上的柱形开盘价,从中请求写入结构的数据。

//+------------------------------------------------------------------+
//| 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:

高价-交易品种和图表周期上柱形的最高价格,从中请求写入结构的数据。

//+------------------------------------------------------------------+
//| 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:

低价-交易品种和图表周期上柱形的最低价格,从中请求写入结构的数据。

//+------------------------------------------------------------------+
//| 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:

收盘价—交易品种和图表周期上柱形的收盘价,从中请求写入结构的数据。
对于当前柱形,收盘价等于当前出价或最后价格,具体取决于图表所基于的价格。

//+------------------------------------------------------------------+
//| 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:

柱形的分时交易量。

//+------------------------------------------------------------------+
//| 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:

柱形的点差。

//+------------------------------------------------------------------+
//| 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:

柱形的交易量。

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


使用示例

让我们创建以下脚本来打印日志中最后10条的数据:

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

将所需数量的数据复制到数组中后,将其作为时间序列进行索引,以便数据以与终端中的图表柱形相同的方式显示。索引为零的数据对应于当前柱形。

此脚本将以表格形式打印日志中的最后4个柱形,标题字段向左缩进两个字符,标题字段宽度为14个字符:

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结构

提供市场深度数据的结构。

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

要使用该结构,只需声明一个这种类型的变量就足够了。市场深度并非适用于所有金融工具。在收到市场深度数据之前,我们应该使用MarketBookAdd()订阅它。完成后,我们需要取消订阅市场深度:MarketBookRelease()。EA程序应该包含OnBookEvent()void型函数来处理传入通知。


MqlBookInfo打印方法

每次接收市场深度都涉及到接收其中的订单列表。这是一个数据数组。因此,我们可以使用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
   */
  }

正如您所看到的,这里的请求类型是用数值表示的,这不便于感知。让我们为上面讨论的其他结构编写函数,以已经接受的样式返回市场领域深度的描述。


用于处理MqlBookInfo结构数据的函数。

返回MqlBookInfo结构的字段的字符串表示的所有函数都将与上面描述的用于描述相应结构的域的函数具有相同的样式。让我们探讨一下这些方法。


MqlBookInfo市场深度结构中的订单类型:

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


MqlBookInfo市场深度结构中的订单价格:

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


MqlBookInfo市场深度结构中的订单交易量:

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


具有更高精度的交易量:

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


使用示例:

现在,让我们编写一个函数,将MqlBookInfo结构中的所有数据打印到日志中。可以以两种模式打印:一行打印和表格打印:

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

这里的主要模式应该是在一行中输出到日记账,因为市场深度不是单个订单,并且我们在数组中收到这些订单的列表。因此,我们可以使用以下函数将此数组打印到日志中:

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

该函数接收市场深度的订单数组,并在该数组的循环中,使用短输出将所有市场深度数据打印到日志中。

一个脚本,演示如何使用此函数,以及结果:

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

但有时我们可能需要以表格形式显示相同的数据。为此,我们可以使用以下函数:

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

一个脚本,演示如何使用此函数,以及结果:

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


结论

我们已经探讨了打印四种结构的字段:MqlDateTimeMqlTickMqlRatesMqlBookInfo。创建的函数以“标题数据”格式返回每个结构的字段描述,作为一个字符串,可以打印或在另一个函数中使用。所有函数都是独立的,随时可以使用,并且可以在自定义程序中“按原样”使用。下一步是描述和展示交易结构。