English 中文 Español Deutsch 日本語 Português
preview
Структуры в MQL5 и способы вывода их данных на печать

Структуры в MQL5 и способы вывода их данных на печать

MetaTrader 5Примеры | 24 июля 2023, 14:16
1 147 5
Artyom Trishkin
Artyom Trishkin

Содержание


Введение

Структура является удобным инструментом для хранения, записи и получения из одной переменной логически связанных между собой данных, принадлежащих какому-либо определению.

В языке MQL5 есть 12 предопределённых структур, предназначенных для хранения и передачи служебной информации:

Структуры MqlParam и MqlTradeRequest служат для передачи технической информации для создания индикаторов и отсылки торговых запросов на сервер. Мы сами заполняем нужные поля структур в соответствии с необходимым результатом отправки данных в заполненной структуре. Т.е. эти структуры особо не нуждаются в том, чтобы вывести на печать данные, которыми были заполнены поля этих структур программистом самостоятельно.
А вот остальные структуры возвращают результаты запросов, и каждое поле заполняется либо подсистемой терминала, либо торговым сервером. Получить данные из этих структур, проанализировать программно заполненные поля структур, либо распечатать в журнал для последующего ручного анализа — весьма удобно и необходимо как для принятия решения программно, так и для понимания и поиска места логической ошибки.

Для того, чтобы распечатать все поля структуры есть стандартная функция ArrayPrint(), которая выводит в удобном табличном формате данные, содержащиеся в массиве с типом обрабатываемой структуры. Но иногда нужно вывести на печать данные из структуры и в другом формате, который может быть удобнее табличного представления. Например, в одну строку вывести все поля структуры — с заголовками и соответствующими им данными. Для анализа больших массивов данных это может быть удобнее. В то же время иногда требуется видеть более детальное представление — с описанием полей структуры и иным представлением соответствующих данных.

Сегодня рассмотрим стандартные средства просмотра данных структур и создадим пользовательские функции для различного представления данных структур в журнале.


Структура MqlDateTime

Структура даты содержит в себе восемь полей типа int.

struct MqlDateTime
  {
   int year;           // год
   int mon;            // месяц
   int day;            // день
   int hour;           // час
   int min;            // минуты
   int sec;            // секунды
   int day_of_week;    // день недели (0-воскресенье, 1-понедельник, ... ,6-суббота)
   int day_of_year;    // порядковый номер в году (1 января имеет номер 0)
  };

Для заполнения полей структуры служат стандартные функции TimeCurrent(), TimeGMT(), TimeLocal(), TimeTradeServer() и TimeToStruct().

Первые четыре функции кроме того, что возвращают текущую дату, дату GMT, локальное время компьютера и дату торгового сервера, имеют по одной перегрузке — в формальных параметрах функции в неё можно передать по ссылке структуру даты и после отработки функции поля структуры, переданной в функцию, будут заполнены данными даты, которую возвращает функция.

Функция TimeToStruct() специально предназначена для заполнения структуры из значения типа datetime (количество секунд с 01.01.1970) в переменную типа структуры MqlDateTime.

bool  TimeToStruct(
   datetime      dt,            // дата и время
   MqlDateTime&  dt_struct      // структура для принятия значений
   );

Возвращает true – в случае успеха, иначе false. После её работы структура даты будет заполнена данными о времени, переданными в первом параметре в переменной с типом datetime.

Хорошо. Структура даты у нас заполнена, но как её вывести на печать? Есть стандартная функция TimeToString(), преобразующая значение, содержащее время в секундах, прошедшее с 01.01.1970, в строку формата "yyyy.mm.dd hh:min:sec".

Но функция работает не со структурой MqlDateTime, а с датой datetime. Что же, нам обратно преобразовывать структуру в значение времени? Нет конечно. Каждая из функций предназначена для своих целей. Из структуры даты мы можем по раздельности брать любую составляющую даты и времени — отдельно год, отдельно месяц, час, минуту, день недели, и т.д... Но как же вывести все данные структуры?
Для этого хорошо подойдёт функция ArrayPrint(), выводящая в журнал массив простого типа или простой структуры. Выводит данные в виде таблицы, где столбцами являются поля структуры, а строками — каждая ячейка массива. Т.е., для вывода структуры всего одной даты нам потребуется массив, размерностью 1. Для торговой недели, при условии получения данных с графика D1, размерность массива будет составлять 5 (обычно) торговых дней.


MqlDateTime, способы вывода на печать

Такой скрипт распечатает в журнале при помощи ArrayPrint() структуру даты, полученную из текущего времени:

void OnStart()
  {
//--- Объявляем переменную-структуру даты
   MqlDateTime  time;
//--- Получаем текущее время и заодно заполняем структуру даты
   TimeCurrent(time);
//--- Объявляем массив с типом MqlDateTime и записываем в него данные заполненной структуры
   MqlDateTime array[1];
   array[0]=time;
//--- Выводим заголовок и время при помощи стандартной ArrayPrint()
   Print("Time current (ArrayPrint):");
   ArrayPrint(array);
   /* Пример вывода:
      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()                               |
//+------------------------------------------------------------------+
void MqlDateTimePrint(const MqlDateTime& time_struct)
  {
//--- Объявляем массив с типом MqlDateTime и записываем в него данные полученной структуры
   MqlDateTime array[1];
   array[0]=time_struct;
//--- Выводим массив на печать
   ArrayPrint(array);
   /* Пример вывода:
      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()
  {
//--- Объявляем переменную-структуру даты
   MqlDateTime  time;
//--- Получаем текущее время и заодно заполняем структуру даты
   TimeCurrent(time);
//--- Выводим заголовок и время при помощи стандартной ArrayPrint()
   Print("Time current (ArrayPrint):");
   MqlDateTimePrint(time);
   /* Пример вывода:
      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 и распечатать его.

Функция, принимающая массив datetime и распечатывающая массив MqlDateTime:

//+------------------------------------------------------------------+
//| Принимает массив datetime, конвертирует его в MqlDateTime и      |
//| выводит сконвертированные данные в журнал.                       |
//| Использует для вывода ArrayPrint()                               |
//+------------------------------------------------------------------+
void MqlDateTimePrint(const datetime& array_time[])
  {
//--- Объявляем динамический массив с типом MqlDateTime
   MqlDateTime array_struct[];
//--- Получаем размер переданного в функцию массива
   int total=(int)array_time.Size();
//--- Если передан пустой массив - сообщаем об этом и уходим из функции
   if(total==0)
     {
      PrintFormat("%s: Error. Empty array.",__FUNCTION__);
      return;
     }
//--- Изменяем размер массива MqlDateTime под размер массива datetime
   ResetLastError();
   if(ArrayResize(array_struct,total)!=total)
     {
      PrintFormat("%s: ArrayResize() failed. Error %s",__FUNCTION__,(string)GetLastError());
      return;
     }
//--- Конвертируем даты из массива datetime в структуру дат в массиве MqlDateTime
   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());
     }
//--- Выводим заполненный массив MqlDateTime на печать
   ArrayPrint(array_struct);
   /* Пример вывода:
      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()
  {
//--- Объявляем массив времени
   datetime array[];
//--- Копируем в массив время 10 последних баров
   ResetLastError();
   if(CopyTime(Symbol(),Period(),0,10,array)<0)
     {
      PrintFormat("CopyTime() failed. Error %s",(string)GetLastError());
      return;
     }
//--- Выводим заголовок и массив данных времени 10 последних баров при помощи стандартной ArrayPrint()
   PrintFormat("Time data of the last 10 bars %s %s (ArrayPrint):",Symbol(),StringSubstr(EnumToString(Period()),7));
   MqlDateTimePrint(array);
   /* Пример вывода:
      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. выводить данные в табличном представлении (Заголовок данных       значение);

Прежде, чем начнём создавать функции для возврата описаний полей структуры, создадим вспомогательные функции, возвращающие наименования дней недели и месяцев.

Вспомогательные функции.

Для получения наименования дня недели напишем простую функцию, возвращающую текст дня недели в зависимости от значения, находящегося в поле структуры, хранящем день недели в числовом формате:

//+------------------------------------------------------------------+
//| Возвращает наименование дня недели                               |
//+------------------------------------------------------------------+
string DayWeek(MqlDateTime &date_time)
  {
//--- Определяем название дня недели
   string dw=EnumToString((ENUM_DAY_OF_WEEK)date_time.day_of_week);
//--- Преобразуем все полученные символы в нижний регистр и заменяем первую букву с маленькой на заглавную
   if(dw.Lower())
      dw.SetChar(0,ushort(dw.GetChar(0)-0x20));
//--- Возвращаем полученную строку
   return dw;
   /* Пример вывода:
      Wednesday
   */
  }

Так как в MQL5 есть перечисление с наименованиями дней недели, то здесь мы используем строковое представление константы данного перечисления. А чтобы вид текста был корректным, преобразуем все символы строки в нижний регистр за исключением первой буквы слова.

Для получения наименования месяца напишем простую функцию, возвращающую текст месяца в зависимости от значения, находящегося в поле структуры, хранящем месяц в числовом формате:

//+------------------------------------------------------------------+
//| Возвращает наименование месяца                                   |
//+------------------------------------------------------------------+
string Month(MqlDateTime &date_time)
  {
//--- Определяем название месяца
   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";
     }
   /* Пример вывода:
      July
   */
  }

В MQL5 нет перечисления для выбора месяца, поэтому здесь нам приходится в операторе switch выбирать и возвращать текст наименования месяца в зависимости от цифрового значения, записанного в поле структуры.

Эти функции пригодятся нам для вывода описания дней недели и месяца, и могут пригодиться в дальнейшем для нужд в других своих проектах.

Функции, возвращающие описания полей структуры MqlDateTime будут иметь тот же формат, что был принят в статье "Функции для работы с данными структуры MqlDateTimeStringFormat(). Обзор, готовые примеры использования". Каждая строка, возвращаемая функцией, будет иметь заголовок и данные. Заголовок может иметь отступ от левого края и ему можно задать ширину для табличного вида возвращаемой записи. Значения отступа и ширины будем передавать параметрами в функции. По умолчанию значения этих параметров равны нулю, что означает отсутствие отступа и ширину, равную длине текста заголовка + 1.


Год в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки год из структуры MqlDateTime            |
//+------------------------------------------------------------------+
string MqlDateTimeYear(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Year:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%-lu",indent,"",w,header,date_time.year);
   /* Пример вывода:
      Year: 2023
   */
  }


Месяц в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки месяц из структуры MqlDateTime          |
//+------------------------------------------------------------------+
string MqlDateTimeMonth(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Month:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Получаем название месяца
   string mn=Month(date_time);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%02lu (%s)",indent,"",w,header,date_time.mon,mn);
   /* Пример вывода:
      Month: 07 (July)
   */
  }


День в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки день из структуры MqlDateTime           |
//+------------------------------------------------------------------+
string MqlDateTimeDay(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Day:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.day);
   /* Пример вывода:
      Day: 19
   */
  }


Часы в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки часы из структуры MqlDateTime           |
//+------------------------------------------------------------------+
string MqlDateTimeHour(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Hour:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.hour);
   /* Пример вывода:
      Hour: 08
   */
  }


Минуты в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки минуты из структуры MqlDateTime         |
//+------------------------------------------------------------------+
string MqlDateTimeMin(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Minutes:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.min);
   /* Пример вывода:
      Minutes: 41
   */
  }


Секунды в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки секунды из структуры MqlDateTime        |
//+------------------------------------------------------------------+
string MqlDateTimeSec(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Seconds:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%02lu",indent,"",w,header,date_time.sec);
   /* Пример вывода:
      Seconds: 23
   */
  }


День недели в структуре MqlDateTime:

//+------------------------------------------------------------------+
//| Возвращает в виде строки день недели из структуры MqlDateTime    |
//+------------------------------------------------------------------+
string MqlDateTimeDayWeek(MqlDateTime &date_time,const uint header_width=0,const uint indent=0,bool descr=true)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Day of week:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Получаем название дня недели
   string dw=DayWeek(date_time);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%-s (%-lu)",indent,"",w,header,dw,date_time.day_of_week);
   /* Пример вывода:
      Day of week: Wednesday (3)
   */
  }


Номер дня в году в структуре MqlDateTime:

//+------------------------------------------------------------------+
//|Возвращает в виде строки номер дня в году из структуры MqlDateTime|
//+------------------------------------------------------------------+
string MqlDateTimeDayYear(MqlDateTime &date_time,const uint header_width=0,const uint indent=0)
  {
//--- Определяем текст заголовка и ширину поля заголовка
//--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
   string header="Day of year:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
   return StringFormat("%*s%-*s%-lu",indent,"",w,header,date_time.day_of_year);
   /* Пример вывода:
      Day of year: 199
   */
  }


Пример использования:

Для вывода краткой записи о дате-времени из структуры MqlDateTime напишем функцию, возвращающую дату в формате "DW, Month DD, YYYY, HH:MM:SS":

//+------------------------------------------------------------------+
//| Возвращает в виде строки дату из структуры MqlDateTime           |
//| в формате DW, Month DD, YYYY, HH:MM:SS                           |
//+------------------------------------------------------------------+
string DateTime(MqlDateTime &date_time)
  {
//--- Получаем месяц и первые три символа дня недели
   string mn=Month(date_time);
   string dw=StringSubstr(DayWeek(date_time),0,3);
//--- Возвращаем строку, отформатированную в формате DW, Month DD, YYYY, HH:MM:SS
   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);
   /* Пример вывода:
      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]));
           }
        }
     }
   /* Пример вывода:
      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
   */
  }


Для вывода в журнал всех полей структуры в кратком и табличном форматах на выбор, напишем такую функцию:

//+------------------------------------------------------------------+
//| Выводит в журнал описание всех полей структуры MqlDateTime       |
//+------------------------------------------------------------------+
void MqlDateTimePrint(MqlDateTime &date_time,const bool short_entry=true,const uint header_width=0,const uint indent=0)
  {
//--- Если краткая запись - выводим в журнал дату-время в формате DW, Month DD, YYYY, HH:MM:SS
   if(short_entry)
      Print(DateTime(date_time));
   /* Пример вывода:
      Wed, July 19, 2023, 08:41:23
   */
//--- Иначе
   else
     {
      //--- создаём строку с описанием всех данных структуры с отступами и заданной шириной поля заголовка
      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)
                             );
      //--- Выводим в журнал полученную строку
      Print(res);
     }
   /* Пример вывода:
      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);
   /* Пример вывода:
      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;          // Время последнего обновления цен
   double       bid;           // Текущая цена Bid
   double       ask;           // Текущая цена Ask
   double       last;          // Текущая цена последней сделки (Last)
   ulong        volume;        // Объем для текущей цены Last
   long         time_msc;      // Время последнего обновления цен в миллисекундах
   uint         flags;         // Флаги тиков
   double       volume_real;   // Объем для текущей цены Last c повышенной точностью
  };

Переменная типа MqlTick позволяет за один вызов функции SymbolInfoTick() получить значения Ask, Bid, Last, Volume и время в миллисекундах.

У каждого тика всегда заполняются все параметры, независимо от того, изменились ли данные по сравнению с предыдущим тиком. Это позволяет всегда иметь актуальное состояние цен на любой момент времени без поиска предыдущих значений по тиковой истории. Например, с тиком могла измениться только цена бид, но в структуре помимо новой цены будут указаны и остальные параметры: предыдущая цена аск, объем и т.д.

Чтобы узнать, какие именно данные изменились с текущим тиком, нужно анализировать его флаги:

  • TICK_FLAG_BID – тик изменил цену бид
  • TICK_FLAG_ASK  – тик изменил цену аск
  • TICK_FLAG_LAST – тик изменил цену последней сделки
  • TICK_FLAG_VOLUME – тик изменил объем
  • TICK_FLAG_BUY – тик возник в результате сделки на покупку
  • TICK_FLAG_SELL – тик возник в результате сделки на продажу

    MqlTick, способы вывода на печать

    Для вывода структуры в журнал так же, как и для MqlDateTime, подойдёт функция ArrayPrint():

    void OnStart()
      {
    //--- Объявляем переменную с типом MqlTick
       MqlTick  tick;
    //--- Если последний тик не получен - выводим сообщение об ошибке и уходим
       if(!SymbolInfoTick(Symbol(),tick))
         {
          Print("SymbolInfoTick failed, error: ",(string)GetLastError());
          return;
         }
    //--- Выводим тик при помощи стандартной ArrayPrint()
    //--- Для этого объявляем массив размерностью 1 с типом MqlTick,
    //--- вписываем в него значение переменной tick и выводим на печать
       MqlTick array[1];
       array[0]=tick;
       Print("Last tick (ArrayPrint):");
       ArrayPrint(array);
       /* Пример вывода:
          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()
      {
    //--- Объявляем динамический массив с типом MqlTick
       MqlTick  array[];
    //--- Если последние 10 тиков в массив не получен - выводим сообщение об ошибке и уходим
       if(CopyTicks(Symbol(),array,COPY_TICKS_ALL,0,10)!=10)
         {
          Print("CopyTicks failed, error: ",(string)GetLastError());
          return;
         }
       Print("Last 10 tick (ArrayPrint):");
       ArrayPrint(array);
       /* Пример вывода:
          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.


    Вспомогательные функции.

    Для возврата времени в миллисекундах в виде строки мы уже делали функцию. Здесь она пригодится для возврата времени тика в миллисекундах, воспользуемся ею:

    //+------------------------------------------------------------------+
    //| Принимает дату в мс, возвращает время в формате Date Time.Msc    |
    //+------------------------------------------------------------------+
    string TimeMSC(const long time_msc)
      {
       return StringFormat("%s.%.3hu",string((datetime)time_msc / 1000),time_msc % 1000);
       /* Пример вывода:
          2023.07.13 09:31:58.177
       */
      }
    


    Время в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки время последнего обновления цен         |
    //+------------------------------------------------------------------+
    string MqlTickTime(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Time:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-s",indent,"",w,header,(string)tick.time);
       /* Пример вывода:
          Time: 2023.07.19 20:58:00
       */
      }
    


    Цена Bid в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену Bid                                |
    //+------------------------------------------------------------------+
    string MqlTickBid(const string symbol,const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Bid:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,tick.bid);
       /* Пример вывода:
          Bid: 1.29237
       */
      }
    


    Цена Ask в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену Ask                                |
    //+------------------------------------------------------------------+
    string MqlTickAsk(const string symbol,const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Ask:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,tick.ask);
       /* Пример вывода:
          Ask: 1.29231
       */
      }
    


    Цена последней сделки (Last) в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену Last                                |
    //+------------------------------------------------------------------+
    string MqlTickLast(const string symbol,const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Last:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,tick.last);
       /* Пример вывода:
          Last: 0.00000
       */
      }
    


    Объем для цены Last в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки объём для цены Last                     |
    //+------------------------------------------------------------------+
    string MqlTickVolume(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-I64u",indent,"",w,header,tick.volume);
       /* Пример вывода:
          Volume: 0
       */
      }
    


    Время в миллисекундах в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает как строку время в миллисекундах                      |
    //+------------------------------------------------------------------+
    string MqlTickTimeMSC(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Time msc:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-s",indent,"",w,header,TimeMSC(tick.time_msc));
       /* Пример вывода:
          Time msc: 2023.07.19 21:21:09.732
       */
      }
    


    Флаги тиков в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает как строку флаги тика                                 |
    //+------------------------------------------------------------------+
    string MqlTickFlags(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Flags:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Опеределяем переменную для описания флагов тика
       string flags="";
    //--- Разбираем флаги тика на составляющие
       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 StringFormat("%*s%-*s%-s",indent,"",w,header,flags);
       /* Пример вывода:
          Flags: BID|ASK
       */
      }
    


    Объем для цены Last c повышенной точностью в структуре MqlTick:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки объём для цены Last                     |
    //+------------------------------------------------------------------+
    string MqlTickVolumeReal(const MqlTick &tick,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Volume Real:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.2f",indent,"",w,header,tick.volume_real);
       /* Пример вывода:
          Volume Real: 0.00
       */
      }
    


    Примеры использования.

    Для вывода в журнал данных тика напишем функцию. Чтобы знать с какой точностью выводить в журнал значения цен, в функцию будем передавать имя символа. Так как поля Volume и Volume Real содержат объёмы последней цены Last, то в случае, если Last нулевая (не транслируется), то выводить объёмы не имеет смысла — они тоже нулевые. Чтобы была возможность указать и распечатать индекс тика, взятого из массива тиков, во входных параметрах функции будем передавать этот индекс. По умолчанию его значение равно -1, и при таком значении индекс не выводится на печать.

    //+------------------------------------------------------------------+
    //| Выводит в журнал описание всех полей структуры MqlTick           |
    //| Если Last==0, то поля Last, Volume и Volume Real не выводятся    |
    //+------------------------------------------------------------------+
    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)
      {
    //--- Объявляем переменную для хранения результата
       string res="";
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
       string num=(index==WRONG_VALUE ? "" : StringFormat("[%ld] ",index));
    //--- Если краткая запись - выводим в журнал данные тика в формате Symbol TimeMSC, Bid, Ask, Last, Vol/VolR, Flags
       if(short_entry)
         {
          //--- Если значение Last не нулевое, будем выводить Last, Volume и Volume Real, иначе - они все нулевые и выводить их не имеет смысла
          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);
         }
       /* Пример вывода (если Last не нулевой):
          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
          Пример вывода (если Last нулевой):
          Tick GBPUSD Time: 2023.07.20 13:59:33.274, Bid: 1.28956, Ask: 1.28960, Flags: BID|ASK
       */
    //--- Иначе
       else
         {
          //--- создаём строку с описанием всех данных структуры с отступами и заданной шириной поля заголовка
          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) : "")
                          );
          //--- Выводим в журнал полученную строку
          Print(res);
         }
       /* Пример вывода (если Last не нулевой):
          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
          
          Пример вывода (если Last нулевой):
          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()
      {
    //--- Объявляем динамический массив с типом MqlTick
       MqlTick  array[];
    //--- Если последние 10 тиков в массив не получен - выводим сообщение об ошибке и уходим
       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);
    
       /* Пример вывода:
          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()
      {
    //--- Объявляем динамический массив с типом MqlTick
       MqlTick  array[];
    //--- Если последние 4 тика в массив не получены - выводим сообщение об ошибке и уходим
       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);
         }
    
       /* Пример вывода:
          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;         // время начала периода
       double   open;         // цена открытия
       double   high;         // наивысшая цена за период
       double   low;          // наименьшая цена за период
       double   close;        // цена закрытия
       long     tick_volume;  // тиковый объем
       int      spread;       // спред
       long     real_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);
       /* Пример вывода:
          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);
       /* Пример вывода:
          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:

    Time — время начала периода. Иными словами — время открытия бара на символе и периоде графика, с которого запрошены данные, записанные в структуру.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки время открытия бара                     |
    //+------------------------------------------------------------------+
    string MqlRatesTime(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Time:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-s",indent,"",w,header,(string)rates.time);
       /* Пример вывода:
          Time: 2023.07.21 06:00:00
       */
      }
    


    Open:

    Цена открытия бара на символе и периоде графика, с которого запрошены данные, записанные в структуру.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену открытия бара                      |
    //+------------------------------------------------------------------+
    string MqlRatesOpen(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Open:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.open);
       /* Пример вывода:
          Open: 1.28812
       */
      }
    


    High:

    Цена High — наивысшая цена бара на символе и периоде графика, с которого запрошены данные, записанные в структуру.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену High бара                          |
    //+------------------------------------------------------------------+
    string MqlRatesHigh(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="High:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.high);
       /* Пример вывода:
          High: 1.28859
       */
      }
    


    Low:

    Цена Low — наименьшая цена бара на символе и периоде графика, с которого запрошены данные, записанные в структуру.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену Low бара                           |
    //+------------------------------------------------------------------+
    string MqlRatesLow(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Low:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.low);
       /* Пример вывода:
          Low: 1.28757
       */
      }
    


    Close:

    Цена Close — цена закрытия бара на символе и периоде графика, с которого запрошены данные, записанные в структуру.
    Для текущего бара цена закрытия равна текущей цене Bid или Last в зависимости от того по какой цене строится график.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену закрытия бара                      |
    //+------------------------------------------------------------------+
    string MqlRatesClose(const string symbol,const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Close:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,rates.close);
       /* Пример вывода:
          Close: 1.28770
       */
      }
    


    TickVolume:

    Тиковый объём бара.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки тиковый объём бара                      |
    //+------------------------------------------------------------------+
    string MqlRatesTickVolume(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Tick Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-lld",indent,"",w,header,rates.tick_volume);
       /* Пример вывода:
          Tick Volume: 963
       */
      }
    


    Spread:

    Спред на баре.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки спред на баре                           |
    //+------------------------------------------------------------------+
    string MqlRatesSpread(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Spread:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-ld",indent,"",w,header,rates.spread);
       /* Пример вывода:
          Spread: 4
       */
      }
    


    RealVolume:

    Биржевой объём бара.

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки биржевой объём бара                     |
    //+------------------------------------------------------------------+
    string MqlRatesRealVolume(const MqlRates &rates,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Real Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-lld",indent,"",w,header,rates.real_volume);
       /* Пример вывода:
          Real Volume: 0
       */
      }
    


    Примеры использования.

    Чтобы распечатать в журнале данные последних 10 баров, напишем такой скрипт:

    void OnStart()
      {
    //--- Копируем 10 последних баров данных в массив MqlRates
       MqlRates array[];
       if(CopyRates(Symbol(),PERIOD_CURRENT,0,10,array)!=10)
         {
          Print("CopyRates failed, error: ",(string)GetLastError());
          return;
         }
    
    //--- Устанавливаем индексацию массива как у таймсерии
       ArraySetAsSeries(array,true);
    //--- В цикле по массиву с полученными данными баров распечатываем в журнале краткие записи
       for(int i=0;i<(int)array.Size();i++)
          MqlRatesPrint(Symbol(),PERIOD_CURRENT,array[i],true,0,0,i);
       
       /* Пример вывода:
          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()
      {
    //--- Копируем 4 последних бара данных в массив MqlRates
       MqlRates array[];
       if(CopyRates(Symbol(),PERIOD_CURRENT,0,4,array)!=4)
         {
          Print("CopyRates failed, error: ",(string)GetLastError());
          return;
         }
    
    //--- Устанавливаем индексацию массива как у таймсерии
       ArraySetAsSeries(array,true);
    //--- В цикле по массиву с полученными данными баров распечатываем в журнале краткие записи
       for(int i=0;i<(int)array.Size();i++)
          MqlRatesPrint(Symbol(),PERIOD_CURRENT,array[i],false,14,2,i);
       
       /* Пример вывода:
          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;            // тип заявки из перечисления ENUM_BOOK_TYPE
       double           price;           // цена
       long             volume;          // объем
       double           volume_real;     // объем с повышенной точностью
      };

    Чтобы использовать структуру, достаточно объявить переменную данного типа. Стакан доступен не для всех финансовых инструментов.Перед получением данных стакана необходимо подписаться на получение стакана цен при помощи MarketBookAdd(). По завершению работы от стакана нужно отписаться: MarketBookRelease(). Для обработки приходящих извещений в программе эксперта должна присутствовать функция void OnBookEvent().


    MqlBookInfo, способы вывода на печать

    Каждое получение стакана цен предполагает получение списка заявок, находящихся в стакане. Соответственно, это массив данных. Поэтому распечатать один снимок стакана цен мы можем с помощью ArrayPrint():

    void OnStart()
      {
    //--- Объявляем массив для хранения снимка стакана цен
       MqlBookInfo array[];
    //--- Если не получилось открыть стакан цен и подписаться на его события - сообщаем об этом и уходим
       if(!MarketBookAdd(Symbol()))
         {
          Print("MarketBookAdd failed, error: ",(string)GetLastError());
          return;
         }
    //--- Если записи стакана цен получить не удалось - сообщаем об этом и уходим
       if(!MarketBookGet(Symbol(),array))
         {
          Print("MarketBookGet failed, error: ",(string)GetLastError());
          return;
         }
    //--- Распечатываем в журнале заголовок и ниже снимок стакана цен из массива
       Print("MarketBookInfo by ",Symbol(),":");
       ArrayPrint(array);
    //--- Если не получилось отписаться от стакана цен - сообщим об ошибке в журнал
       if(!MarketBookRelease(Symbol()))
          Print("MarketBookRelease failed, error: ",(string)GetLastError());
       /* Пример вывода:
          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:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки тип заявки в стакане цен                |
    //+------------------------------------------------------------------+
    string MqlBookInfoType(const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Получаем значение типа заявки
       ENUM_BOOK_TYPE book_type=book.type;
    //--- "Вырезаем" из строки, полученной из enum, только тип
       string type=StringSubstr(EnumToString(book_type),10);
    //--- Преобразуем все полученные символы в нижний регистр и заменяем первую букву с маленькой на заглавную
       if(type.Lower())
          type.SetChar(0,ushort(type.GetChar(0)-0x20));
    //--- Заменяем в полученной строке все символы подчёркивания на символ пробела
       StringReplace(type,"_"," ");
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Type:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-s",indent,"",w,header,type);
       /* Пример вывода:
          Type: Sell
       */
      }
    


    Цена заявки в структуре стакана цен MqlBookInfo:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки цену заявки в стакане цен               |
    //+------------------------------------------------------------------+
    string MqlBookInfoPrice(const string symbol,const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Price:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Получаем количество знаков после запятой
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,book.price);
       /* Пример вывода:
          Price: 1.28498
       */
      }
    


    Объем заявки в структуре стакана цен MqlBookInfo:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки объём заявки в стакане цен              |
    //+------------------------------------------------------------------+
    string MqlBookInfoVolume(const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Volume:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-lld",indent,"",w,header,book.volume);
       /* Пример вывода:
          Volume: 100
       */
      }
    


    Объем с повышенной точностью:

    //+------------------------------------------------------------------+
    //| Возвращает в виде строки объём заявки с повышенной точностью     |
    //+------------------------------------------------------------------+
    string MqlBookInfoVolumeReal(const MqlBookInfo &book,const uint header_width=0,const uint indent=0)
      {
    //--- Определяем текст заголовка и ширину поля заголовка
    //--- Если ширина заголовка передана в функцию равной нулю, то шириной будет размер строки заголовка + 1
       string header="Volume Real:";
       uint w=(header_width==0 ? header.Length()+1 : header_width);
    //--- Возвращаем значение свойства с заголовком с нужной шириной и отступом
       return StringFormat("%*s%-*s%-.2f",indent,"",w,header,book.volume_real);
       /* Пример вывода:
          Volume Real: 100.00
       */
      }
    


    Примеры использования:

    Теперь напишем функцию, распечатывающую в журнал все данные структуры MqlBookInfo. Распечатать можно будет в двух режимах: в одну строку и в табличном виде:

    //+------------------------------------------------------------------+
    //| Выводит в журнал описание всех полей структуры MqlRates          |
    //+------------------------------------------------------------------+
    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)
      {
    //--- Объявляем переменную для хранения результата
       string res="";
    //--- Получаем количество знаков после запятой и строковое значение индекса
       int dg=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
       string num=(index==WRONG_VALUE ? "" : StringFormat("[%02ld]",index));
       
    //--- "Вырезаем" из строки с наименованием типа заявки, полученной из enum, только тип
       string type=StringSubstr(EnumToString(book.type),10);
    //--- Преобразуем все полученные символы в нижний регистр и заменяем первую букву с маленькой на заглавную
       if(type.Lower())
          type.SetChar(0,ushort(type.GetChar(0)-0x20));
    //--- Заменяем в полученной строке все символы подчёркивания на символ пробела
       StringReplace(type,"_"," ");
       
    //--- Если краткая запись - выводим в журнал данные записи стакана в формате [index] Type Price V VR
       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);
         }
       /* Пример вывода:
          [00]    Sell        1.28598  Volume 100  Real 100.00 
       */
    //--- Иначе
       else
         {
          //--- создаём строку с описанием всех данных структуры с отступами и заданной шириной поля заголовка
          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)
                          );
          //--- Выводим в журнал полученную строку
          Print(res);
         }
       /* Пример вывода
          BoolInfo by GBPUSD [00]:
            Type:         Sell
            Price:        1.28588
            Volume:       100
            Volume Real:  100.00
       */
      }
    

    Основным режимом тут должен быть вывод в журнал в одну строку, так как всё же стакан цен — это не одна заявка, и получаем мы список этих заявок в массив. Соответственно, можно этот массив и распечатать в журнал при помощи такой функции:

    //+------------------------------------------------------------------+
    //| Выводит в журнал записи стакана цен в кратком формате            |
    //+------------------------------------------------------------------+
    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()
      {
    //--- Объявляем массив для хранения снимка стакана цен
       MqlBookInfo array[];
    //--- Если не получилось открыть стакан цен и подписаться на его события - сообщаем об этом и уходим
       if(!MarketBookAdd(Symbol()))
         {
          Print("MarketBookAdd failed, error: ",(string)GetLastError());
          return;
         }
    //--- Если записи стакана цен получить не удалось - сообщаем об этом и уходим
       if(!MarketBookGet(Symbol(),array))
         {
          Print("MarketBookGet failed, error: ",(string)GetLastError());
          return;
         }
    //--- Распечатываем в журнале снимок стакана цен из массива в виде строк
       MqlBookInfoPrintShort(Symbol(),array);
    
    //--- Если не получилось отписаться от стакана цен - сообщим об ошибке в журнал
       if(!MarketBookRelease(Symbol()))
          Print("MarketBookRelease failed, error: ",(string)GetLastError());
       
       /* Пример вывода:
          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 
       */
      }
    

    Но иногда, быть может, потребуется вывести те же данные, но в табличном виде. Для этого можно использовать такую функцию:

    //+------------------------------------------------------------------+
    //| Выводит в журнал записи стакана цен в табличном формате          |
    //+------------------------------------------------------------------+
    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()
      {
    //--- Объявляем массив для хранения снимка стакана цен
       MqlBookInfo array[];
    //--- Если не получилось открыть стакан цен и подписаться на его события - сообщаем об этом и уходим
       if(!MarketBookAdd(Symbol()))
         {
          Print("MarketBookAdd failed, error: ",(string)GetLastError());
          return;
         }
    //--- Если записи стакана цен получить не удалось - сообщаем об этом и уходим
       if(!MarketBookGet(Symbol(),array))
         {
          Print("MarketBookGet failed, error: ",(string)GetLastError());
          return;
         }
    //--- Распечатываем в журнале снимок стакана цен из массива в виде строк
       MqlBookInfoPrintTable(Symbol(),array,14,2);
    
    //--- Если не получилось отписаться от стакана цен - сообщим об ошибке в журнал
       if(!MarketBookRelease(Symbol()))
          Print("MarketBookRelease failed, error: ",(string)GetLastError());
       
       /* Пример вывода:
          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
       */
      }
    


    Заключение

    Мы рассмотрели вывод на печать полей четырёх структур: MqlDateTime, MqlTick, MqlRates и MqlBookInfo. Созданные функции возвращают описание полей каждой структуры в формате "Заголовок-Данные" в виде строки, которую можно отправить на печать, либо использовать внутри другой функции. Все функции самодостаточны, готовы к применению, и их можно использовать "как есть" в своих программах. На очереди структуры торговых транзакций — их описание и вывод в журнал.


    Последние комментарии | Перейти к обсуждению на форуме трейдеров (5)
    Aliaksandr Hryshyn
    Aliaksandr Hryshyn | 24 июл. 2023 в 15:07
    Данного автора уже по названию статьи определяю :)))))
    Aleksandr Slavskii
    Aleksandr Slavskii | 24 июл. 2023 в 17:26

    Если эти статьи станут серией, то это будет просто замечательно!!!

    Как же мне не хватало подобных статей года четыре назад, когда я начал учится писать код.

    Да и сейчас нахожу в них, то чего ещё не знал.

    Особенно в предыдущей про StringFormat и PrintFormat очень познавательно, учитывая, что справка по этим функциям весьма скудная.

    Спасибо автору за интересные и нужные статьи!

    Zhongquan Jiang
    Zhongquan Jiang | 28 июл. 2023 в 03:19
    Очень хорошая статья. Где находится файл mqh, который мы можем скачать?
    Artyom Trishkin
    Artyom Trishkin | 28 июл. 2023 в 04:44
    Zhongquan Jiang # :
    Very good article. Where is the mqh file for us to download?

    Files are not attached to the article, as it was conceived as a kind of extended reference information. You can simply use Copy-Paste codes from the article in the MetaEditor code editor.

    Alexey Viktorov
    Alexey Viktorov | 28 июл. 2023 в 08:58
    Artyom Trishkin #:

    Files are not attached to the article, as it was conceived as a kind of extended reference information. You can simply use Copy-Paste codes from the article in the MetaEditor code editor.

    Нейросети — это просто (Часть 51): Актор-критик, управляемый поведением (BAC) Нейросети — это просто (Часть 51): Актор-критик, управляемый поведением (BAC)
    В последних двух статьях рассматривался алгоритм Soft Actor-Critic, который включает энтропийную регуляризацию в функцию вознаграждения. Этот подход позволяет балансировать исследование среды и эксплуатацию модели, но он применим только к стохастическим моделям. В данной статье рассматривается альтернативный подход, который применим как для стохастических, так и для детерминированных моделей.
    Сделайте торговые графики лучше с интерактивным графическим интерфейсом на основе MQL5 (Часть I): Перемещаемый интерфейс (I) Сделайте торговые графики лучше с интерактивным графическим интерфейсом на основе MQL5 (Часть I): Перемещаемый интерфейс (I)
    Раскройте всю мощь динамического представления данных в своих торговых стратегиях или утилитах с помощью нашего подробного руководства по разработке перемещаемого графического интерфейса в MQL5. Погрузитесь в события графика и узнайте, как спроектировать и реализовать простой и множественный перемещаемый графический интерфейс на одном графике. В статье также рассматриваются добавление элементов в графический интерфейс, повышение их функциональности и эстетической привлекательности.
    Теория категорий в MQL5 (Часть 10): Моноидные группы Теория категорий в MQL5 (Часть 10): Моноидные группы
    Статья продолжает серию о реализации теории категорий в MQL5. Здесь мы рассматриваем группы моноидов как средство, нормализующее множества моноидов и делающее их более сопоставимыми в более широком диапазоне множеств моноидов и типов данных.
    Брутфорс-подход к поиску закономерностей (Часть V): Взгляд с другой стороны Брутфорс-подход к поиску закономерностей (Часть V): Взгляд с другой стороны
    В статье я покажу совершенно иной подход к алготрейдингу, к которому мне пришлось прийти спустя достаточно длительное время. Конечно же все это связано с моей брутфорс программой, которая претерпела ряд изменений, которые позволяют ей решать одновременно несколько задач. Тем не менее статья получилась больше общей и максимально простой, по этому годится и для тех кто не в теме или просто проходил мимо.