English Русский 中文 Español 日本語 Português
Zeitreihen in der Bibliothek DoEasy (Teil 35): das Bar-Objekt und die Liste der Zeitreihen eines Symbols

Zeitreihen in der Bibliothek DoEasy (Teil 35): das Bar-Objekt und die Liste der Zeitreihen eines Symbols

MetaTrader 5Beispiele | 1 Juni 2020, 10:56
650 0
Artyom Trishkin
Artyom Trishkin

Inhalt

Dieser Artikel beginnt den neuen Abschnitt in der Beschreibung der Bibliothek zur bequemen Entwicklung von Programmen für MetaTrader 5 und 4 Terminals. Die erste Serie (34 Artikel) war dem Konzept der Bibliotheksobjekte und ihrer Verbindungen gewidmet. Das Konzept wurde verwendet, um die Funktionen für die Arbeit mit einem Konto zu entwickeln — seinen aktuellen Status und seine Historie.

Gegenwärtig verfügt die Bibliothek über die folgenden Funktionen:

  • Suchen, Sortieren und Vergleichen der Daten von jeder Order oder Position,
  • schneller und bequemer Zugang zu allen Eigenschaften von Order und Position,
  • Verfolgen jedes Kontoereignisses,
  • Beschaffen und Vergleichen von Konto- und Symboldaten,
  • Reagieren auf Eigenschaftsänderungen aller in Datenbanken (Sammlungen) vorhandenen Objekte und Senden von Benachrichtigungen über aufgetretene Ereignisse an das Programm.

Wir können der Bibliothek auch mitteilen, auf welche Ereignisse sie reagieren und Benachrichtigungen über die überwachten Ereignisse senden soll.

Außerdem haben wir die Möglichkeit implementiert, mit den Handelsfunktionen des Terminals zu arbeiten. Wir haben auch neue Arten von Handelsanfragen entwickelt — schwebende Handelsanfragen, die es uns ermöglichen, die Verhaltenslogik des Programms unter verschiedenen Handelsbedingungen zu erstellen, sowie den vollständigen Satz von Funktionen für die Erstellung neuer Arten von schwebenden Aufträgen bereitzustellen.
All dies und noch viel mehr wurde in den vorangegangenen Artikeln der Bibliotheksentwicklungsbeschreibungen erstellt und beschrieben.

Die zweite Reihe der Bibliotheksbeschreibung berücksichtigt viele Aspekte der Arbeit mit Preisdaten, Symboldiagrammen, Markttiefen, Indikatoren usw. Generell soll die neue Artikelserie der Entwicklung der Bibliotheksfunktionalität für den schnellen Zugriff, die Suche, den Vergleich und die Sortierung beliebiger Preisdatenfelder sowie deren Speicherobjekte und Quellen gewidmet werden.

Konzept

Das Konzept der Anordnung der Bibliotheksobjekte, ihre Aufbewahrung und die Arbeit mit ihnen wurden in den ersten Artikeln der vorherigen Serie ausführlich beschrieben. Im aktuellen Artikel werde ich kurz die Grundprinzipien des Ordnens und Aufbewahrens der Bibliotheksobjekte wiederholen.

Jeder Satz gleichartiger Daten kann als eine Liste von Objekten mit identischen Eigenschaften dargestellt werden, die diesem Datentyp eigen sind. Jedes Objekt aus der Liste gleichartiger Objekte weist einen ähnlichen Satz von Eigenschaften auf, obwohl jedes Objekt der gleichen sortierten Liste unterschiedliche Werte hat.

Jede Liste, die einen Satz gleichartiger Objekte speichert, kann nach jeder der Eigenschaften sortiert werden, die die Listenobjekte haben.

Die Objekteigenschaften haben drei Haupttypen — ganzzahlige, reelle und Zeichenketten.

Die Sortierung solcher Objekte in sortierten Listen ermöglicht es uns, jedes der Objekte schnell zu finden, alle Daten daraus zu erhalten und sie in benutzerdefinierten Programmen zu verwenden.

Fast alle Bibliotheksobjekte sind in der Lage, ihre eigenen Eigenschaften unabhängig zu verfolgen und ihre Werte um einen bestimmten Betrag zu ändern, der für jedes Objekt, an dem wir interessiert sind, eingestellt werden kann. Wenn Objekteigenschaften um einen bestimmten Wert geändert werden, sendet das verfolgte Objekt die Meldung an das Steuerprogramm, dass der für die Verfolgung eingestellte Schwellenwert erreicht wurde.

Die Basis aller Bibliotheksobjekte ist ein Basis-Standard-Bibliotheksobjekt, das mit der Handelsplattform geliefert wird, während die Objektliste (Objektkollektion) ein dynamisches Array von Zeigern auf CObject-Klasseninstanzen und ihre abgeleiteten Klassen der Standard-Bibliothek ist.

Im aktuellen Artikel werde ich das Bar-Objekt erstellen, das die gesamten Daten eines einzelnen Balkens in einem Symboldiagramm sowie die Liste aller Bar-Objekte für ein einzelnes Symbol und einen einzelnen Zeitrahmen speichern soll.

Jede Bar eines Symboldiagramms hat einen bestimmten Satz von Parametern, die in der Struktur MqlRates beschrieben sind:

  • Perioden-Startzeit
  • Eröffnungspreis
  • höchster Preis im Zeitraum
  • niedrigster Preis im Zeitraum
  • Schlusskurs
  • Tick-Volumen
  • Spreizung (Spread)
  • Börsen-Volumen

Abgesehen von den Eigenschaften des Bar-Objekts können wir die zusätzlichen Eigenschaften direkt beim Erstellen eines Objekts festlegen:

  • Das Jahr, zu dem die Bar gehört.
  • Der Monat, zu dem der Balken gehört.
  • Der Wochentag der Bar.
  • Die Seriennummer der Bar im Jahr.
  • Der Tag des Monats (Nummer).
  • Die Stunde der Bar.
  • Die Minute der Bar.
  • Balkenindex in einer Zeitreihe des Symbols.
  • Bar (von Hoch bis Tief).
  • Kerzenkörper Oben (Max(Open,Close)).
  • Kerzenkörper Unten (Min(Open,Close)).
  • Kerzenkörpergröße (Oben - Unten).
  • Kerzendocht oben (vom Hoch bis Oben des Kerzenkörpers).
  • Kerzendocht unten (von Unten des Kerzenkörpers bis zum Tief).

Alle diese Eigenschaften ermöglichen es uns, nach beliebigen Kombinationen von Bars und Kerzen (in der Barliste gespeichert) innerhalb des Bereichs zu suchen. Der gewünschte Bereich der gespeicherten Daten kann für die Liste der Bar-Objekte eingestellt werden. Standardmäßig speichern wir tausend Bars in der Liste (oder die gesamte verfügbare Historie der Bars des Symbols, wenn es sich um weniger als tausend Bars oder einen anderen festgelegten Bereich handelt).

Das Bar-Objekt

Erstellen wir die Aufzählungen aller Eigenschaften der Bar-Objekte. Dazu öffnen wir die Datei Defines.mqh, die in \MQL5\Include\DoEasy\Defines.mqh gespeichert ist und fügen am Ende der Datei die Enumeration der Eigenschaften des Bar-Typs, ganzzahlig, reell und Zeichen der Bar-Objekte, sowie die Methoden zum Sortieren der Liste der Bar-Objekte hinzu:

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Info for working with serial data                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Bar type (candle body)                                           |
//+------------------------------------------------------------------+
enum ENUM_BAR_BODY_TYPE
  {
   BAR_BODY_TYPE_BULLISH,                                   // Bullish bar
   BAR_BODY_TYPE_BEARISH,                                   // Bearish bar
   BAR_BODY_TYPE_NULL,                                      // Zero bar
   BAR_BODY_TYPE_CANDLE_ZERO_BODY,                          // Candle with a zero body
  };
//+------------------------------------------------------------------+
//| Bar integer properties                                           |
//+------------------------------------------------------------------+
enum ENUM_BAR_PROP_INTEGER
  {
   BAR_PROP_INDEX = 0,                                      // Bar index in timeseries
   BAR_PROP_TYPE,                                           // Bar type (from the ENUM_BAR_BODY_TYPE enumeration)
   BAR_PROP_PERIOD,                                         // Bar period (timeframe)
   BAR_PROP_SPREAD,                                         // Bar spread
   BAR_PROP_VOLUME_TICK,                                    // Bar tick volume
   BAR_PROP_VOLUME_REAL,                                    // Bar exchange volume
   BAR_PROP_TIME,                                           // Bar period start time
   BAR_PROP_TIME_DAY_OF_YEAR,                               // Bar day serial number in a year
   BAR_PROP_TIME_YEAR,                                      // A year the bar belongs to
   BAR_PROP_TIME_MONTH,                                     // A month the bar belongs to
   BAR_PROP_TIME_DAY_OF_WEEK,                               // Bar week day
   BAR_PROP_TIME_DAY,                                       // Bar day of month (number)
   BAR_PROP_TIME_HOUR,                                      // Bar hour
   BAR_PROP_TIME_MINUTE,                                    // Bar minute
  }; 
#define BAR_PROP_INTEGER_TOTAL (14)                         // Total number of integer bar properties
#define BAR_PROP_INTEGER_SKIP  (0)                          // Number of bar properties not used in sorting
//+------------------------------------------------------------------+
//| Real bar properties                                              |
//+------------------------------------------------------------------+
enum ENUM_BAR_PROP_DOUBLE
  {
//--- bar data
   BAR_PROP_OPEN = BAR_PROP_INTEGER_TOTAL,                  // Bar open price
   BAR_PROP_HIGH,                                           // Highest price for the bar period
   BAR_PROP_LOW,                                            // Lowest price for the bar period
   BAR_PROP_CLOSE,                                          // Bar close price
//--- candle data
   BAR_PROP_CANDLE_SIZE,                                    // Candle size
   BAR_PROP_CANDLE_SIZE_BODY,                               // Candle body size
   BAR_PROP_CANDLE_BODY_TOP,                                // Candle body top
   BAR_PROP_CANDLE_BODY_BOTTOM,                             // Candle body bottom
   BAR_PROP_CANDLE_SIZE_SHADOW_UP,                          // Candle upper wick size
   BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,                        // Candle lower wick size
  }; 
#define BAR_PROP_DOUBLE_TOTAL  (10)                         // Total number of real bar properties
#define BAR_PROP_DOUBLE_SKIP   (0)                          // Number of bar properties not used in sorting
//+------------------------------------------------------------------+
//| Bar string properties                                            |
//+------------------------------------------------------------------+
enum ENUM_BAR_PROP_STRING
  {
   BAR_PROP_SYMBOL = (BAR_PROP_INTEGER_TOTAL+BAR_PROP_DOUBLE_TOTAL), // Bar symbol
  };
#define BAR_PROP_STRING_TOTAL  (1)                          // Total number of string bar properties
//+------------------------------------------------------------------+
//| Possible bar sorting criteria                                    |
//+------------------------------------------------------------------+
#define FIRST_BAR_DBL_PROP          (BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP)
#define FIRST_BAR_STR_PROP          (BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP+BAR_PROP_DOUBLE_TOTAL-BAR_PROP_DOUBLE_SKIP)
enum ENUM_SORT_BAR_MODE
  {
//--- Sort by integer properties
   SORT_BY_BAR_INDEX = 0,                                   // Sort by bar index
   SORT_BY_BAR_TYPE,                                        // Sort by bar type (from the ENUM_BAR_BODY_TYPE enumeration)
   SORT_BY_BAR_PERIOD,                                      // Sort by bar period (timeframe)
   SORT_BY_BAR_SPREAD,                                      // Sort by bar spread
   SORT_BY_BAR_VOLUME_TICK,                                 // Sort by bar tick volume
   SORT_BY_BAR_VOLUME_REAL,                                 // Sort by bar exchange volume
   SORT_BY_BAR_TIME,                                        // Sort by bar period start time
   SORT_BY_BAR_TIME_DAY_OF_YEAR,                            // Sort by bar day number in a year
   SORT_BY_BAR_TIME_YEAR,                                   // Sort by a year the bar belongs to
   SORT_BY_BAR_TIME_MONTH,                                  // Sort by a month the bar belongs to
   SORT_BY_BAR_TIME_DAY_OF_WEEK,                            // Sort by a bar week day
   SORT_BY_BAR_TIME_DAY,                                    // Sort by a bar day
   SORT_BY_BAR_TIME_HOUR,                                   // Sort by a bar hour
   SORT_BY_BAR_TIME_MINUTE,                                 // Sort by a bar minute
//--- Sort by real properties
   SORT_BY_BAR_OPEN = FIRST_BAR_DBL_PROP,                   // Sort by bar open price
   SORT_BY_BAR_HIGH,                                        // Sort by the highest price for the bar period
   SORT_BY_BAR_LOW,                                         // Sort by the lowest price for the bar period
   SORT_BY_BAR_CLOSE,                                       // Sort by a bar close price
   SORT_BY_BAR_CANDLE_SIZE,                                 // Sort by a candle price
   SORT_BY_BAR_CANDLE_SIZE_BODY,                            // Sort by a candle body size
   SORT_BY_BAR_CANDLE_BODY_TOP,                             // Sort by a candle body top
   SORT_BY_BAR_CANDLE_BODY_BOTTOM,                          // Sort by a candle body bottom
   SORT_BY_BAR_CANDLE_SIZE_SHADOW_UP,                       // Sort by candle upper wick size
   SORT_BY_BAR_CANDLE_SIZE_SHADOW_DOWN,                     // Sort by candle lower wick size
//--- Sort by string properties
   SORT_BY_BAR_SYMBOL = FIRST_BAR_STR_PROP,                 // Sort by a bar symbol
  };
//+------------------------------------------------------------------+

Der Typ einer Bar kann auf-, abwärts oder Null sein. Ein separater Eigenschaftstyp der Bars wird verwendet, um einen Kerzenkörpertyp einzustellen, da die Kerzenkörpergröße einer der Parameter ist, der zur Definition verschiedener Kerzenformationen verwendet wird. Daher sollte eine Kerzenkörpergröße von Null nicht als äquivalent zum Null-Bar betrachtet werden. Eine Null-Bar hat nur einen Preis für alle seine vier OHLC-Preise, während die Nullkerzenkörper-Kerze Dochte haben kann, während die Lage und die Dochtgröße bei der Definition von Kerzenformationen berücksichtigt werden.

Um Beschreibungen der Bareigenschaften und einige andere Bibliotheksnachrichten anzuzeigen, benötigen wir neue Textnachrichten.
Die Datei Datas.mqh befindet sich in \MQL5\Include\DoEasy\Datas.mqh und erhält die Indizes der neuer Nachrichten:

   MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE,               // Return code out of range of error codes
   MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ,               // Failed to create the \"Pause\" object
   MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ,                 // Failed to create the \"Bar\" object
   MSG_LIB_SYS_FAILED_SYNC_DATA,                      // Failed to synchronize data with the server

...

   MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY,               // Order lifetime till the end of the current day to be used
   
   MSG_LIB_TEXT_JANUARY,                              // January
   MSG_LIB_TEXT_FEBRUARY,                             // February
   MSG_LIB_TEXT_MARCH,                                // March
   MSG_LIB_TEXT_APRIL,                                // April
   MSG_LIB_TEXT_MAY,                                  // May
   MSG_LIB_TEXT_JUNE,                                 // June
   MSG_LIB_TEXT_JULY,                                 // July
   MSG_LIB_TEXT_AUGUST,                               // August
   MSG_LIB_TEXT_SEPTEMBER,                            // September
   MSG_LIB_TEXT_OCTOBER,                              // October
   MSG_LIB_TEXT_NOVEMBER,                             // November
   MSG_LIB_TEXT_DECEMBER,                             // December
   
   MSG_LIB_TEXT_SUNDAY,                               // Sunday

...

   MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS,          // Added pending request activation conditions

//--- CBar
   MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA,              // Failed to receive bar data
   MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA,           // Failed to receive timeseries data
   MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST,               // Could not add bar object to the list
   MSG_LIB_TEXT_BAR,                                  // Bar
   MSG_LIB_TEXT_BAR_PERIOD,                           // Timeframe
   MSG_LIB_TEXT_BAR_SPREAD,                           // Spread
   MSG_LIB_TEXT_BAR_VOLUME_TICK,                      // Tick volume
   MSG_LIB_TEXT_BAR_VOLUME_REAL,                      // Exchange volume
   MSG_LIB_TEXT_BAR_TIME,                             // Period start time
   MSG_LIB_TEXT_BAR_TIME_YEAR,                        // Year
   MSG_LIB_TEXT_BAR_TIME_MONTH,                       // Month
   MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR,                 // Day serial number in a year
   MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK,                 // Week day
   MSG_LIB_TEXT_BAR_TIME_DAY,                         // Two months
   MSG_LIB_TEXT_BAR_TIME_HOUR,                        // Hour
   MSG_LIB_TEXT_BAR_TIME_MINUTE,                      // Minute
   MSG_LIB_TEXT_BAR_INDEX,                            // Index in timeseries
   MSG_LIB_TEXT_BAR_HIGH,                             // Highest price for the period
   MSG_LIB_TEXT_BAR_LOW,                              // Lowest price for the period
   MSG_LIB_TEXT_BAR_CANDLE_SIZE,                      // Candle size
   MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY,                 // Candle body size
   MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP,            // Candle upper wick size
   MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN,          // Candle lower wick size
   MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP,                  // Candle body top
   MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM,               // Candle body bottom
   MSG_LIB_TEXT_BAR_TYPE_BULLISH,                     // Bullish bar
   MSG_LIB_TEXT_BAR_TYPE_BEARISH,                     // Bearish bar
   MSG_LIB_TEXT_BAR_TYPE_NULL,                        // Zero bar
   MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY,            // Candle with a zero body
   MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA,        // First, we need to set the required amount of data using SetAmountUsedData()
  
  };
//+------------------------------------------------------------------+

und Textnachrichten, die den neu hinzugefügten Indizes entsprechen:

   {"Код возврата вне заданного диапазона кодов ошибок","Out of range of error codes return code"},
   {"Не удалось создать объект \"Пауза\"","Failed to create \"Pause\" object"},
   {"Не удалось создать объект \"Бар\"","Failed to create \"Bar\" object"},
   {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"},

...

   {"Будет использоваться время действия ордера до конца текущего дня","Order validity time until the end of the current day will be used"},

   {"Январь","January"},
   {"Февраль","February"},
   {"Март","March"},
   {"Апрель","April"},
   {"Май","May"},
   {"Июнь","June"},
   {"Июль","July"},
   {"Август","August"},
   {"Сентябрь","September"},
   {"Октябрь","October"},
   {"Ноябрь","November"},
   {"Декабрь","December"},
   
   {"Воскресение","Sunday"},

...

   {"Добавлены условия активации отложенного запроса","Pending request activation conditions added"},
   
   {"Не удалось получить данные бара","Failed to get bar data"},
   {"Не удалось получить данные таймсерии","Failed to get timeseries data"},
   {"Не удалось добавить объект-бар в список","Failed to add bar object to list"},
   {"Бар","Bar"},
   {"Таймфрейм","Timeframe"},
   {"Спред","Spread"},
   {"Тиковый объём","Tick volume"},
   {"Биржевой объём","Real volume"},
   {"Время начала периода","Period start time"},
   {"Год","Year"},
   {"Месяц","Month"},
   {"Порядковый номер дня в году","Sequence day number in a year"},
   {"День недели","Day of week"},
   {"День месяца","Day of month"},
   {"Час","Hour"},
   {"Минута","Minute"},
   {"Индекс в таймсерии","Timeseries index"},
   {"Наивысшая цена за период","Highest price for the period"},
   {"Наименьшая цена за период","Lowest price for the period"},
   {"Размер свечи","Candle size"},
   {"Размер тела свечи","Candle body size"},
   {"Размер верхней тени свечи","Candle upper shadow size"},
   {"Размер нижней тени свечи","Candle lower shadow size"},
   {"Верх тела свечи","Top of candle body"},
   {"Низ тела свечи","Bottom of candle body"},
   
   {"Бычий бар","Bullish bar"},
   {"Медвежий бар","Bearish bar"},
   {"Нулевой бар","Zero bar"},
   {"Свеча с нулевым телом","Candle with zero body"},
   {"Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData()","First you need to set required amount of data using SetAmountUsedData()"},
   
  };
//+---------------------------------------------------------------------+

Die Datei für die Dienstfunktionen DELib.mqh befindet sich in \MQL5\Include\DoEasy\Services\DELib.mqh und erhält die Funktion, die einen Monatsnamen zurückgibt und die Funktion, die eine Zeitrahmenbeschreibung zurückgibt:

//+------------------------------------------------------------------+
//| Return month names                                               |
//+------------------------------------------------------------------+
string MonthDescription(const int month)
  {
   return
     (
      month==1    ?  CMessage::Text(MSG_LIB_TEXT_JANUARY)   :
      month==2    ?  CMessage::Text(MSG_LIB_TEXT_FEBRUARY)  :
      month==3    ?  CMessage::Text(MSG_LIB_TEXT_MARCH)     :
      month==4    ?  CMessage::Text(MSG_LIB_TEXT_APRIL)     :
      month==5    ?  CMessage::Text(MSG_LIB_TEXT_MAY)       :
      month==6    ?  CMessage::Text(MSG_LIB_TEXT_JUNE)      :
      month==7    ?  CMessage::Text(MSG_LIB_TEXT_JULY)      :
      month==8    ?  CMessage::Text(MSG_LIB_TEXT_AUGUST)    :
      month==9    ?  CMessage::Text(MSG_LIB_TEXT_SEPTEMBER) :
      month==10   ?  CMessage::Text(MSG_LIB_TEXT_OCTOBER)   :
      month==11   ?  CMessage::Text(MSG_LIB_TEXT_NOVEMBER)  :
      month==12   ?  CMessage::Text(MSG_LIB_TEXT_DECEMBER)  :
      (string)month
     );
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Return timeframe description                                     |
//+------------------------------------------------------------------+
string TimeframeDescription(const ENUM_TIMEFRAMES timeframe)
  {
   return StringSubstr(EnumToString(timeframe),7);
  }
//+------------------------------------------------------------------+

Die Funktion, die einen Monatsnamen zurückgibt, erhält eine Monatsnummer, und ihre Textbeschreibung wird dementsprechend zurückgegeben.

Die Funktion, die den Namen eines Zeitrahmens zurückgibt, erhält einen Zeitrahmen. Als Nächstes wird die Textrepräsentation des Wertes einer Enumeration vom Zeitrahmen verwendet, um eine Teilzeichenkette von der Position 7 bis zum Ende der Zeichenkette abzurufen. Das erhaltene Ergebnis wird als Text zurückgegeben. Zum Beispiel wird der Wert von H1 aus der Textrepräsentation von PERIOD_H1 Stunde Zeitrahmen abgerufen.

Um die Klassen der Bar-Objekte zu speichern, erstellen wir einen neuen Ordner im Objektkatalog der Bibliothek \MQL5\Include\DoEasy\Objekte\Serien\. Dann erstellen wir die Datei Bar.mqh der Klasse CBar im neuen Ordner.

Betrachten wir nun die Auflistung des Klassenkörpers und die Implementierung seiner Methoden:

//+------------------------------------------------------------------+
//|                                                          Bar.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| bar class                                                        |
//+------------------------------------------------------------------+
class CBar : public CObject
  {
private:
   MqlDateTime       m_dt_struct;                                 // Date structure
   int               m_digits;                                    // Symbol's digits value
   string            m_period_description;                        // Timeframe string description
   long              m_long_prop[BAR_PROP_INTEGER_TOTAL];         // Integer properties
   double            m_double_prop[BAR_PROP_DOUBLE_TOTAL];        // Real properties
   string            m_string_prop[BAR_PROP_STRING_TOTAL];        // String properties

//--- Return the index of the array the bar's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_BAR_PROP_DOUBLE property)     const { return(int)property-BAR_PROP_INTEGER_TOTAL;                        }
   int               IndexProp(ENUM_BAR_PROP_STRING property)     const { return(int)property-BAR_PROP_INTEGER_TOTAL-BAR_PROP_DOUBLE_TOTAL;  }

//--- Return the bar type (bullish/bearish/zero)
   ENUM_BAR_BODY_TYPE BodyType(void)                              const;
//--- Calculate and return the size of (1) candle, (2) candle body,
//--- (3) upper, (4) lower candle wick,
//--- (5) candle body top and (6) bottom
   double            CandleSize(void)                             const { return(this.High()-this.Low());                                    }
   double            BodySize(void)                               const { return(this.BodyHigh()-this.BodyLow());                            }
   double            ShadowUpSize(void)                           const { return(this.High()-this.BodyHigh());                               }
   double            ShadowDownSize(void)                         const { return(this.BodyLow()-this.Low());                                 }
   double            BodyHigh(void)                               const { return ::fmax(this.Close(),this.Open());                           }
   double            BodyLow(void)                                const { return ::fmin(this.Close(),this.Open());                           }

//--- Return the (1) year and (2) month the bar belongs to, (3) week day,
//--- (4) bar serial number in a year, (5) day, (6) hour, (7) minute,
   int               TimeYear(void)                               const { return this.m_dt_struct.year;                                      }
   int               TimeMonth(void)                              const { return this.m_dt_struct.mon;                                       }
   int               TimeDayOfWeek(void)                          const { return this.m_dt_struct.day_of_week;                               }
   int               TimeDayOfYear(void)                          const { return this.m_dt_struct.day_of_year;                               }
   int               TimeDay(void)                                const { return this.m_dt_struct.day;                                       }
   int               TimeHour(void)                               const { return this.m_dt_struct.hour;                                      }
   int               TimeMinute(void)                             const { return this.m_dt_struct.min;                                       }

public:
//--- Set bar's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_BAR_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                              }
   void              SetProperty(ENUM_BAR_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;            }
   void              SetProperty(ENUM_BAR_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;            }
//--- Return (1) integer, (2) real and (3) string bar properties from the properties array
   long              GetProperty(ENUM_BAR_PROP_INTEGER property)  const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_BAR_PROP_DOUBLE property)   const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_BAR_PROP_STRING property)   const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of the bar supporting the property
   virtual bool      SupportProperty(ENUM_BAR_PROP_INTEGER property)    { return true; }
   virtual bool      SupportProperty(ENUM_BAR_PROP_DOUBLE property)     { return true; }
   virtual bool      SupportProperty(ENUM_BAR_PROP_STRING property)     { return true; }
//--- Return itself
   CBar             *GetObject(void)                                    { return &this;}
//--- Set (1) bar symbol, timeframe and index, (2) bar object parameters
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   void              SetProperties(const MqlRates &rates);

//--- Compare CBar objects by all possible properties (for sorting the lists by a specified bar object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CBar objects by all properties (to search for equal bar objects)
   bool              IsEqual(CBar* compared_bar) const;
//--- Constructors
                     CBar(){;}
                     CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
                     CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates);
                     
//+------------------------------------------------------------------+
//| Methods of a simplified access to the order object properties    |
//+------------------------------------------------------------------+
//--- Return the (1) type, (2) period, (3) spread, (4) tick, (5) exchange volume,
//--- (6) bar period start time, (7) year, (8) month the bar belongs to
//--- (9) week number since the year start, (10) week number since the month start
//--- (11) bar's day, (12) hour, (13) minute, (14) index
   ENUM_BAR_BODY_TYPE   TypeBody(void)                                  const { return (ENUM_BAR_BODY_TYPE)this.GetProperty(BAR_PROP_TYPE);  }
   ENUM_TIMEFRAMES   Period(void)                                       const { return (ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD);   }
   int               Spread(void)                                       const { return (int)this.GetProperty(BAR_PROP_SPREAD);               }
   long              VolumeTick(void)                                   const { return this.GetProperty(BAR_PROP_VOLUME_TICK);               }
   long              VolumeReal(void)                                   const { return this.GetProperty(BAR_PROP_VOLUME_REAL);               }
   datetime          Time(void)                                         const { return (datetime)this.GetProperty(BAR_PROP_TIME);            }
   long              Year(void)                                         const { return this.GetProperty(BAR_PROP_TIME_YEAR);                 }
   long              Month(void)                                        const { return this.GetProperty(BAR_PROP_TIME_MONTH);                }
   long              DayOfWeek(void)                                    const { return this.GetProperty(BAR_PROP_TIME_DAY_OF_WEEK);          }
   long              DayOfYear(void)                                    const { return this.GetProperty(BAR_PROP_TIME_DAY_OF_YEAR);          }
   long              Day(void)                                          const { return this.GetProperty(BAR_PROP_TIME_DAY);                  }
   long              Hour(void)                                         const { return this.GetProperty(BAR_PROP_TIME_HOUR);                 }
   long              Minute(void)                                       const { return this.GetProperty(BAR_PROP_TIME_MINUTE);               }
   long              Index(void)                                        const { return this.GetProperty(BAR_PROP_INDEX);                     }

//--- Return bar's (1) Open, (2) High, (3) Low, (4) Close price,
//--- size of the (5) candle, (6) body, (7) candle top, (8) bottom,
//--- size of the (9) candle upper, (10) lower wick
   double            Open(void)                                         const { return this.GetProperty(BAR_PROP_OPEN);                      }
   double            High(void)                                         const { return this.GetProperty(BAR_PROP_HIGH);                      }
   double            Low(void)                                          const { return this.GetProperty(BAR_PROP_LOW);                       }
   double            Close(void)                                        const { return this.GetProperty(BAR_PROP_CLOSE);                     }
   double            Size(void)                                         const { return this.GetProperty(BAR_PROP_CANDLE_SIZE);               }
   double            SizeBody(void)                                     const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_BODY);          }
   double            TopBody(void)                                      const { return this.GetProperty(BAR_PROP_CANDLE_BODY_TOP);           }
   double            BottomBody(void)                                   const { return this.GetProperty(BAR_PROP_CANDLE_BODY_BOTTOM);        }
   double            SizeShadowUp(void)                                 const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP);     }
   double            SizeShadowDown(void)                               const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN);   }
   
//--- Return bar symbol
   string            Symbol(void)                                       const { return this.GetProperty(BAR_PROP_SYMBOL);                    }
  
//+------------------------------------------------------------------+
//| Descriptions of bar object properties                            |
//+------------------------------------------------------------------+
//--- Get description of a bar's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_BAR_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_BAR_PROP_STRING property);

//--- Return the bar type description
   string            BodyTypeDescription(void)  const;
//--- Send description of bar properties to the journal (full_prop=true - all properties, false - only supported ones)
   void              Print(const bool full_prop=false);
//--- Display a short bar description in the journal
   virtual void      PrintShort(void);
//--- Return the bar object short name
   virtual string    Header(void);
//---
  };
//+------------------------------------------------------------------+

Betrachten wir die Klasseninhalte, um die Erinnerung an das vorher beschriebene Material aufzufrischen.

Die Eigenschaften im 'private' Abschnitt der Klasse:

Drei Arrays, die die entsprechenden Eigenschaften der Bar-Objekte speichern — ganzzahlige, reelle und Zeichenketten-Objekte.
Die Methoden zur Berechnung des wahren Index der Objekteigenschaft im entsprechenden Array
.
Die Methoden zur Berechnung und Rückgabe zusätzlicher Eigenschaften der Bar-Objekte.

Der 'public' Teil der Klasseneigenschaften:

Die Methoden, die den übergebenen Objekt-Eigenschaftswert in die Arrays der Integer-, Real- und String-Eigenschaften schreiben.
Die Methoden, die den Wert einer angeforderten ganzzahlige, reelle oder Zeichenketten-Eigenschaft aus den Arrays zurückgeben.
Die virtuellen Methoden, die das Flag der Unterstützung einer Eigenschaft durch das Objekt für jede der Eigenschaften zurückgeben. Die Methoden sollen in Nachfolgeobjekten des Bar-Objekts implementiert werden und sollten false zurückgeben, falls das Nachfolgeobjekt die angegebene Eigenschaft nicht unterstützt. Im Bar-Objekt werden alle Eigenschaften unterstützt und die Methoden geben true zurück.

Wir diskutierten die gesamte Struktur der Bibliotheksobjekte im ersten Artikel. Hier werden wir kurz auf die Implementierung der übrigen Klassenmethoden eingehen.

Die Klasse verfügt über drei Konstruktoren:

1. Standardmäßig wird der Konstruktor ohne Parameter für eine einfache Deklaration einer Objektklasse mit anschließender Einstellung aller notwendigen Parameter für das erzeugte Objekt verwendet.

2. Der erste parametrische Konstruktor erhält drei Parameter — Symbol, Zeitrahmen und Barindex. Basierend auf diesen drei Parametern erhält er alle Eigenschaften eines einzelnen Taktobjekts aus der Zeitreihe unter Verwendung der ersten Form der Funktion CopyRates():

//+------------------------------------------------------------------+
//| Constructor 1                                                    |
//+------------------------------------------------------------------+
CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   MqlRates rates_array[1];
   this.SetSymbolPeriod(symbol,timeframe,index);
   ::ResetLastError();
//--- If failed to write bar data to the array by index or set the time to the time structure
//--- display an error message, create and fill the structure with zeros, and write it to the rates_array array
   if(::CopyRates(symbol,timeframe,index,1,rates_array)<1 || !::TimeToStruct(rates_array[0].time,this.m_dt_struct))
     {
      int err_code=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code));
      MqlRates err={0};
      rates_array[0]=err;
     }
//--- Set the bar properties
   this.SetProperties(rates_array[0]);
  }
//+------------------------------------------------------------------+

Dieser Konstruktor wird zur einmaligen Datenerfassung aus der Zeitreihe unmittelbar bei der Erstellung des Bar-Objekts verwendet.

3. Der zweite parametrische Konstruktor wird verwendet, um ein Bar-Objekt aus dem bereits vorbereiteten Array der Struktur MqlRates zu erzeugen.
Mit anderen Worten, dies bedeutet, dass das Array der MqlRates-Struktur in einer Schleife durchlaufen und ein Objekt durch den Array-Index erzeugt wird:

//+------------------------------------------------------------------+
//| Constructor 2                                                    |
//+------------------------------------------------------------------+
CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates)
  {
   this.SetSymbolPeriod(symbol,timeframe,index);
   ::ResetLastError();
//--- If failed to set time to the time structure, display the error message,
//--- create and fill the structure with zeros, set the bar properties from this structure and exit
   if(!::TimeToStruct(rates.time,this.m_dt_struct))
     {
      int err_code=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code));
      MqlRates err={0};
      this.SetProperties(err);
      return;
     }
//--- Set the bar properties
   this.SetProperties(rates);
  }
//+------------------------------------------------------------------+

Hier wird neben dem Symbol, dem Zeitrahmen und dem Index auch der Link zur MqlRates-Struktur an den Konstruktor übergeben. Das Bar-Objekt wird auf der Grundlage dieser Daten erstellt.

Die virtuelle Methode Compare() ist für den Vergleich zweier Objekte anhand der angegebenen Eigenschaft gedacht. Sie ist in der CObject Basisobjektklasse der Standardbibliothek definiert und sollte null zurückgeben, wenn die Werte gleich sind, und 1/-1, wenn einer der verglichenen Werte höher/niedriger ist. Zum Suchen und Sortieren wird die Methode Search() der Standardbibliothek verwendet. Die Methode sollte in den abgeleiteten Klassen neu definiert werden:

//+------------------------------------------------------------------+
//| Compare CBar objects by all possible properties                  |
//+------------------------------------------------------------------+
int CBar::Compare(const CObject *node,const int mode=0) const
  {
   const CBar *bar_compared=node;
//--- compare integer properties of two bars
   if(mode<BAR_PROP_INTEGER_TOTAL)
     {
      long value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_BAR_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two bars
   else if(mode<BAR_PROP_DOUBLE_TOTAL+BAR_PROP_INTEGER_TOTAL)
     {
      double value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_BAR_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two bars
   else if(mode<BAR_PROP_DOUBLE_TOTAL+BAR_PROP_INTEGER_TOTAL+BAR_PROP_STRING_TOTAL)
     {
      string value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_BAR_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Die Methode zur Definition von zwei ähnlichen Bar-Objekte wird verwendet, um zwei Objektbalken zu vergleichen. Sie gibt true nur dann zurück, wenn alle Felder der beiden verglichenen Objekte identisch sind:

//+------------------------------------------------------------------+
//| Compare CBar objects by all properties                           |
//+------------------------------------------------------------------+
bool CBar::IsEqual(CBar *compared_bar) const
  {
   int beg=0, end=BAR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BAR_PROP_INTEGER prop=(ENUM_BAR_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; 
     }
   beg=end; end+=BAR_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BAR_PROP_DOUBLE prop=(ENUM_BAR_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; 
     }
   beg=end; end+=BAR_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BAR_PROP_STRING prop=(ENUM_BAR_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methode zur Platzierung eines Symbol-, Zeitrahmen- und Bar-Objekt-Indexes in der Zeitreihe:

//+------------------------------------------------------------------+
//| Set bar symbol, timeframe and index                              |
//+------------------------------------------------------------------+
void CBar::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   this.SetProperty(BAR_PROP_INDEX,index);
   this.SetProperty(BAR_PROP_SYMBOL,symbol);
   this.SetProperty(BAR_PROP_PERIOD,timeframe);
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   this.m_period_description=TimeframeDescription(timeframe);
  }
//+------------------------------------------------------------------+

Abgesehen von der Einstellung der drei genannten Eigenschaften weist die Methode die Anzahl der Nachkommastellen eines Symbolpreises der Variablen m_Ziffern zu und die zeitliche Textbeschreibung der Variablen m_Periodenbeschreibung . Es genügt, sie einmal bei der Erstellung des Bar-Objekts anzugeben.

Die Methode zum Setzen aller Parameter des Bar-Objekts schreibt einfach die Werte aus der der Methode übergebenen Struktur MqlRates in die Objekteigenschaften und berechnet die Parameter weiterer Objekteigenschaften mit den entsprechenden Methoden:

//+------------------------------------------------------------------+
//| Set bar object parameters                                        |
//+------------------------------------------------------------------+
void CBar::SetProperties(const MqlRates &rates)
  {
   this.SetProperty(BAR_PROP_SPREAD,rates.spread);
   this.SetProperty(BAR_PROP_VOLUME_TICK,rates.tick_volume);
   this.SetProperty(BAR_PROP_VOLUME_REAL,rates.real_volume);
   this.SetProperty(BAR_PROP_TIME,rates.time);
   this.SetProperty(BAR_PROP_TIME_YEAR,this.TimeYear());
   this.SetProperty(BAR_PROP_TIME_MONTH,this.TimeMonth());
   this.SetProperty(BAR_PROP_TIME_DAY_OF_YEAR,this.TimeDayOfYear());
   this.SetProperty(BAR_PROP_TIME_DAY_OF_WEEK,this.TimeDayOfWeek());
   this.SetProperty(BAR_PROP_TIME_DAY,this.TimeDay());
   this.SetProperty(BAR_PROP_TIME_HOUR,this.TimeHour());
   this.SetProperty(BAR_PROP_TIME_MINUTE,this.TimeMinute());
//---
   this.SetProperty(BAR_PROP_OPEN,rates.open);
   this.SetProperty(BAR_PROP_HIGH,rates.high);
   this.SetProperty(BAR_PROP_LOW,rates.low);
   this.SetProperty(BAR_PROP_CLOSE,rates.close);
   this.SetProperty(BAR_PROP_CANDLE_SIZE,this.CandleSize());
   this.SetProperty(BAR_PROP_CANDLE_SIZE_BODY,this.BodySize());
   this.SetProperty(BAR_PROP_CANDLE_BODY_TOP,this.BodyHigh());
   this.SetProperty(BAR_PROP_CANDLE_BODY_BOTTOM,this.BodyLow());
   this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP,this.ShadowUpSize());
   this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,this.ShadowDownSize());
//---
   this.SetProperty(BAR_PROP_TYPE,this.BodyType());
  }
//+------------------------------------------------------------------+

Die Methode, die Beschreibungen aller Eigenschaften eines Bar-Objekts anzeigt:

//+------------------------------------------------------------------+
//| Display bar properties in the journal                            |
//+------------------------------------------------------------------+
void CBar::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
   int beg=0, end=BAR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BAR_PROP_INTEGER prop=(ENUM_BAR_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=BAR_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BAR_PROP_DOUBLE prop=(ENUM_BAR_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=BAR_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BAR_PROP_STRING prop=(ENUM_BAR_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
  }
//+------------------------------------------------------------------+

Die Beschreibung jeder nächsten Eigenschaft wird durch Objekteigenschaften-Arrays in drei Schleifen angezeigt. Wenn die Eigenschaft nicht unterstützt wird, wird sie nicht im Journal angezeigt, wenn die Eingabe der Methode full_prop false (standardmäßig) ist.

Die Methode, die eine kurze Beschreibung des Bar-Objekts im Journal anzeigt:

//+------------------------------------------------------------------+
//| Display a short bar description in the journal                   |
//+------------------------------------------------------------------+
void CBar::PrintShort(void)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   string params=
     (
      ::TimeToString(this.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+", "+
      "O: "+::DoubleToString(this.Open(),dg)+", "+
      "H: "+::DoubleToString(this.High(),dg)+", "+
      "L: "+::DoubleToString(this.Low(),dg)+", "+
      "C: "+::DoubleToString(this.Close(),dg)+", "+
      "V: "+(string)this.VolumeTick()+", "+
      (this.VolumeReal()>0 ? "R: "+(string)this.VolumeReal()+", " : "")+
      this.BodyTypeDescription()
     );
   ::Print(this.Header(),": ",params);
  }
//+------------------------------------------------------------------+

Die Methode zeigt die Beschreibung einer Bar in folgendem Format

Bar "SYMBOL" H4[INDEX]: YYYY.MM.TD HH:MM:SS, O: X.XXXXX, H: X.XXXXX, L: X.XXXXX, C: X.XXXXX, V: XXXX, BAR_TYPE

Zum Beispiel:

Bar "EURUSD" H4[6]: 2020.02.06 20:00:00, O: 1.09749, H: 1.09828, L: 1.09706, C: 1.09827, V: 3323, Bullish bar

Die Methode, die einen kurzen Bar-Namen anzeigt:

//+------------------------------------------------------------------+
//| Return the bar object short name                                 |
//+------------------------------------------------------------------+
string CBar::Header(void)
  {
   return
     (
      CMessage::Text(MSG_LIB_TEXT_BAR)+" \""+this.GetProperty(BAR_PROP_SYMBOL)+"\" "+
      TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD))+"["+(string)this.GetProperty(BAR_PROP_INDEX)+"]"
     );
  }
//+------------------------------------------------------------------+

Zeigen wir den Bar-Namen in folgendem Format an

Bar "SYMBOL" H4[INDEX]

Zum Beispiel:

Bar "EURUSD" H4[6]

Die Methode, die die Beschreibung der Ganzzahleigenschaft des Bar-Objekts:

//+------------------------------------------------------------------+
//| Return the description of the bar integer property               |
//+------------------------------------------------------------------+
string CBar::GetPropertyDescription(ENUM_BAR_PROP_INTEGER property)
  {
   return
     (
      property==BAR_PROP_INDEX               ?  CMessage::Text(MSG_LIB_TEXT_BAR_INDEX)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BAR_PROP_TYPE                ?  CMessage::Text(MSG_ORD_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.BodyTypeDescription()
         )  :
      property==BAR_PROP_PERIOD              ?  CMessage::Text(MSG_LIB_TEXT_BAR_PERIOD)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.m_period_description
         )  :
      property==BAR_PROP_SPREAD              ?  CMessage::Text(MSG_LIB_TEXT_BAR_SPREAD)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BAR_PROP_VOLUME_TICK         ?  CMessage::Text(MSG_LIB_TEXT_BAR_VOLUME_TICK)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BAR_PROP_VOLUME_REAL         ?  CMessage::Text(MSG_LIB_TEXT_BAR_VOLUME_REAL)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BAR_PROP_TIME                ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==BAR_PROP_TIME_YEAR           ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_YEAR)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.Year()
         )  :
      property==BAR_PROP_TIME_MONTH          ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_MONTH)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+MonthDescription((int)this.Month())
         )  :
      property==BAR_PROP_TIME_DAY_OF_YEAR    ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)::IntegerToString(this.DayOfYear(),3,'0')
         )  :
      property==BAR_PROP_TIME_DAY_OF_WEEK    ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+DayOfWeekDescription((ENUM_DAY_OF_WEEK)this.DayOfWeek())
         )  :
      property==BAR_PROP_TIME_DAY ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)::IntegerToString(this.Day(),2,'0')
         )  :
      property==BAR_PROP_TIME_HOUR           ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_HOUR)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)::IntegerToString(this.Hour(),2,'0')
         )  :
      property==BAR_PROP_TIME_MINUTE         ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME_MINUTE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)::IntegerToString(this.Minute(),2,'0')
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Die Methode übergibt die Integer-Eigenschaft. Abhängig von ihrem Wert wird ihre in der Datei Datas.mqh gesetzte Textbeschreibung zurückgegeben.

Die Methoden, die Beschreibungen der real- und string-Eigenschaften des Bar-Objekts zurückgeben, sind ähnlich wie die Methode, die die Beschreibung der integer-Eigenschaft des Bar-Objekts zurückgibt:

//+------------------------------------------------------------------+
//| Return the description of the bar's real property                |
//+------------------------------------------------------------------+
string CBar::GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      property==BAR_PROP_OPEN                ?  CMessage::Text(MSG_ORD_PRICE_OPEN)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_HIGH                ?  CMessage::Text(MSG_LIB_TEXT_BAR_HIGH)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_LOW                 ?  CMessage::Text(MSG_LIB_TEXT_BAR_LOW)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CLOSE               ?  CMessage::Text(MSG_ORD_PRICE_CLOSE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CANDLE_SIZE         ?  CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CANDLE_SIZE_BODY    ?  CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CANDLE_SIZE_SHADOW_UP  ?  CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CANDLE_SIZE_SHADOW_DOWN   ?  CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CANDLE_BODY_TOP     ?  CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==BAR_PROP_CANDLE_BODY_BOTTOM  ?  CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return the description of the bar string property                |
//+------------------------------------------------------------------+
string CBar::GetPropertyDescription(ENUM_BAR_PROP_STRING property)
  {
   return(property==BAR_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : "");
  }
//+------------------------------------------------------------------+

Die Methode für die Rückgabe des Bartyps:

//+------------------------------------------------------------------+
//| Return the bar type (bullish/bearish/zero)                       |
//+------------------------------------------------------------------+
ENUM_BAR_BODY_TYPE CBar::BodyType(void) const
  {
   return
     (
      this.Close()>this.Open() ? BAR_BODY_TYPE_BULLISH : 
      this.Close()<this.Open() ? BAR_BODY_TYPE_BEARISH : 
      (this.ShadowUpSize()+this.ShadowDownSize()==0 ? BAR_BODY_TYPE_NULL : BAR_BODY_TYPE_CANDLE_ZERO_BODY)
     );
  }
//+------------------------------------------------------------------+

Hier ist alles einfach: Wenn der Schlusskurs der Bar den Eröffnungskurs übersteigt, ist dies eine Aufwärtsbar, liegt der Schlusskurs der Bar unter dem Eröffnungskurs (bullish), ist dies eine Abwärtsbar (bearish). Wenn beide Kerzendochte gleich Null sind, handelt es sich um eine Bar mit einem Nullkörper , andernfalls ist dies eine Kerze mit einem Nullkörper.

Die Methode, die die Beschreibung einer Bar zurückgibt:

//+------------------------------------------------------------------+
//| Return the bar type description                                  |
//+------------------------------------------------------------------+
string CBar::BodyTypeDescription(void) const
  {
   return
     (
      this.BodyType()==BAR_BODY_TYPE_BULLISH          ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_BULLISH)          : 
      this.BodyType()==BAR_BODY_TYPE_BEARISH          ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_BEARISH)          : 
      this.BodyType()==BAR_BODY_TYPE_CANDLE_ZERO_BODY ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY) :
      CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_NULL)
     );
  }
//+------------------------------------------------------------------+

Abhängig vom Typ der Bar gibt die Methode ihre in der Datei Datas.mqh festgelegte Textbeschreibung zurück.

Die Objektklasse Bar ist bereit. Jetzt können wir ein Bar-Objekt für jeden erforderlichen Takt einer erforderlichen Zeitreihe erstellen. Das Objekt allein kann uns jedoch keinen signifikanten Vorteil gegenüber dem üblichen Empfang von Daten über die Anforderung eines Zeitreihen-Balkens mittels CopyRates() verschaffen.

Um die Zeitreihendaten so verwalten zu können, wie wir wollen, müssen wir die Liste der Bar-Objekte erstellen, die der benötigten Zeitreihe in der erforderlichen Menge entsprechen. Dann können wir bereits die Listendaten analysieren und nach allen für die Analyse erforderlichen Daten suchen.

Das bedeutet, dass wir eine Zeitserienliste erstellen müssen, um Bar-Objekte zu speichern.
Außerdem müssen wir die Eröffnung einer neuen Bar erkennen, um der Liste ein weiteres Bar-Objekt hinzuzufügen, und immer ein Werkzeug zur Verfügung haben, das die Eröffnung einer neuen Bar unabhängig von der Anzahl der Symbole und dem Zeitrahmen anzeigt.

Bevor wir eine Liste mit gespeicherten Bar-Objekten erstellen, schreiben wir die Klasse "New bar", da das Klassenobjekt eine der Eigenschaften der Barliste ist.

Ein neues Bar-Objekt

Um das Ereignis der Eröffnung einer neuen Bar zu erkennen, vergleichen wir den Zeitpunkt der Eröffnung der aktuellen Bar mit dem Zeitpunkt der vorherigen Eröffnung. Wenn sie nicht übereinstimmen, wurde eine neue Bar eröffnet. In diesem Fall müssen wir die neue Eröffnungszeit als die vorherige für einen späteren Vergleich speichern:

NewBar = false;
if(PrevTime != Time)
  {
   NewBar = true;
   PrevTime = Time;
  }

In diesem Fall wird das neue Barereignis einmal geöffnet, während alle nachfolgenden Befehle bereits mit der neuen Bar ausgeführt werden.

Manchmal verlangen wir jedoch, dass bestimmte Befehle genau nach dem "Neuen Takt" ausgeführt werden. Das Ereignis sollte so lange relevant bleiben, bis alle Befehle ausgeführt sind. Um dies zu erreichen, müssen wir alle Befehle ausführen, die bis zum Erscheinen eines neuen Balkens abgeschlossen sein sollten, bevor wir dem vorherigen einen neuen Wert zuweisen: Das Ereignis sollte relevant bleiben, bis alle Befehle abgeschlossen sind. Um dies zu erreichen, müssen wir alle Befehle ausführen, die bis zum Erscheinen eines neuen Balkens abgeschlossen sein sollten, bevor wir dem vorherigen einen neuen Wert zuweisen:

NewBar = false;
if(PrevTime != Time)
  {
   NewBar = true;
   // ... commands to be
   // ... executed
   // ... when a new bar appears
   PrevTime = Time;
  }

Somit kann die erste Option als unabhängige Funktion ausgeführt werden, die das neue Flag der Eröffnung des Balkens zurückgibt. Die zweite Option sollte in der vorliegenden Version Teil des OnTick()-Handlers sein, vorzugsweise ganz am Anfang, so dass alle Befehle, die zum Zeitpunkt des Erscheinens der neuen Bar ausgeführt werden sollen, zuerst ausgeführt werden. Ihnen folgen dann alle Befehle, die zu jeder Zeit ausgeführt werden.

Im einfachsten Fall ist dies ausreichend, um das Öffnen einer neuen Bar zu steuern.
Dies reicht jedoch für die Bedürfnisse der Bibliothek nicht aus — wir benötigen eine separate neue Definition der Bars für jedes Symbol und jeden Zeitrahmen in zwei beschriebenen Optionen:

  1. automatische Steuerung und Zeitersparnis für ein bestimmtes Symbol und einen bestimmten Zeitrahmen (Rückgabe des Ereignis-Flags von "New bar" für jedes Symbol und jeden Zeitrahmen separat),
  2. Zeitmanagement für ein bestimmtes Symbol und einen bestimmten Zeitrahmen mit manueller Steuerung der Speicherung seines neuen Wertes
    (Definition des Ereignisses "New bar" und die Möglichkeit, für jedes Symbol und jeden Zeitrahmen separat festzulegen, wann die neue Zeit für die spätere Kontrolle des nächsten neuen Balkens gespeichert werden soll).

Es wird nicht empfohlen, auf die Zeitreihen-Aktualisierungsfunktionen zuzugreifen, wenn Daten über das aktuelle Symbol und den aktuellen Zeitrahmen angefordert werden. Eine solche Anforderung führt wahrscheinlich zu einem Konflikt, da historische Daten im gleichen Thread aktualisiert werden, in dem der Indikator in arbeitet. Stellen Sie daher sicher, dass Sie den Erhalt der Zeitreihendaten des aktuellen Symbols oder Zeitrahmens prüfen. Wenn ja, verwenden Sie für eine solche Anfrage eine andere Methode, anstatt SeriesInfoInteger() und andere Funktionen, die serielle Daten zurückgeben, für den Zugriff, der mit dem Laden der Historie beginnt.
Eine solche Methode existiert, und sie ist recht einfach:

Die Parameter von OnCalculate() der Indikatoren verfügen bereits über die notwendigen Variablen:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])

  • rates_total — Anzahl der in der Historie verfügbaren Zeitreihen (ähnlich wie die Funktion Bars() ohne Parameter),
  • prev_calculated — Menge der bereits berechneten Daten beim letzten Aufruf,
  • time[] — Zeitreihenarray mit Daten zur Barzeit.

Die Indikatoren ermöglichen es Ihnen, Veränderungen der historischen Daten mit Hilfe einer einfachen Berechnung zu verfolgen:

  • Wenn (rates_total — prev_calculated) 1 überschreitet, bedeutet dies, dass die Historie geladen wird und der Indikator neu gezeichnet werden sollte,
  • wenn (rates_total — prev_calculated) gleich 1 ist, wird ein neuer Balken auf dem Symbol für den aktuellen Zeitrahmen geöffnet.
  • normalerweise ist der Ausdruck (rates_total — prev_calculated) bei jedem neuen Tick 0.

In den Indikatoren wird die Taktzeit aus dem Array time[] an die Klassenmethoden für das aktuelle Symbol und den Zeitrahmen übergeben, um einen neuen Takt zu definieren und seine Zeit anzugeben. In anderen Fällen erhalten wir die Zeit innerhalb der Klassenmethoden.

Erstellen wir in \MQL5\Include\DoEasy\Objects\Series\ die neue Datei NewBarObj.mqh für die Klasse CNewBarObj:

//+------------------------------------------------------------------+
//|                                                    NewBarObj.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| "New bar" object class                                           |
//+------------------------------------------------------------------+
class CNewBarObj
  {
private:
   string            m_symbol;                                    // Symbol
   ENUM_TIMEFRAMES   m_timeframe;                                 // Timeframe
   datetime          m_new_bar_time;                              // New bar time for auto time management
   datetime          m_prev_time;                                 // Previous time for auto time management
   datetime          m_new_bar_time_manual;                       // New bar time for manual time management
   datetime          m_prev_time_manual;                          // Previous time for manual time management
//--- Return the current bar data
   datetime          GetLastBarDate(const datetime time);
public:
//--- Set (1) symbol and (2) timeframe
   void              SetSymbol(const string symbol)               { this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);                     }
   void              SetPeriod(const ENUM_TIMEFRAMES timeframe)   { this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); }
//--- Save the new bar time during the manual time management
   void              SaveNewBarTime(const datetime time)          { this.m_prev_time_manual=this.GetLastBarDate(time);                                      }
//--- Return (1) symbol and (2) timeframe
   string            Symbol(void)                           const { return this.m_symbol;       }
   ENUM_TIMEFRAMES   Period(void)                           const { return this.m_timeframe;    }
//--- Return the (1) new bar time
   datetime          TimeNewBar(void)                       const { return this.m_new_bar_time; }
//--- Return the new bar opening flag during the time (1) auto, (2) manual management
   bool              IsNewBar(const datetime time);
   bool              IsNewBarManual(const datetime time);
//--- Constructors
                     CNewBarObj(void) : m_symbol(::Symbol()),
                                        m_timeframe((ENUM_TIMEFRAMES)::Period()),
                                        m_prev_time(0),m_new_bar_time(0),
                                        m_prev_time_manual(0),m_new_bar_time_manual(0) {}
                     CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe);
  };
//+------------------------------------------------------------------+

Ich denke, hier ist alles ganz einfach:
Die Variablen der Klassenmitglieder werden im 'private' Abschnitt zur Speicherung eines Symbols und eines Zeitrahmens deklariert, für den das Objekt das Ereignis "New bar" definieren soll.
Die Variablen für die Speicherung der neuen Eröffnungszeit der Bar und der vorherigen Öffnungszeit sind getrennt für ein automatisches und manuelles Zeitmanagement (wir hatten bereits besprochen, warum wir dies benötigen).
Die Methode GetLastBarDate() gibt die neue Eröffnungszeit der Bar zurück und ist nachfolgend zu betrachten.
Die Methode SaveNewBarTime() zum Speichern der neuen Bar-Zeit, da die vorherige Zeit für die manuelle Zeitverwaltung deklariert und im 'public' Klassenabschnitt implementiert ist. Das ermöglicht es Bibliotheksnutzern, die neue Bar-Zeit zu speichern, nachdem alle erforderlichen Aktionen darauf abgeschlossen sind.
Die übrigen Methoden sind selbsterklärend, und es hat keinen Sinn, hier auf sie einzugehen.

Die Klasse verfügt über zwei Konstruktoren. Der erste Konstruktor hat keine Parameter. Das aktuelle Symbol und der Zeitrahmen werden in seiner Initialisierungsliste festgelegt, während alle neuen Bar-Zeitwerte und die vorherige Bar-Eröffnungszeit zurückgesetzt werden. Nach der Erstellung eines solchen Objekts müssen wir die Methoden zum Setzen des erforderlichen Symbols und Zeitrahmens für das erstellte Klassenobjekt unabhängig voneinander aufrufen.
Der zweite Konstruktor ist parametrisch. Das erforderliche Symbol und der Zeitrahmen werden sofort an ihn gesendet:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CNewBarObj::CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe) : m_symbol(symbol),m_timeframe(timeframe)
  {
   this.m_prev_time=this.m_prev_time_manual=this.m_new_bar_time=this.m_new_bar_time_manual=0;
  }
//+------------------------------------------------------------------+

Ein Symbol und ein Zeitrahmen, die den erzeugten Klassenobjektparametern übergeben werden, werden in seiner Initialisierungsliste festgelegt. Anschließend wird allen Zeitvariablen im Klassenkörper Null zugewiesen.

Die Methode, die das Flag einer neuen Bareröffnung während der automatischen Zeitverwaltung zurückgibt:

//+------------------------------------------------------------------+
//| Return new bar opening flag                                      |
//+------------------------------------------------------------------+
bool CNewBarObj::IsNewBar(const datetime time)
  {
//--- Get the current bar time
   datetime tm=this.GetLastBarDate(time);
//--- If the previous and current time are equal to zero, this is the first launch
   if(this.m_prev_time==0 && this.m_new_bar_time==0)
     {
      //--- set the new bar opening time,
      //--- set the previous bar time as the current one and return 'false'
      this.m_new_bar_time=this.m_prev_time=tm;
      return false;
     }
//--- If the previous time is not equal to the current bar open time, this is a new bar
   if(this.m_prev_time!=tm)
     {
      //--- set the new bar opening time,
      //--- set the previous time as the current one and return 'true'
      this.m_new_bar_time=this.m_prev_time=tm;
      return true;
     }
//--- in other cases, return 'false'
   return false;
  }
//+------------------------------------------------------------------+

Die Methode gibt true jedes Mal zurück, wenn eine neue Bar auf einem für das Objekt festgelegten Symbol oder Zeitrahmen eröffnet wird.

Die Methode gibt das Flag für das Öffnen einer neuen Bar während der manuellen Zeitverwaltung zurück:

//+------------------------------------------------------------------+
//| Return the new bar opening flag during the manual management     |
//+------------------------------------------------------------------+
bool CNewBarObj::IsNewBarManual(const datetime time)
  {
//--- Get the current bar time
   datetime tm=this.GetLastBarDate(time);
//--- If the previous and current time are equal to zero, this is the first launch
   if(this.m_prev_time_manual==0 && this.m_new_bar_time_manual==0)
     {
      //--- set the new bar opening time,
      //--- set the previous bar time as the current one and return 'false'
      this.m_new_bar_time_manual=this.m_prev_time_manual=tm;
      return false;
     }
//--- If the previous time is not equal to the current bar open time, this is a new bar
   if(this.m_prev_time_manual!=tm)
     {
      //--- set the new bar opening time and return 'true'
      //--- Save the previous time as the current one from the program using the SaveNewBarTime() method
      //--- Till the previous time is forcibly set as the current one from the program,
      //--- the method returns the new bar flag allowing the completion of all the necessary actions on the new bar.
      this.m_new_bar_time=tm;
      return true;
     }
//--- in other cases, return 'false'
   return false;
  }
//+------------------------------------------------------------------+

Im Gegensatz zur automatischen Zeitverwaltung weist die Methode die aktuelle neue Bar-Eröffnungszeit nicht der Variablen zu, die die vorherige Zeit der Bar speichert. Dadurch ist es möglich, das Flag für die Eröffnung einer neuen Bar jedes Mal auf einem neuen Tick zu senden, nachdem das Ereignis "New bar" erkannt wurde, bis der Nutzer entscheidet, dass alle bei der Eröffnung eines neuen Taktes auszuführenden Aktionen abgeschlossen sind und es notwendig ist, die Eröffnungszeit der neuen Bar als die vorherige zu speichern.
Die Zeit einer neuen Bar wird an die Eingaben beider Methoden weitergegeben. Die Methode GetLastBarDate() wird dann verwendet, um die zu verwendende Zeit zu definieren:

//+------------------------------------------------------------------+
//| Return the current bar time                                      |
//+------------------------------------------------------------------+
datetime CNewBarObj::GetLastBarDate(const datetime time)
  {
   return
     (
      ::MQLInfoInteger(MQL_PROGRAM_TYPE)==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period() ? time :
      (datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE)
     );
  }
//+------------------------------------------------------------------+

Wenn es sich um einen Indikator handelt, stimmen sowohl das Symbol als auch der Zeitrahmen des Objekts "New bar" mit dem aktuellen Symbol und Zeitrahmen überein, die an die Methode übergebene Zeit wird zurückgegeben (bei Indikatoren ist die Zeit in den OnCalculate()-Parametern vorhanden, und zwar im Array time[] — dies ist das Array, von dem aus die Zeit an die Methoden übergeben werden soll, die einen neuen Takt definieren), Andererseits empfangen Sie die Zeit des letzten Taktes mit SeriesInfoInteger() — in diesem Fall kann statt der Zeit ein beliebiger Wert übergeben werden.

Die Objektklasse "New bar" ist bereit für unsere aktuellen Bedürfnisse. Jetzt ist es an der Zeit, die Liste der Barobjekte zu erstellen.

Die Liste der Bar-Objekte ist im Wesentlichen ein Segment von historischen Zeitreihendaten, die Teil von MqlRates sind. Warum brauchen wir eine separate Liste?
Zunächst erhalten wir die schnelle Sortier-, Such- und Vergleichsfunktion. Zweitens liefern die Bar-Objekte, deren angegebene Anzahl in der Liste gespeichert ist, neben den Strukturfeldern MqlRates weitere Felder mit Werten, die in Zukunft die Suche nach verschiedenen Kerzenformationen vereinfachen sollen.

Die Liste der Bar-Objekte, Suchen und Sortieren

Für die Liste der Zeitreihen (Liste der Bar-Objekte) verwenden wir die Klasse der Standardbibliothek der dynamischen Arrays von Zeigern auf Instanzen der Klasse CObject und deren Nachkommen. Im aktuellen Artikel werde ich das einzige Listenobjekt von Bar-Objekten implementieren, um Bars nur einer Zeitreihe nach Symbol und Zeitrahmen zu speichern. In den zukünftigen Artikeln werde ich die Liste als Grundlage für die Sammlung von Zeitreihen nach Zeitrahmen für jedes einzelne im Benutzerprogramm verwendete Symbol verwenden. Auf diese Weise werden wir mehrere gleichartige Sammlungen von Zeitreihenlisten haben, in denen es möglich sein wird, schnell nach den notwendigen Informationen für die Analyse und den Vergleich mit anderen Listen von Zeitreihensammlungen zu suchen, die den Bibliotheksbenutzern zur Verfügung stehen.

Jede Liste mit Bar-Objekten soll eine nutzerdefinierte Anzahl von Bar-Objekten haben (History-Tiefe). Standardmäßig soll die Historietiefe aller Listen eine Anzahl von 1000 Bars haben. Vor ihrer Erstellung sollte jede Liste die Synchronisation der Daten mit dem Handelsserver in Betracht ziehen. Jede Liste jeder Zeitreihe soll einen Wert haben, der die Anzahl der verfügbaren historischen Bars angibt. Dieser Wert wird von der Funktion Bars() ohne Parameter zurückgegeben. Wenn sie Null zurückgibt, ist die Historie noch nicht synchronisiert. Wir werden mehrere Versuche mit kleinen Unterbrechungen zwischen ihnen machen, während wir auf die Datensynchronisation mit dem Server warten.

In der Datei Defines.mqh erstellen wir die Makro-Substitution, die die Tiefe der angewandten Historie definiert, die Anzahl der Pausen-Millisekunden zwischen den Versuchen, die Historie mit dem Server zu synchronisieren und die Anzahl der Versuche, das Synchronisationsereignis zu erhalten:

//--- Pending request type IDs
#define PENDING_REQUEST_ID_TYPE_ERR    (1)                        // Type of a pending request created based on the server return code
#define PENDING_REQUEST_ID_TYPE_REQ    (2)                        // Type of a pending request created by request
//--- Timeseries parameters
#define SERIES_DEFAULT_BARS_COUNT      (1000)                     // Required default amount of timeseries data
#define PAUSE_FOR_SYNC_ATTEMPTS        (16)                       // Amount of pause milliseconds between synchronization attempts
#define ATTEMPTS_FOR_SYNC              (5)                        // Number of attempts to receive synchronization with the server
//+------------------------------------------------------------------+
//| Structures                                                       |
//+------------------------------------------------------------------+

Um die Kollektionsliste schnell durchsuchen und sortieren zu können, haben wir die Funktionen bereits in der Klasse CSelect erstellt, die in der Datei \MQL5\Include\DoEasy\Services\ Ordner der Dienstfunktionen und Klassen der Datei Select.mqh ausgewählt wurde.

Fügen wir die Methoden zum Suchen und Sortieren in den Listen der Bar-Objekte hinzu.
Binden wir die Objektklassendatei der Bar-Objekte in die Auflistung ein:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\Bar.mqh"
//+------------------------------------------------------------------+

Fügen wir die Definitionen der Methoden zum Suchen und Sortieren nach den Eigenschaften der Bar-Objekte zum Klassenkörper hinzu:

//+------------------------------------------------------------------+
//| Class for sorting objects meeting the criterion                  |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Method for comparing two values
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
//+------------------------------------------------------------------+
//| Methods of working with orders                                   |
//+------------------------------------------------------------------+
   //--- Return the list of orders with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the order index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
   //--- Return the order index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with events                                   |
//+------------------------------------------------------------------+
   //--- Return the list of events with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
   //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with accounts                                 |
//+------------------------------------------------------------------+
   //--- Return the list of accounts with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
   //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with symbols                                  |
//+------------------------------------------------------------------+
   //--- Return the list of symbols with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the symbol index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property);
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property);
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property);
   //--- Return the symbol index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property);
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property);
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with pending requests                         |
//+------------------------------------------------------------------+
   //--- Return the list of pending requests with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the pending request index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property);
   static int        FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property);
   static int        FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property);
   //--- Return the pending request index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property);
   static int        FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property);
   static int        FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with timeseries bars                          |
//+------------------------------------------------------------------+
   //--- Return the list of bars with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the pending request index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property);
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property);
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property);
   //--- Return the pending request index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property);
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property);
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Implementieren wir die hinzuzufügenden Methoden außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Methods of working with timeseries bar lists                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of bars with one integer                         |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of bars with one real                            |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CBar *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of bars with one string                          |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CBar *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the listed bar index                                      |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBar *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the listed bar index                                      |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBar *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the listed bar index                                      |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBar *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the listed bar index                                      |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_INTEGER property)
  {
   int index=0;
   CBar *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the listed bar index                                      |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_DOUBLE property)
  {
   int index=0;
   CBar *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the listed bar index                                      |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_STRING property)
  {
   int index=0;
   CBar *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBar *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Wir haben ähnliche Methoden im dritten Artikel der vorhergehenden Serie besprochen, sodass es keinen Sinn macht, hier darauf einzugehen. Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren stellen.

Wir legen in \MQL5\Include\DoEasy\Objects\Series\ die Datei Series.mqh der Klasse CSeries an und binden die Klassendatei CSelect ein, zusammen mit den neu angelegten Objektklassen "New Bar" und "Bar":

//+------------------------------------------------------------------+
//|                                                       Series.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\Select.mqh"
#include "NewBarObj.mqh"
#include "Bar.mqh"
//+------------------------------------------------------------------+

Fügen wir nun alle erforderlichen Variablen der Klasse hinzu und deklarieren die Klassenmethoden im Klassenkörper:

//+------------------------------------------------------------------+
//|                                                       Series.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\Select.mqh"
#include "NewBarObj.mqh"
#include "Bar.mqh"
//+------------------------------------------------------------------+
//| Timeseries class                                                 |
//+------------------------------------------------------------------+
class CSeries : public CObject
  {
private:
   ENUM_PROGRAM_TYPE m_program;                                         // Program type
   ENUM_TIMEFRAMES   m_timeframe;                                       // Timeframe
   string            m_symbol;                                          // Symbol
   uint              m_amount;                                          // Amount of applied timeseries data
   uint              m_bars;                                            // Number of bars in history by symbol and timeframe
   bool              m_sync;                                            // Synchronized data flag
   CArrayObj         m_list_series;                                     // Timeseries list
   CNewBarObj        m_new_bar_obj;                                     // "New bar" object
public:
//--- Return the timeseries list
   CArrayObj*        GetList(void)                                      { return &m_list_series;}
//--- Return the list of bars by selected (1) double, (2) integer and (3) string property fitting a compared condition
   CArrayObj*        GetList(ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); }
   CArrayObj*        GetList(ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByBarProperty(this.GetList(),property,value,mode); }
   CArrayObj*        GetList(ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); }

//--- Set (1) symbol and timeframe and (2) the number of applied timeseries data
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe);
   bool              SetAmountUsedData(const uint amount,const uint rates_total);

//--- Return (1) symbol, (2) timeframe, (3) number of applied timeseries data,
//--- (4) number of bars in the timeseries, the new bar flag with the (5) auto, (6) manual time management
   string            Symbol(void)                                          const { return this.m_symbol;                            }
   ENUM_TIMEFRAMES   Period(void)                                          const { return this.m_timeframe;                         }
   uint              AmountUsedData(void)                                  const { return this.m_amount;                            }
   uint              Bars(void)                                            const { return this.m_bars;                              }
   bool              IsNewBar(const datetime time)                               { return this.m_new_bar_obj.IsNewBar(time);        }
   bool              IsNewBarManual(const datetime time)                         { return this.m_new_bar_obj.IsNewBarManual(time);  }
//--- Return the bar object by index (1) in the list and (2) in the timeseries
   CBar             *GetBarByListIndex(const uint index);
   CBar             *GetBarBySeriesIndex(const uint index);
//--- Return (1) Open, (2) High, (3) Low, (4) Close, (5) time, (6) tick volume, (7) real volume, (8) bar spread by index
   double            Open(const uint index,const bool from_series=true);
   double            High(const uint index,const bool from_series=true);
   double            Low(const uint index,const bool from_series=true);
   double            Close(const uint index,const bool from_series=true);
   datetime          Time(const uint index,const bool from_series=true);
   long              TickVolume(const uint index,const bool from_series=true);
   long              RealVolume(const uint index,const bool from_series=true);
   int               Spread(const uint index,const bool from_series=true);

//--- Save the new bar time during the manual time management
   void              SaveNewBarTime(const datetime time)                         { this.m_new_bar_obj.SaveNewBarTime(time);         }
//--- Synchronize symbol data with server data
   bool              SyncData(const uint amount,const uint rates_total);
//--- (1) Create and (2) update the timeseries list
   int               Create(const uint amount=0);
   void              Refresh(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
                             
//--- Constructors
                     CSeries(void);
                     CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0);
  };
//+------------------------------------------------------------------+

Die Methoden zum Empfang der Liste sind in allen Objektsammlungsklassen vorhanden, die Entwicklung dieser Methoden wurde im dritten Teil der vorhergehenden Serie beschrieben.

Gehen wir die Liste der deklarierten Methoden durch und analysieren ihre Implementierung.

Der erste Klassenkonstruktor hat keine Parameter und wird für die Erstellung der Liste für das aktuelle Symbol und den aktuellen Zeitrahmen verwendet:

//+------------------------------------------------------------------+
//| Constructor 1 (current symbol and period timeseries)             |
//+------------------------------------------------------------------+
CSeries::CSeries(void) : m_bars(0),m_amount(0),m_sync(false)
  {
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_list_series.Clear();
   this.m_list_series.Sort(SORT_BY_BAR_INDEX);
   this.SetSymbolPeriod(::Symbol(),(ENUM_TIMEFRAMES)::Period());
  }
//+------------------------------------------------------------------+

Die Anzahl der verfügbaren Bars der Zeitreihen, die Anzahl der in der Liste gespeicherten Bars und das Flag der Datensynchronisation mit dem Server werden in der Initialisierungsliste zurückgesetzt.
Der Programmtyp wird dann gesetzt, die Liste der Bar-Objekte wird gelöscht und dafür wird das Flag der Sortierung nach Taktindex gesetzt. Das aktuelle Symbol und der Zeitrahmen für die Liste wird danach gesetzt.
Stellen wir nach dem Erstellen der Liste der Bar-Objekte sicher, dass wir die Anzahl der verwendeten Bars dafür mit der Methode SetAmountUsedData() oder SyncData(), die die Methode SetAmountUsedData() enthält, festlegen. Im Falle von Indikatoren stellen wir sicher, dass rates_total als zweiten Parameter an die Methode übergeben wird.

Der zweite Konstruktor der Klasse verfügt über drei Eingaben (Symbol, Zeitrahmen und die Größe der Liste der Bar-Objekte) und wird dazu verwendet, die Liste für das angegebene Symbol und den angegebenen Zeitrahmen unter Berücksichtigung der erforderlichen Historientiefe zu erstellen:

//+------------------------------------------------------------------+
//| Constructor 2 (specified symbol and period timeseries)           |
//+------------------------------------------------------------------+
CSeries::CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0) : m_bars(0), m_amount(0),m_sync(false)
  {
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_list_series.Clear();
   this.m_list_series.Sort(SORT_BY_BAR_INDEX);
   this.SetSymbolPeriod(symbol,timeframe);
   this.m_sync=this.SetAmountUsedData(amount,0);
  }
//+------------------------------------------------------------------+

Die Anzahl der verfügbaren Bars der Zeitreihen, die Anzahl der in der Liste gespeicherten Bars und das Flag der Datensynchronisation mit dem Server werden in der Initialisierungsliste zurückgesetzt.
Der Programmtyp wird dann gesetzt, die Liste der Bar-Objekte wird gelöscht und dafür wird das Flag der Sortierung nach Taktindex gesetzt.
Das aktuelle Symbol und der Zeitrahmen werden anschließend für die Liste gesetzt.
Schließlich wird das Ergebnis der Methode zum Setzen der Anzahl der erforderlichen Balken für die Liste der Bar-Objekte SetAmountUsedData() für das Synchronisationsflag gesetzt. Die Liste erhält die erforderliche Historietiefe, die durch die Eingabevariable amount des Konstruktors angegeben wird.
Nachdem wir die Liste der Bar-Objekte erstellt haben, stellen wir sicher, dass die Synchronisierung mit dem Server mit der Methode SyncData() des Programms überprüfen.

Die Methode zum Festlegen eines Symbols und Zeitrahmens:

//+------------------------------------------------------------------+
//| Set a symbol and timeframe                                       |
//+------------------------------------------------------------------+
void CSeries::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);
   this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe);
   this.m_new_bar_obj.SetSymbol(this.m_symbol);
   this.m_new_bar_obj.SetPeriod(this.m_timeframe);
  }
//+------------------------------------------------------------------+

Die Methode übergibt ein Symbol und einen Zeitrahmen und die Gültigkeit der übergebenen Werte wird überprüft. Schließlich werden entweder das aktuelle Symbol und der aktuelle Zeitrahmen oder die an die Methode übergebenen Werte gesetzt. Als Nächstes werden das neu in den Variablen gespeicherte Symbol und der Zeitrahmen für das Objekt "New bar" der Barliste gesetzt.

Die Methode, die die Anzahl der verwendeten Zeitreihendaten für die Liste der Bar-Objekte setzt:

//+------------------------------------------------------------------+
//| Set the number of required data                                  |
//+------------------------------------------------------------------+
bool CSeries::SetAmountUsedData(const uint amount,const uint rates_total)
  {
//--- Set the number of available timeseries bars
   this.m_bars=
     (
      //--- If this is an indicator and the work is performed on the current symbol and timeframe,
      //--- add the rates_total value passed to the method,
      //--- otherwise, get the number from the environment
      this.m_program==PROGRAM_INDICATOR && 
      this.m_symbol==::Symbol() && this.m_timeframe==::Period() ? rates_total : 
      ::Bars(this.m_symbol,this.m_timeframe)
     );
//--- If managed to set the number of available history, set the amount of data in the list:
   if(this.m_bars>0)
     {
      //--- if zero 'amount' value is passed,
      //--- use either the default value (1000 bars) or the number of available history bars - the least one of them
      //--- if non-zero 'amount' value is passed,
      //--- use either the 'amount' value or the number of available history bars - the least one of them
      this.m_amount=(amount==0 ? ::fmin(SERIES_DEFAULT_BARS_COUNT,this.m_bars) : ::fmin(amount,this.m_bars));
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Die Methode erhält die notwendige Datenmenge für die Liste der Bar-Objekte und die Gesamtzahl der Bars der aktuellen Zeitreihe (für Indikatoren).
Anschließend wird die Programmart geprüft und die Quelle der Anzahl der verfügbaren Historie für die Variable m_bars definiert — entweder aus dem an die Methode übergebenen Wert (für den Indikator auf dem aktuellen Symbol und Zeitrahmen) oder aus dem Umfeld. Als Nächstes wird definiert, welcher Wert für die Variable m_amount auf der Grundlage der verfügbaren und erforderlichen Historie festgelegt werden soll.

Die Methode zur Synchronisierung von Symbol- und Zeitrahmendaten mit den Serverdaten:

//+------------------------------------------------------------------+
//|Synchronize symbol and timeframe data with server data            |
//+------------------------------------------------------------------+
bool CSeries::SyncData(const uint amount,const uint rates_total)
  {
//--- If managed to obtain the available number of bars in the timeseries
//--- and return the size of the bar object list, return 'true'
   this.m_sync=this.SetAmountUsedData(amount,rates_total);
   if(this.m_sync)
      return true;

//--- Data is not yet synchronized with the server
//--- Create a pause object
   CPause *pause=new CPause();
   if(pause==NULL)
     {
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ));
      return false;
     }
//--- Set the pause duration of 16 milliseconds (PAUSE_FOR_SYNC_ATTEMPTS) and initialize the tick counter
   pause.SetWaitingMSC(PAUSE_FOR_SYNC_ATTEMPTS);
   pause.SetTimeBegin(0);
//--- Make five (ATTEMPTS_FOR_SYNC) attempts to obtain the available number of bars in the timeseries
//--- and set the bar object list size
   int attempts=0;
   while(attempts<ATTEMPTS_FOR_SYNC && !::IsStopped())
     {
      //--- If data is currently synchronized with the server
      if(::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED))
        {
         //--- if managed to obtain the available number of bars in the timeseries
         //--- and set the size of the bar object list, break the loop
         this.m_sync=this.SetAmountUsedData(amount,rates_total);
         if(this.m_sync)
            break;
        }
      //--- Data is not yet synchronized.
      //--- If the pause of 16 ms is over
      if(pause.IsCompleted())
        {
         //--- set the new start of the next waiting for the pause object
         //--- and increase the attempt counter
         pause.SetTimeBegin(0);
         attempts++;
        }
     }
//--- Remove the pause object and return the m_sync value
   delete pause;
   return this.m_sync;
  }
//+------------------------------------------------------------------+

Die Logik der Methode wird in den Code-Kommentaren beschrieben. Ich glaube, da ist alles klar.

Die Methoden zum Erstellen und Aktualisieren der Liste mit den Bar-Objekten sind in der Auflistung ausführlich kommentiert. Betrachten wir sie in ihrer Gesamtheit, um nicht viel Platz für ihre Beschreibung zu beanspruchen. Wenn Sie Fragen zu diesen Methoden haben, können Sie diese gerne in den Kommentaren stellen:

//+------------------------------------------------------------------+
//| Create the timeseries list                                       |
//+------------------------------------------------------------------+
int CSeries::Create(const uint amount=0)
  {
//--- If the required history depth is not set for the list yet,
//--- display the appropriate message and return zero,
   if(this.m_amount==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA));
      return 0;
     }
//--- otherwise, if the passed 'amount' value exceeds zero and is not equal to the one already set, 
//--- while being lower than the available bar number,
//--- set the new value of the required history depth for the list
   else if(amount>0 && this.m_amount!=amount && amount<this.m_bars)
     {
      //--- If failed to set a new value, return zero
      if(!this.SetAmountUsedData(amount,0))
         return 0;
     }
//--- For the rates[] array we are to receive historical data to,
//--- set the flag of direction like in the timeseries,
//--- clear the bar object list and set the flag of sorting by bar index
   MqlRates rates[];
   ::ArraySetAsSeries(rates,true);
   this.m_list_series.Clear();
   this.m_list_series.Sort(SORT_BY_BAR_INDEX);
   ::ResetLastError();
//--- Get historical data of the MqlRates structure to the rates[] array starting from the current bar in the amount of m_amount,
//--- if failed to get data, display the appropriate message and return zero
   int copied=::CopyRates(this.m_symbol,this.m_timeframe,0,this.m_amount,rates),err=ERR_SUCCESS;
   if(copied<1)
     {
      err=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA)," ",this.m_symbol," ",TimeframeDescription(this.m_timeframe),". ",
                   CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err));
      return 0;
     }

//--- Historical data is received in the rates[] array
//--- In the rates[] array loop,
   for(int i=0; i<copied; i++)
     {
      //--- create a new bar object out of the current MqlRates structure by the loop index
      ::ResetLastError();
      CBar* bar=new CBar(this.m_symbol,this.m_timeframe,i,rates[i]);
      if(bar==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError()));
         continue;
        }
      //--- If failed to add bar object to the list,
      //--- display the appropriate message with the error description in the journal
      if(!this.m_list_series.Add(bar))
        {
         err=::GetLastError();
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST)," ",bar.Header(),". ",
                      CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err));
        }
     }
//--- Return the size of the created bar object list
   return this.m_list_series.Total();
  }
//+------------------------------------------------------------------+
//| Update timeseries list and data                                  |
//+------------------------------------------------------------------+
void CSeries::Refresh(const datetime time=0,
                      const double open=0,
                      const double high=0,
                      const double low=0,
                      const double close=0,
                      const long tick_volume=0,
                      const long volume=0,
                      const int spread=0)
  {
   MqlRates rates[1];
//--- Set the flag of sorting the list of bars by index
   this.m_list_series.Sort(SORT_BY_BAR_INDEX);
//--- If a new bar is present on a symbol and period,
   if(this.IsNewBarManual(time))
     {
      //--- create a new bar object and add it to the end of the list
      CBar *new_bar=new CBar(this.m_symbol,this.m_timeframe,0);
      if(new_bar==NULL)
         return;
      if(!this.m_list_series.Add(new_bar))
        {
         delete new_bar;
         return;
        }
      //--- if the specified timeseries size exceeds one bar, remove the earliest bar
      if(this.m_list_series.Total()>1)
         this.m_list_series.Delete(0);
      //--- save the new bar time as the previous one for the subsequent new bar check
      this.SaveNewBarTime(time);
     }
//--- Get the index of the last bar in the list and the object bar by the index
   int index=this.m_list_series.Total()-1;
   CBar *bar=this.m_list_series.At(index);
//--- if the work is performed in an indicator and the timeseries belongs to the current symbol and timeframe,
//--- copy price parameters (passed to the method from the outside) to the bar price structure
   int copied=1;
   if(this.m_program==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period())
     {
      rates[0].time=time;
      rates[0].open=open;
      rates[0].high=high;
      rates[0].low=low;
      rates[0].close=close;
      rates[0].tick_volume=tick_volume;
      rates[0].real_volume=volume;
      rates[0].spread=spread;
     }
//--- otherwise, get data to the bar price structure from the environment
   else
      copied=::CopyRates(this.m_symbol,this.m_timeframe,0,1,rates);
//--- If the prices are obtained, set the new properties from the price structure for the bar object
   if(copied==1)
      bar.SetProperties(rates[0]);
  }
//+------------------------------------------------------------------+

Um die Liste der Zeitreihen zu erstellen, setzen wir zunächst die Größe der erforderlichen Historie und holen das Flag der Synchronisierung mit dem Server mithilfe der Methode SyncData(). Anschließend rufen wir die Methode Create() auf. Um die Daten der Liste der Bar-Objekte zu aktualisieren, stellen wir sicher, dass bei jedem Tick die Methode Refresh() aufrufen. Die Methode erkennt von sich aus eine neue Taktöffnung und fügt das neue Bar-Objekt der Liste der Zeitreihen hinzu. Das früheste Bar-Objekt wird aus der Liste der Bar-Objekte entfernt, so dass die Listengröße immer auf der durch die SyncData()-Methode festgelegten Wert bleibt.

Wir müssen die Bar-Objekte aus der Zeitreihen holen, um die Daten zu verwalten. Wenn das Flag für die Sortierung nach Index (SORT_BY_BAR_INDEX) für die Zeitreihenliste gesetzt ist, entspricht die Reihenfolge der Bar-Objekte in der Liste der Position der realen Bar in der Zeitreihe. Wenn wir jedoch ein anderes Sortierflag für die Liste setzen, entspricht die Reihenfolge der Objekte in der Liste nicht mehr der Lage der realen Takte in der Zeitreihe — sie sind in aufsteigender Reihenfolge der Eigenschaft angeordnet, nach der die Liste sortiert ist. Daher haben wir zwei Methoden, um Objekte aus der Liste der Bar-Objekte auszuwählen: die Methode, die einen Bar-Objekt nach seinem Index in der Zeitreihe zurückgibt, und die Methode, die ein Bar-Objekt nach seinem Index in der Liste der Bar-Objekte zurückgibt.

Betrachten wir diese beiden Methoden.

Die Methode, die ein Bar-Objekt anhand seines Indexes in der Liste der Bar-Objekte zurückgibt:

//+------------------------------------------------------------------+
//| Return the bar object by index in the list                       |
//+------------------------------------------------------------------+
CBar *CSeries::GetBarByListIndex(const uint index)
  {
   return this.m_list_series.At(this.m_list_series.Total()-index-1);
  }
//+------------------------------------------------------------------+

Der Index des erforderlichen Bar-Objekts wird an die Methode übergeben. Der übergebene Index impliziert die gleiche Richtung wie in der Zeitreihe: der Index Null zeigt das letzte Objekt in der Liste an. Die Objekte in der Liste werden jedoch vom Nullindex bis zu list.Total()-1 gespeichert, d.h. um die letzte Bar in der Liste zu erhalten, fordern wir ihn über den Index list.Total()-1 an, während wir, um die äußerste rechte Bar im Diagramm zu erhalten, ihn über den Index 0 anfordern (umgekehrte Indizierung).
Daher wird der Index in der entsprechenden Methode neu berechnet: Die Nummer des übergebenen Index-1 wird von der Listengröße subtrahiert, und das Bar-Objekt wird auf der Grundlage des berechneten Index entsprechend der Indexierungsrichtung wie in der Zeitreihe zurückgegeben.

Die Methode, die ein Bar-Objekt anhand seines Index in der Zeitreihe zurückgibt:

//+------------------------------------------------------------------+
//| Return the bar object by index in the timeseries                 |
//+------------------------------------------------------------------+
CBar *CSeries::GetBarBySeriesIndex(const uint index)
  {
   CArrayObj *list=this.GetList(BAR_PROP_INDEX,index);
   return(list==NULL || list.Total()==0 ? NULL : list.At(0));
  }
//+------------------------------------------------------------------+

Der Index des erforderlichen Bar-Objekts wird an die Methode übergeben. Der übergebene Index impliziert die gleiche Richtung wie in der Zeitreihe.
Um das Objekt mit dem gleichen Barindex wie in der Zeitreihe zu erhalten, sollte es durch die Eigenschaft BAR_PROP_INDEX ausgewählt werden. Wenn die Liste der Bar-Objekte die Bar mit dem erforderlichen Index aufweist, weist list ein einzelnes Objekt auf, das zurückgegeben werden sollte.
Wenn kein solches Objekt vorhanden ist, wird NULL zurückgegeben. Im Falle eines Fehlers geben beide Methoden NULL zurück.

Die Methoden, die die grundlegenden Eigenschaften des Bar-Objekts aus der Liste der Bar-Objekte durch den Index zurückgeben:

//+------------------------------------------------------------------+
//| Return bar's Open by the index                                   |
//+------------------------------------------------------------------+
double CSeries::Open(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.Open() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return bar's High by the timeseries index or the list of bars    |
//+------------------------------------------------------------------+
double CSeries::High(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.High() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return bar's Low by the timeseries index or the list of bars     |
//+------------------------------------------------------------------+
double CSeries::Low(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.Low() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return bar's Close by the timeseries index or the list of bars   |
//+------------------------------------------------------------------+
double CSeries::Close(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.Close() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return bar time by the timeseries index or the list of bars      |
//+------------------------------------------------------------------+
datetime CSeries::Time(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.Time() : 0);
  }
//+-------------------------------------------------------------------+
//|Return bar tick volume by the timeseries index or the list of bars |
//+-------------------------------------------------------------------+
long CSeries::TickVolume(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.VolumeTick() : WRONG_VALUE);
  }
//+--------------------------------------------------------------------+
//|Return bar real volume by the timeseries index or the list of bars  |
//+--------------------------------------------------------------------+
long CSeries::RealVolume(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.VolumeReal() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return bar spread by the timeseries index or the list of bars    |
//+------------------------------------------------------------------+
int CSeries::Spread(const uint index,const bool from_series=true)
  {
   CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index));
   return(bar!=NULL ? bar.Spread() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

Die Methoden erhalten den Barindex und das Flag, das anzeigt, dass der angeforderte Index der Indexierungsrichtung entspricht (true), wie in der Zeitreihe.
Basierend auf dem Wert des Flags, erhalten wir das Bar-Objekt entweder mit der Methode GetBarBySeriesIndex() oder GetBarByListIndex(). Dann gibt den Wert der Eigenschaft zurück, die in der Methode angefordert wurde.

Die übrigen hier nicht betrachteten Klassenmethoden von CSeries werden zum Setzen oder Zurückgeben der Werte von Klassenvariablen verwendet.

Um zu überprüfen, was wir im aktuellen Artikel getan haben, müssen wir das externe Programm über die erstellten Klassen informieren. Um dies zu erreichen, binden wir einfach die Klassendatei CSeries in die Hauptobjektdatei der CEngine-Bibliothek ein:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "TradingControl.mqh"
#include "Objects\Series\Series.mqh"
//+------------------------------------------------------------------+

Jetzt sind wir in der Lage, die Variable der Klasse CSeries im Test-EA zu definieren, um die Listen der Zeitreihen mit der angegebenen Anzahl von Bar-Objekten zu erstellen und zu verwenden (in der aktuellen Implementierung werden die Listen nur zum Testen verwendet). Machen wir das jetzt.

Tests

Um den Test durchzuführen, verwenden wir den EA aus dem letzten Artikel der vorherigen Serie und speichern ihn in \MQL5\Experts\TestDoEasy\Part35\ unter dem Namen TestDoEasyPart35.mq5.


Erstellen wir zwei Variablen (zum Testen) des Objekts der Klasse CSeries — für M1 (2 Balken) und den aktuellen Zeitrahmen (10 Balken). Wir setzen alle erforderlichen Parameter in OnInit() und zeigen drei Listen:

  1. die Liste aller Bars der aktuellen Zeitreihe, sortiert nach Kerzengröße (von Hoch bis Tief der Bar) — kurze Beschreibungen der Bar-Objekte;
  2. die Liste aller Bars der aktuellen Zeitreihe, sortiert nach Barindizes (von Bar 0 bis 9) — Kurzbeschreibungen der Bar-Objekte;
  3. die Liste aller Eigenschaften der Bar-Objekte, die dem entsprechenden Index 1 (vorherige Bar) der aktuellen Zeitreihe entsprechen — vollständige Liste aller Bareigenschaften.

Aktualisieren wir in OnTick() beide Zeitreihen bei jedem Tick und zeigen den Eintrag zum Öffnen einer neuen Bar in jeder der beiden Zeitreihen dieser beiden Zeitreihenlisten an — М1 und die aktuelle. Spielen wir neben dem Journaleintrag den Standardton "news.wav" ab, wenn wir eine neue Bar für die aktuelle Zeitreihe öffnen.

Definieren wir in der Liste der globalen EA-Variablen die beiden Variablen des Klassentyps CSeriesdie Variable der Liste der Zeitreihen des aktuellen Zeitrahmens und die Variable der Liste der Zeitreihen M1:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart35.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- enums
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_SET_STOP_LOSS,
   BUTT_SET_TAKE_PROFIT,
   BUTT_PROFIT_WITHDRAWAL,
   BUTT_TRAILING_ALL
  };
#define TOTAL_BUTT   (20)
//--- structures
struct SDataButt
  {
   string      name;
   string      text;
  };
//--- input variables
input    ushort            InpMagic             =  123;  // Magic number
input    double            InpLots              =  0.1;  // Lots
input    uint              InpStopLoss          =  150;  // StopLoss in points
input    uint              InpTakeProfit        =  150;  // TakeProfit in points
input    uint              InpDistance          =  50;   // Pending orders distance (points)
input    uint              InpDistanceSL        =  50;   // StopLimit orders distance (points)
input    uint              InpDistancePReq      =  50;   // Distance for Pending Request's activate (points)
input    uint              InpBarsDelayPReq     =  5;    // Bars delay for Pending Request's activate (current timeframe)
input    uint              InpSlippage          =  5;    // Slippage in points
input    uint              InpSpreadMultiplier  =  1;    // Spread multiplier for adjusting stop-orders by StopLevel
input    uchar             InpTotalAttempts     =  5;    // Number of trading attempts
sinput   double            InpWithdrawal        =  10;   // Withdrawal funds (in tester)
sinput   uint              InpButtShiftX        =  0;    // Buttons X shift 
sinput   uint              InpButtShiftY        =  10;   // Buttons Y shift 
input    uint              InpTrailingStop      =  50;   // Trailing Stop (points)
input    uint              InpTrailingStep      =  20;   // Trailing Step (points)
input    uint              InpTrailingStart     =  0;    // Trailing Start (points)
input    uint              InpStopLossModify    =  20;   // StopLoss for modification (points)
input    uint              InpTakeProfitModify  =  60;   // TakeProfit for modification (points)
sinput   ENUM_SYMBOLS_MODE InpModeUsedSymbols   =  SYMBOLS_MODE_CURRENT;   // Mode of used symbols list
sinput   string            InpUsedSymbols       =  "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)
sinput   bool              InpUseSounds         =  true; // Use sounds
//--- global variables
CEngine        engine;
CSeries        series;
CSeries        series_m1;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pressed_pending_buy;
bool           pressed_pending_buy_limit;
bool           pressed_pending_buy_stop;
bool           pressed_pending_buy_stoplimit;
bool           pressed_pending_close_buy;
bool           pressed_pending_close_buy2;
bool           pressed_pending_close_buy_by_sell;
bool           pressed_pending_sell;
bool           pressed_pending_sell_limit;
bool           pressed_pending_sell_stop;
bool           pressed_pending_sell_stoplimit;
bool           pressed_pending_close_sell;
bool           pressed_pending_close_sell2;
bool           pressed_pending_close_sell_by_buy;
bool           pressed_pending_delete_all;
bool           pressed_pending_close_all;
bool           pressed_pending_sl;
bool           pressed_pending_tp;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         used_symbols;
string         array_used_symbols[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;
//+------------------------------------------------------------------+

In OnInit() des EA setzen wir die notwendigen Eigenschaften für die beiden Variablen der Zeitreihenobjekte und zeigen die gesamten Daten auf der erstellten Liste der Bar-Objekte des aktuellen Zeitrahmens an. Bei М1 zeigen wir einfach die Meldung über die erfolgreiche Listenerstellung an:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   testing=engine.IsTester();
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq);
   bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq);
   g_point=SymbolInfoDouble(NULL,SYMBOL_POINT);
   g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS);
//--- Initialize random group numbers
   group1=0;
   group2=0;
   srand(GetTickCount());
   
//--- Initialize DoEasy library
   OnInitDoEasy();
   
//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- Reset states of the buttons for working using pending requests
   for(int i=0;i<14;i++)
     {
      ButtonState(butt_data[i].name+"_PRICE",false);
      ButtonState(butt_data[i].name+"_TIME",false);
     }

//--- Check playing a standard sound by macro substitution and a custom sound by description
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

//--- Set the M1 timeseries object parameters
   series_m1.SetSymbolPeriod(Symbol(),PERIOD_M1);
//--- If symbol and M1 timeframe data are synchronized
   if(series_m1.SyncData(2,0))
     {
      //--- create the timeseries list of two bars (the current and previous ones),
      //--- if the timeseries is created, display the appropriate message in the journal
      int total=series_m1.Create(2);
      if(total>0)
         Print(TextByLanguage("Создана таймсерия М1 с размером ","Created timeseries M1 with size "),(string)total);
     }
//--- Check filling price data on the current symbol and timeframe
   series.SetSymbolPeriod(Symbol(),(ENUM_TIMEFRAMES)Period());
//--- If symbol and the current timeframe data are synchronized
   if(series.SyncData(10,0))
     {
      //--- create the timeseries list of ten bars (bars 0 - 9),
      //--- if the timeseries is created, display three lists:
      //--- 1. the list of bars sorted by candle size (from bars' High to Low)
      //--- 2. the list of bars sorted by bar indices (according to their sequence in the timeseries)
      //--- 3. the full list of all properties of the previous bar object (bar properties with the timeseries index of 1)
      int total=series.Create(10);
      if(total>0)
        {
         CArrayObj *list=series.GetList();
         CBar *bar=NULL;
         //--- Display short properties of the bar list by the candle size
         Print("\n",TextByLanguage("Бары, сортированные по размеру свечи от High до Low:","Bars sorted by candle size from High to Low:"));
         list.Sort(SORT_BY_BAR_CANDLE_SIZE);
         for(int i=0;i<total;i++)
           {
            bar=series.GetBarByListIndex(i);
            if(bar==NULL)
               continue;
            bar.PrintShort();
           }
         //--- Display short properties of the bar list by the timeseries index
         Print("\n",TextByLanguage("Бары, сортированные по индексу таймсерии:","Bars sorted by timeseries index:"));
         list.Sort(SORT_BY_BAR_INDEX);
         for(int i=0;i<total;i++)
           {
            bar=series.GetBarByListIndex(i);
            if(bar==NULL)
               continue;
            bar.PrintShort();
           }
         //--- Display all bar 1 properties
         Print("");
         list=CSelect::ByBarProperty(list,BAR_PROP_INDEX,1,EQUAL);
         if(list.Total()==1)
           {
            bar=list.At(0);
            bar.Print();
           }
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

In OnTick() aktualisieren wir bei jedem Tick die Zeitreihen der Objekte der Klasse CSeries , bei dem Ereignis "New bar", für jede der beiden Listen die Meldungen zu diesem Ereignis anzeigt. zusätzlich den Ton des Ereignisses neue Bar im aktuellen Zeitrahmen abspielen:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();       // Working in the timer
      PressButtonsControl();  // Button pressing control
      EventsHandling();       // Working with events
     }
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();    // Trailing positions
      TrailingOrders();       // Trailing of pending orders
     }
//--- Check the update of the current and M1 timeseries
   series.Refresh();
   if(series.IsNewBar(0))
     {
      Print("New bar on ",series.Symbol()," ",TimeframeDescription(series.Period())," ",TimeToString(series.Time(0)));
      engine.PlaySoundByDescription(SND_NEWS);
     }
   series_m1.Refresh();
   if(series_m1.IsNewBar(0))
     {
      Print("New bar on ",series_m1.Symbol()," ",TimeframeDescription(series_m1.Period())," ",TimeToString(series_m1.Time(0)));
     }
  }
//+------------------------------------------------------------------+

Kompilieren Sie den EA und starten Sie ihn auf dem Chart.
Zuerst zeigt das Journal Einträge zu Beginn der Erstellung der M1-Zeitserien von zwei Bars, dann die Liste der Bars sortiert nach Kerzengröße, dann die Liste der Bars sortiert in der Reihenfolge der Barindizes in der Zeitreihe und schließlich alle Eigenschaften der Bar-Objekte mit dem Index 1 in der Zeitreihe:

Account 15585535: Artyom Trishkin (MetaQuotes Software Corp.) 9999.40 USD, 1:100, Demo account MetaTrader 5
Work only with the current symbol. The number of symbols used: 1
Created timeseries M1 with size 2
 
Bars, sorted by size candle from High to Low:
Bar "EURUSD" H1[2]: 2020.02.12 10:00:00, O: 1.09145, H: 1.09255, L: 1.09116, C: 1.09215, V: 2498, Bullish bar
Bar "EURUSD" H1[3]: 2020.02.12 09:00:00, O: 1.09057, H: 1.09150, L: 1.09022, C: 1.09147, V: 1773, Bullish bar
Bar "EURUSD" H1[1]: 2020.02.12 11:00:00, O: 1.09215, H: 1.09232, L: 1.09114, C: 1.09202, V: 1753, Bearish bar
Bar "EURUSD" H1[9]: 2020.02.12 03:00:00, O: 1.09130, H: 1.09197, L: 1.09129, C: 1.09183, V: 1042, Bullish bar
Bar "EURUSD" H1[4]: 2020.02.12 08:00:00, O: 1.09108, H: 1.09108, L: 1.09050, C: 1.09057, V: 581, Bearish bar
Bar "EURUSD" H1[8]: 2020.02.12 04:00:00, O: 1.09183, H: 1.09197, L: 1.09146, C: 1.09159, V: 697, Bearish bar
Bar "EURUSD" H1[5]: 2020.02.12 07:00:00, O: 1.09122, H: 1.09143, L: 1.09096, C: 1.09108, V: 591, Bearish bar
Bar "EURUSD" H1[6]: 2020.02.12 06:00:00, O: 1.09152, H: 1.09159, L: 1.09121, C: 1.09122, V: 366, Bearish bar
Bar "EURUSD" H1[7]: 2020.02.12 05:00:00, O: 1.09159, H: 1.09177, L: 1.09149, C: 1.09152, V: 416, Bearish bar
Bar "EURUSD" H1[0]: 2020.02.12 12:00:00, O: 1.09202, H: 1.09204, L: 1.09181, C: 1.09184, V: 63, Bearish bar
 
Bars, sorted by timeseries index:
Bar "EURUSD" H1[9]: 2020.02.12 03:00:00, O: 1.09130, H: 1.09197, L: 1.09129, C: 1.09183, V: 1042, Bullish bar
Bar "EURUSD" H1[8]: 2020.02.12 04:00:00, O: 1.09183, H: 1.09197, L: 1.09146, C: 1.09159, V: 697, Bearish bar
Bar "EURUSD" H1[7]: 2020.02.12 05:00:00, O: 1.09159, H: 1.09177, L: 1.09149, C: 1.09152, V: 416, Bearish bar
Bar "EURUSD" H1[6]: 2020.02.12 06:00:00, O: 1.09152, H: 1.09159, L: 1.09121, C: 1.09122, V: 366, Bearish bar
Bar "EURUSD" H1[5]: 2020.02.12 07:00:00, O: 1.09122, H: 1.09143, L: 1.09096, C: 1.09108, V: 591, Bearish bar
Bar "EURUSD" H1[4]: 2020.02.12 08:00:00, O: 1.09108, H: 1.09108, L: 1.09050, C: 1.09057, V: 581, Bearish bar
Bar "EURUSD" H1[3]: 2020.02.12 09:00:00, O: 1.09057, H: 1.09150, L: 1.09022, C: 1.09147, V: 1773, Bullish bar
Bar "EURUSD" H1[2]: 2020.02.12 10:00:00, O: 1.09145, H: 1.09255, L: 1.09116, C: 1.09215, V: 2498, Bullish bar
Bar "EURUSD" H1[1]: 2020.02.12 11:00:00, O: 1.09215, H: 1.09232, L: 1.09114, C: 1.09202, V: 1753, Bearish bar
Bar "EURUSD" H1[0]: 2020.02.12 12:00:00, O: 1.09202, H: 1.09204, L: 1.09181, C: 1.09184, V: 63, Bearish bar
 
============= The beginning of the event parameter list (Bar "EURUSD" H1[1]) =============
Timeseries index: 1
Type: Bearish bar
Timeframe: H1
Spread: 1
Tick volume: 1753
Real volume: 0
Period start time: 2020.02.12 11:00:00
Sequence day number in the year: 042
Year: 2020
Month: February
Day of week: Wednesday
Day od month: 12
Hour: 11
Minute: 00
------
Price open: 1.09215
Highest price for the period: 1.09232
Lowest price for the period: 1.09114
Price close: 1.09202
Candle size: 0.00118
Candle body size: 0.00013
Top of the candle body: 1.09215
Bottom of the candle body: 1.09202
Candle upper shadow size: 0.00017
Candle lower shadow size: 0.00088
------
Symbol: "EURUSD"
============= End of the parameter list (Bar "EURUSD" H1[1]) =============


Lassen wir nun den EA im visuellen Tester-Modus auf М5 starten und einen Blick auf die Meldungen des Tester-Journals über das Öffnen neuer Bars werfen:

Wie wir sehen können, geht es in jeder fünften Nachricht um die Eröffnung einer neuen Bar auf М5, unterbrochen von den Nachrichten über die Eröffnung einer neuen Bar auf М1.

Was kommt als Nächstes?

Im nächsten Artikel werden wir die Kollektionsklasse der Bar-Listen erstellen. 

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und herunterladen können.
Schreiben Sie Ihre Fragen und Vorschläge in den Kommentaren.

Zurück zum Inhalt

Artikel aus der vorhergehenden Serie:

Teil 1. Konzept, Datenverwaltung.
Teil 2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung (Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4. Handelsereignisse. Konzept
Teil 5. Klassen und Kollektionen von Handelsereignissen. Senden von Ereignissen an das Programm
Teil 6. Ereignisse auf Netting-Konten
Teil 7. Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen
Teil 8. Ereignisse von Änderungen von Orders und Positionen
Teil 9. Kompatibilität mit MQL4 — Datenvorbereitung
Teil 10. Kompatibilität mit MQL4 - Ereignisse der Positionseröffnung und Aktivierung von Pending-Orders
Teil 11. Kompatibilität mit MQL4 - Ereignisse des Schließens von Positionen
Teil 12. Objektklasse "Account" und die Kollektion von Konto-Objekten
Teil 13. Das Objekt der Kontoereignisse
Teil 14. Das Symbolobjekt
Teil 15. Die Kollektion der Symbolobjekte
Teil 16. Ereignisse der Kollektionssymbole
Teil 17. Interaktivität von Bibliotheksobjekten
Teil 18. Interaktivität des Kontos und aller anderen Bibliotheksobjekt
Teil 19. Klassenbibliothek für Nachrichten
Teil 20. Erstellen und Speichern von Programmressourcen
Teil 21. Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt
Teil 22. Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen
Teil 23. Handelsklasse - Basisklasse des Handels, Verifikation der Parameter
Teil 24. Trading classes - Handelsklassen, automatische Korrektur ungültiger Parametern
Teil 25. Handelsklassen - Basisklasse des Handels, Behandlung der Fehlermeldungen vom Server
Teil 26. Arbeiten mit schwebenden Handelsanfragen - Erste Implementation (Öffnen von Positionen)
Teil 27. Arbeiten mit schwebenden Handelsanfragen - Platzieren von Pending-Orders
Teil 28. Arbeiten mit schwebenden Handelsanfragen - Schließen, Entfernen und Ändern
Teil 29. Arbeiten mit schwebenden Handelsanfragen - Die Klasse der Anfrageobjekte
Teil 30. Schwebende Handelsanfragen - Die Behandlung der Anfrageobjekte
Teil 31. Schwebende Handelsanfragen - Positionseröffnungen unter bestimmten Bedingungen
Teil 32. Schwebende Handelsanfragen - Platzieren von Pending-Orders unter bestimmten Bedingungen
Teil 33. Schwebende Handelsanfragen - Positionen unter bestimmten Bedingungen schließen (ganz, teilweise oder durch eine entgegengesetzte P.)
Teil 34. Schwebende Handelsanfragen - Entfernen und Ändern von Orders und Positionen unter bestimmten Bedingungen


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7594

Beigefügte Dateien |
MQL5.zip (3683.65 KB)
MQL4.zip (3683.65 KB)
Prognose von Zeitreihen (Teil 1): Methode der Empirischen Modus Dekomposition (Empirical Mode Decomposition, EMD) Prognose von Zeitreihen (Teil 1): Methode der Empirischen Modus Dekomposition (Empirical Mode Decomposition, EMD)
Dieser Artikel befasst sich mit der Theorie und der praktischen Anwendung des Algorithmus zur Vorhersage von Zeitreihen, basierend auf der empirischen Moduszerlegung. Er schlägt die MQL-Implementierung dieser Methode vor und stellt Testindikatoren und Expert Advisors vor.
Die Handelssignale mehrerer Währungen überwachen (Teil 2): Implementierung des visuellen Teils der Anwendung Die Handelssignale mehrerer Währungen überwachen (Teil 2): Implementierung des visuellen Teils der Anwendung
Im vorigen Artikel haben wir den Anwendungsrahmen geschaffen, den wir als Grundlage für alle weiteren Arbeiten verwenden werden. In diesem Teil werden wir mit der Entwicklung fortfahren: Wir werden den visuellen Teil der Anwendung erstellen und die grundlegende Interaktion der Oberflächenelemente konfigurieren.
Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolperioden Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolperioden
In diesem Artikel werden wir uns überlegen, die Listen der Bar-Objekte für jede verwendete Symbolperiode zu einem einzigen Symbol-Zeitreihen-Objekt zusammenzufassen. Auf diese Weise wird jedes Symbol ein Objekt haben, das die Listen aller verwendeten Symbolzeitreihen-Perioden speichert.
Verwendung von Netzwerkfunktionen oder MySQL ohne DLL: Teil II - Programm zur Überwachung von Änderungen der Signaleigenschaften Verwendung von Netzwerkfunktionen oder MySQL ohne DLL: Teil II - Programm zur Überwachung von Änderungen der Signaleigenschaften
Im vorherigen Teil haben wir die Implementierung des MySQL-Konnektors besprochen. In diesem Artikel wenden wir uns seiner Anwendung durch die Implementierung eines Dienstes zum Sammeln von Signaleigenschaften und des Programms zum Anzeigen ihrer Änderungen im Laufe der Zeit. Das implementierte Beispiel ist praktisch sinnvoll, wenn Nutzer Änderungen an Eigenschaften beobachten müssen, die auf der Webseite des Signals nicht angezeigt werden.