Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen

29 Juni 2020, 09:59
Artyom Trishkin
0
180

Inhalt


Konzept

  • Zu Beginn der Serie haben wir das Objekt Balken erstellt, das die Daten eines einzelnen Balkens eines bestimmten Diagrammsymbols und einer bestimmten Periode enthält.
  • Wir haben das Balkenkollektion — Zeitreihenobjekt eines bestimmten Chartsymbols und einer bestimmten Periode erstellt.
  • Alle Zeitreihenobjekte eines einzelnen Symbols wurden zu einem einzigen Zeitreihenobjekt eines einzelnen Symbols zusammengefasst.

Heute werden wir das Sammelobjekt der Zeitreihen der im Programm verwendeten Symbole erstellen, von denen jedes Daten bestimmter Zeiträume eines Symbols enthält. Als Ergebnis erhalten wir ein Objekt, das alle Daten für eine bestimmte Anzahl von Takten für jede Zeitreihe jedes Symbols enthält.

Die Zeitreihensammlung soll alle erforderlichen historischen Daten für jedes der im Programm verwendeten Symbole und für alle in den Programmeinstellungen einzustellenden Zeitrahmen speichern.
Darüber hinaus ermöglicht die Kollektion die Einstellung der erforderlichen Daten für jeden Zeitrahmen jedes Symbols separat.

Da die Beschreibung der Funktionalität der Zeitreihensammlung recht umfangreich ist, sollen ihre Echtzeit-Aktualisierungen und die Gewinnung aller möglichen Daten daraus im nächsten Artikel implementiert werden.


Verbesserung der zuvor erstellten Zeitreihenobjekte

Die meisten Bibliotheksobjekte sind vom Basisobjekt aller Bibliotheksobjekte abgeleitet, das wiederum von der Basisklasse für den Aufbau der MQL5-Standardbibliothek abgeleitet ist.
Mit dem wachsenden Bibliotheksbedarf ist auch die Klasse CBaseObj des Basis-Bibliotheksobjekts größer geworden. Wenn wir nun neue Objekte von ihr erben, dann erhalten sie zusätzliche und oft völlig unnötige Methoden.
Um dieses Problem zu lösen, teilen wir die Basisobjektklasse in zwei Klassen auf:

  • die erste (CBaseObj) soll den minimalen, grundlegenden Satz von Eigenschaften und Methoden für jedes Bibliotheksobjekt enthalten,
  • die zweite (CBaseObjExt) ist erweitert und von CBaseObj abgeleitete mit den Eigenschaften und Methoden zur Interaktion mit einem Nutzer und die Ereignisfunktionalität von abgeleiteten Objekten.

So sollen die Objekte, die Basiseigenschaften und Methoden benötigen, von CBaseObj abgeleitet werden, während Objekte, die die Ereignisfunktionen benötigen, von CBaseObjExt abgeleitet werden.

Zuerst benennen Sie einfach die CBaseObj-Basisobjektklasse in \MQL5\Include\DoEasy\Objects\BaseObj.mqh in CBaseObjExt um und kompilieren die Datei des Hauptobjekts der CEngine-Bibliothek in \MQL5\Include\DoEasy\Engine.mqh, was eine große Liste von Kompilierungsfehlern verursacht (da wir die Bibliotheks-Hauptobjektklasse umbenannt haben).

Gehen Sie die Liste aller Fehler durch, die das Fehlen der Klasse CBaseObj anzeigen, und ersetzen Sie alle Instanzen von "CBaseObj"-Strings in den Klassenlisten durch "CBaseObjExt". Eine Neukompilierung mit den korrigierten Basisobjekt-Klassennamen sollte erfolgreich sein.

Fügen Sie nun in der Auflistung der Basisobjektklassen die neue Klasse CBaseObj hinzu, aus dem Basisobjekt der MQL5-Bibliothek ableiten und aus der Klasse CBaseObjExt alle Variablen und Methoden verschieben, die in der neuen Klasse des Basisobjekts vorhanden sein sollen. Die Klasse CBaseObjExt wird von CBaseObj abgeleitet.

Es scheint alles kompliziert zu sein, aber wenn wir einen Blick auf das Klassenverzeichnis werfen, wird alles klar (es hat keinen Sinn, hier das vollständige Verzeichnis anzuzeigen, den gesamten Code finden Sie in den angehängten Dateien):

//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
protected:
   ENUM_LOG_LEVEL    m_log_level;                              // Logging level
   ENUM_PROGRAM_TYPE m_program;                                // Program type
   bool              m_first_start;                            // First launch flag
   bool              m_use_sound;                              // Flag of playing the sound set for an object
   bool              m_available;                              // Flag of using a descendant object in the program
   int               m_global_error;                           // Global error code
   long              m_chart_id_main;                          // Control program chart ID
   long              m_chart_id;                               // Chart ID
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 
   string            m_sound_name;                             // Object sound file name
   int               m_type;                                   // Object type (corresponds to the collection IDs)

public:
//--- (1) Set, (2) return the error logging level
   void              SetLogLevel(const ENUM_LOG_LEVEL level)         { this.m_log_level=level;                 }
   ENUM_LOG_LEVEL    GetLogLevel(void)                         const { return this.m_log_level;                }
//--- (1) Set and (2) return the chart ID of the control program
   void              SetMainChartID(const long id)                   { this.m_chart_id_main=id;                }
   long              GetMainChartID(void)                      const { return this.m_chart_id_main;            }
//--- (1) Set and (2) return chart ID
   void              SetChartID(const long id)                       { this.m_chart_id=id;                     }
   long              GetChartID(void)                          const { return this.m_chart_id;                 }
//--- (1) Set the sub-folder name, (2) return the folder name for storing descendant object files
   void              SetSubFolderName(const string name)             { this.m_folder_name=DIRECTORY+name;      }
   string            GetFolderName(void)                       const { return this.m_folder_name;              }
//--- (1) Set and (2) return the name of the descendant object sound file
   void              SetSoundName(const string name)                 { this.m_sound_name=name;                 }
   string            GetSoundName(void)                        const { return this.m_sound_name;               }
//--- (1) Set and (2) return the flag of playing descendant object sounds
   void              SetUseSound(const bool flag)                    { this.m_use_sound=flag;                  }
   bool              IsUseSound(void)                          const { return this.m_use_sound;                }
//--- (1) Set and (2) return the flag of using the descendant object in the program
   void              SetAvailable(const bool flag)                   { this.m_available=flag;                  }
   bool              IsAvailable(void)                         const { return this.m_available;                }
//--- Return the global error code
   int               GetError(void)                            const { return this.m_global_error;             }
//--- Return the object name
   string            GetName(void)                             const { return this.m_name;                     }
//--- Return an object type
   virtual int       Type(void)                                const { return this.m_type;                     }
//--- Constructor
                     CBaseObj() : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
                                  m_global_error(ERR_SUCCESS),
                                  m_log_level(LOG_LEVEL_ERROR_MSG),
                                  m_chart_id_main(::ChartID()),
                                  m_chart_id(::ChartID()),
                                  m_folder_name(DIRECTORY),
                                  m_sound_name(""),
                                  m_name(__FUNCTION__),
                                  m_type(0),
                                  m_use_sound(false),
                                  m_available(true),
                                  m_first_start(true) {}
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Extended base object class for all library objects               |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObjExt : public CBaseObj
  {
private:
   int               m_long_prop_total;
   int               m_double_prop_total;
   //--- Fill in the object property array
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:
   CArrayObj         m_list_events_base;                       // Object base event list
   CArrayObj         m_list_events;                            // Object event list
   MqlTick           m_tick;                                   // Tick structure for receiving quote data
   double            m_hash_sum;                               // Object data hash sum
   double            m_hash_sum_prev;                          // Object data hash sum during the previous check
   int               m_digits_currency;                        // Number of decimal places in an account currency
   bool              m_is_event;                               // Object event flag
   int               m_event_code;                             // Object event code
   int               m_event_id;                               // Event ID (equal to the object property value)

//--- Data for storing, controlling and returning tracked properties:
//--- [Property index][0] Controlled property increase value
//--- [Property index][1] Controlled property decrease value
//--- [Property index][2] Controlled property value level
//--- [Property index][3] Property value
//--- [Property index][4] Property value change
//--- [Property index][5] Flag of a property change exceeding the increase value
//--- [Property index][6] Flag of a property change exceeding the decrease value
//--- [Property index][7] Flag of a property increase exceeding the control level
//--- [Property index][8] Flag of a property decrease being less than the control level
//--- [Property index][9] Flag of a property value being equal to the control level
   long              m_long_prop_event[][CONTROLS_TOTAL];         // The array for storing object's integer properties values and controlled property change values
   double            m_double_prop_event[][CONTROLS_TOTAL];       // The array for storing object's real properties values and controlled property change values
   long              m_long_prop_event_prev[][CONTROLS_TOTAL];    // The array for storing object's controlled integer properties values during the previous check
   double            m_double_prop_event_prev[][CONTROLS_TOTAL];  // The array for storing object's controlled real properties values during the previous check

//--- Return (1) time in milliseconds, (2) milliseconds from the MqlTick time value
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;  }
   ushort            MSCfromTime(const long time_msc)          const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ;              }
//--- return the flag of the event code presence in the event object
   bool              IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; }
//--- Return the number of decimal places of the account currency
   int               DigitsCurrency(void)                      const { return this.m_digits_currency; }
//--- Returns the number of decimal places in the 'double' value
   int               GetDigits(const double value)             const;

//--- Set the size of the array of controlled (1) integer and (2) real object properties
   bool              SetControlDataArraySizeLong(const int size);
   bool              SetControlDataArraySizeDouble(const int size);
//--- Check the array size of object properties
   bool              CheckControlDataArraySize(bool check_long=true);
   
//--- Check the list of object property changes and create an event
   void              CheckEvents(void);
   
//--- (1) Pack a 'ushort' number to a passed 'long' number
   long              UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value);
   
protected:
//--- (1) convert a 'ushort' value to a specified 'long' number byte
   long              UshortToByte(const ushort value,const uchar to_byte)  const;
   
public:
//--- Set the value of the pbject property controlled (1) increase, (2) decrease, (3) control level
   template<typename T> void  SetControlledValueINC(const int property,const T value);
   template<typename T> void  SetControlledValueDEC(const int property,const T value);
   template<typename T> void  SetControlledValueLEVEL(const int property,const T value);

//--- Return the set value of the controlled (1) integer and (2) real object properties increase
   long              GetControlledLongValueINC(const int property)      const { return this.m_long_prop_event[property][0];                           }
   double            GetControlledDoubleValueINC(const int property)    const { return this.m_double_prop_event[property-this.m_long_prop_total][0];  }
//--- Return the set value of the controlled (1) integer and (2) real object properties decrease
   long              GetControlledLongValueDEC(const int property)      const { return this.m_long_prop_event[property][1];                           }
   double            GetControlledDoubleValueDEC(const int property)    const { return this.m_double_prop_event[property-this.m_long_prop_total][1];  }
//--- Return the specified control level of object's (1) integer and (2) real properties
   long              GetControlledLongValueLEVEL(const int property)    const { return this.m_long_prop_event[property][2];                           }
   double            GetControlledDoubleValueLEVEL(const int property)  const { return this.m_double_prop_event[property-this.m_long_prop_total][2];  }

//--- Return the current value of the object (1) integer and (2) real property
   long              GetPropLongValue(const int property)               const { return this.m_long_prop_event[property][3];                           }
   double            GetPropDoubleValue(const int property)             const { return this.m_double_prop_event[property-this.m_long_prop_total][3];  }
//--- Return the change value of the controlled (1) integer and (2) real object property
   long              GetPropLongChangedValue(const int property)        const { return this.m_long_prop_event[property][4];                           }
   double            GetPropDoubleChangedValue(const int property)      const { return this.m_double_prop_event[property-this.m_long_prop_total][4];  }
//--- Return the flag of an (1) integer and (2) real property value change exceeding the increase value
   long              GetPropLongFlagINC(const int property)             const { return this.m_long_prop_event[property][5];                           }
   double            GetPropDoubleFlagINC(const int property)           const { return this.m_double_prop_event[property-this.m_long_prop_total][5];  }
//--- Return the flag of an (1) integer and (2) real property value change exceeding the decrease value
   long              GetPropLongFlagDEC(const int property)             const { return this.m_long_prop_event[property][6];                           }
   double            GetPropDoubleFlagDEC(const int property)           const { return this.m_double_prop_event[property-this.m_long_prop_total][6];  }
//--- Return the flag of an (1) integer and (2) real property value increase exceeding the control level
   long              GetPropLongFlagMORE(const int property)            const { return this.m_long_prop_event[property][7];                           }
   double            GetPropDoubleFlagMORE(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][7];  }
//--- Return the flag of an (1) integer and (2) real property value decrease being less than the control level
   long              GetPropLongFlagLESS(const int property)            const { return this.m_long_prop_event[property][8];                           }
   double            GetPropDoubleFlagLESS(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][8];  }
//--- Return the flag of an (1) integer and (2) real property being equal to the control level
   long              GetPropLongFlagEQUAL(const int property)           const { return this.m_long_prop_event[property][9];                           }
   double            GetPropDoubleFlagEQUAL(const int property)         const { return this.m_double_prop_event[property-this.m_long_prop_total][9];  }

//--- Reset the variables of (1) tracked and (2) controlled object data (can be reset in the descendants)
   void              ResetChangesParams(void);
   virtual void      ResetControlsParams(void);
//--- Add the (1) object event and (2) the object event reason to the list
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
   bool              EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value);
//--- Set/return the occurred event flag to the object data
   void              SetEvent(const bool flag)                       { this.m_is_event=flag;                   }
   bool              IsEvent(void)                             const { return this.m_is_event;                 }
//--- Return (1) the list of events, (2) the object event code and (3) the global error code
   CArrayObj        *GetListEvents(void)                             { return &this.m_list_events;             }
   int               GetEventCode(void)                        const { return this.m_event_code;               }
//--- Return (1) an event object and (2) a base event by its number in the list
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
   CBaseEvent       *GetEventBase(const int index);
//--- Return the number of (1) object events
   int               GetEventsTotal(void)                      const { return this.m_list_events.Total();      }
//--- Update the object data to search for changes (Calling from the descendants: CBaseObj::Refresh())
   virtual void      Refresh(void);
//--- Return an object event description
   string            EventDescription(const int property,
                                      const ENUM_BASE_EVENT_REASON reason,
                                      const int source,
                                      const string value,
                                      const string property_descr,
                                      const int digits);

//--- Data location in the magic number int value
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
      
//--- Set the ID of the (1) first group, (2) second group, (3) pending request to the magic number value
   void              SetGroupID1(const uchar group,uint &magic)            { magic &=0xFFF0FFFF; magic |= uint(this.ConvToXX(group,0)<<16);  }
   void              SetGroupID2(const uchar group,uint &magic)            { magic &=0xFF0FFFFF; magic |= uint(this.ConvToXX(group,1)<<16);  }
   void              SetPendReqID(const uchar id,uint &magic)              { magic &=0x00FFFFFF; magic |= (uint)id<<24;                      }
//--- Convert the value of 0 - 15 into the necessary uchar number bits (0 - lower, 1 - upper ones)
   uchar             ConvToXX(const uchar number,const uchar index)  const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));   }
//--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value
   ushort            GetMagicID(const uint magic)                    const { return ushort(magic & 0xFFFF);                                  }
   uchar             GetGroupID1(const uint magic)                   const { return uchar(magic>>16) & 0x0F;                                 }
   uchar             GetGroupID2(const uint magic)                   const { return uchar((magic>>16) & 0xF0)>>4;                            }
   uchar             GetPendReqID(const uint magic)                  const { return uchar(magic>>24) & 0xFF;                                 }

//--- Constructor
                     CBaseObjExt();
  };
//+------------------------------------------------------------------+

Drei neue Klassenvariablen werden jetzt in der neuen Basisobjektklasse aller Bibliotheksobjekte deklariert:

   bool              m_use_sound;                              // Flag of playing the sound set for an object
   bool              m_available;                              // Flag of using a descendant object in the program
   string            m_sound_name;                             // Object sound file name

Die entsprechenden Methoden zum Setzen und Zurückgeben der Werte der Variablen werden ebenfalls hinzugefügt:

//--- (1) Set and (2) return the name of the descendant object sound file
   void              SetSoundName(const string name)                 { this.m_sound_name=name;                 }
   string            GetSoundName(void)                        const { return this.m_sound_name;               }
//--- (1) Set and (2) return the flag of playing descendant object sounds
   void              SetUseSound(const bool flag)                    { this.m_use_sound=flag;                  }
   bool              IsUseSound(void)                          const { return this.m_use_sound;                }
//--- (1) Set and (2) return the flag of using the descendant object in the program
   void              SetAvailable(const bool flag)                   { this.m_available=flag;                  }
   bool              IsAvailable(void)                         const { return this.m_available;                }

Der Name der Sounddatei für das abgeleitete Objekt ermöglicht das Setzen (SetSoundName()) oder Empfangen (GetSoundName()) des Namens der Objekt-Sounddatei, die abgespielt werden kann, wenn es irgendeine Bedingung gibt, die die Objekteigenschaften steuert.

Neben der Tatsache, dass der Name dem Objekt zugewiesen werden kann, wird es möglich sein, die Erlaubnis zum Abspielen der Datei zu aktivieren/deaktivieren (SetUseSound()) und das Flag der gesetzten Erlaubnis zum Abspielen der Datei zu erhalten (IsUseSound()).

Also, was ist eigentlich "das Flag der Verwendung des abgeleiteten Objekts" und warum müssen wir es setzen und erhalten?
Zum Beispiel haben wir Symbol-Zeitreihen-Objekte für М5, М30, Н1 und D1. Irgendwann wollen wir aber nicht mehr mit der M5-Zeitreihe arbeiten. Durch Aktivieren/Deaktivieren des Flags können wir die Notwendigkeit der Verwaltung der Ereignisbibliothek regeln, z.B. die der neue Balken für die M5-Zeitreihe.
Das Vorhandensein eines solchen Flags im Basisobjekt aller Bibliotheksobjekte ermöglicht es uns, die Notwendigkeit, den Zustand der Eigenschaften solcher Objekte zu behandeln, flexibel zu steuern. Mit anderen Worten: Wenn Sie das Objekt im Programm handhaben und verwenden müssen, setzen Sie das Flag. Wenn Sie das nicht mehr tun müssen, entfernen Sie das Flag.

Natürlich wurden auch die Klassenkonstruktoren geändert — die in die neue Klasse verschobenen Variablen wurden entfernt, während alle Variablen in der neuen Klasse initialisiert wurden. Sehen Sie sich die Änderungen im Detail in den angehängten Dateien an.

Die Klassen, die nun vom erweiterten Basisobjekt CBaseObjExt abgeleitet werden:

  • CAccountsCollection in \MQL5\Include\DoEasy\Collections\AccountsCollection.mqh
  • CEventsCollection in \MQL5\Include\DoEasy\Collections\EventsCollection.mqh
    (ersetzt "CBaseObj::EventAdd" durch "CBaseObjExt::EventAdd")
  • CSymbolsCollection in \MQL5\Include\DoEasy\Collections\SymbolsCollection.mqh
  • CAccount in \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh
    (ersetzt "CBaseObj::Refresh()" durch "CBaseObjExt::Refresh()")
  • COrder in \MQL5\Include\DoEasy\Objects\Orders\Order.mqh
  • CPendRequest in \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh
    (ersetzt "return CBaseObj::GetMagicID" durch "return CBaseObjExt::GetMagicID",
     CBaseObj::GetGroupID1" mit "return CBaseObjExt::GetGroupID1" und CBaseObj::GetGroupID2" mit "return CBaseObjExt::GetGroupID2")
  • CSymbol in \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh
    (ersetzt "CBaseObj::Refresh()" durch "CBaseObjExt::Refresh()")
  • CTradeObj in \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh
    (die Variable bool m_use_sound sowie die Methoden SetUseSound() und IsUseSound() wurden entfernt — sie befinden sich jetzt in der Basisklasse)
  • CTrading in \MQL5\Include\DoEasy\Trading.mqh
    (die Variable bool m_use_sound und die Methode IsUseSounds() wurden entfernt — sie befinden sich jetzt in der Basisklasse)

Bevor wir die bereits erstellten Zeitreihen-Objektklassen verbessern, fügen wir die notwendigen Daten zur Datei Datas.mqh — die neue Makro-Substitution, die das Trennzeichen in der Zeichenkette der Liste der verwendeten Symbole und Zeitrahmen in den Programmeingaben angibt, die Aufzählung der Zeitrahmen-Betriebsarten, sowie neue Meldungsindizes und Meldungstexte, die den deklarierten Indizes entsprechen:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define INPUT_SEPARATOR                (",")          // Separator in the inputs string
#define TOTAL_LANG                     (2)            // Number of used languages
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                              // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                         // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                   // Work with the full symbol list
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work with the current timeframe only
   TIMEFRAMES_MODE_LIST,                              // Work with the specified timeframe list
   TIMEFRAMES_MODE_ALL                                // Work with the full timeframe list
  };
//+------------------------------------------------------------------+

   MSG_LIB_SYS_ERROR_EMPTY_SYMBOLS_STRING,            // Error. Predefined symbols string empty, to be used
   MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY,        // Failed to prepare array of used symbols. Error 
   MSG_LIB_SYS_ERROR_EMPTY_PERIODS_STRING,            // Error. The string of predefined periods is empty and is to be used
   MSG_LIB_SYS_FAILED_PREPARING_PERIODS_ARRAY,        // Failed to prepare array of used periods. Error 
   MSG_LIB_SYS_INVALID_ORDER_TYPE,                    // Invalid order type:

...

//--- CTimeSeries
   MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL,              // First, set a symbol using SetSymbol()
   MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME,            // Unknown timeframe
   MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ,             // Failed to receive the timeseries object
   MSG_LIB_TEXT_TS_REQUIRED_HISTORY_DEPTH,            // Requested history depth
   MSG_LIB_TEXT_TS_ACTUAL_DEPTH,                      // Actual history depth
   MSG_LIB_TEXT_TS_AMOUNT_HISTORY_DATA,               // Created historical data
   MSG_LIB_TEXT_TS_HISTORY_BARS,                      // Number of history bars on the server
   MSG_LIB_TEXT_TS_TEXT_SYMBOL_TIMESERIES,            // Symbol timeseries
   MSG_LIB_TEXT_TS_TEXT_TIMESERIES,                   // Timeseries
   MSG_LIB_TEXT_TS_TEXT_REQUIRED,                     // Requested
   MSG_LIB_TEXT_TS_TEXT_ACTUAL,                       // Actual
   MSG_LIB_TEXT_TS_TEXT_CREATED,                      // Created
   MSG_LIB_TEXT_TS_TEXT_HISTORY_BARS,                 // On the server
   MSG_LIB_TEXT_TS_TEXT_SYMBOL_FIRSTDATE,             // The very first date by a period symbol
   MSG_LIB_TEXT_TS_TEXT_SYMBOL_LASTBAR_DATE,          // Time of opening the last bar by period symbol
   MSG_LIB_TEXT_TS_TEXT_SYMBOL_SERVER_FIRSTDATE,      // The very first date in history by a server symbol
   MSG_LIB_TEXT_TS_TEXT_SYMBOL_TERMINAL_FIRSTDATE,    // The very first date in history by a symbol in the client terminal
  
};
//+------------------------------------------------------------------+

...

   {"Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols is empty, the Symbol will be used: "},
   {"Не удалось подготовить массив используемых символов. Ошибка ","Failed to create an array of used symbols. Error "},
   {"Ошибка. Строка предопределённых периодов пустая, будет использоваться ","Error. String of predefined periods is empty, the Period will be used: "},
   {"Не удалось подготовить массив используемых периодов. Ошибка ","Failed to create an array of used periods. Error "},
   {"Неправильный тип ордера: ","Invalid order type: "},

...
   {"Сначала нужно установить символ при помощи SetSymbol()","First you need to set the Symbol using SetSymbol()"},
   {"Неизвестный таймфрейм","Unknown timeframe"},
   {"Не удалось получить объект-таймсерию ","Failed to get timeseries object "},
   {"Запрошенная глубина истории: ","Required history depth: "},
   {"Фактическая глубина истории: ","Actual history depth: "},
   {"Создано исторических данных: ","Total historical data created: "},
   {"Баров истории на сервере: ","Server history Bars number: "},
   {"Таймсерия символа","Symbol time series"},
   {"Таймсерия","Timeseries"},
   {"Запрошено","Required"},
   {"Фактически","Actual"},
   {"Создано","Created"},
   {"На сервере","On server"},
   {"Самая первая дата по символу-периоду","The very first date for the symbol-period"},
   {"Время открытия последнего бара по символу-периоду","Open time of the last bar of the symbol-period"},
   {"Самая первая дата в истории по символу на сервере","The very first date in the history of the symbol on the server"},
   {"Самая первая дата в истории по символу в клиентском терминале","The very first date in the history of the symbol in the client terminal"},
  
};
//+---------------------------------------------------------------------+

Wir haben alle Daten aufbereitet, die für Verbesserungen in den zuvor entwickelten Zeitreihenklassen und für die Erstellung der Sammlung aller Zeitreihen erforderlich sind.

Lassen Sie uns die Objektklasse der Symbolzeitreihen für den einzelnen Zeitrahmen CSeries verbessern.
Wir fügen im 'private' Teil der Klasse vier neue Variablen und eine Methode zum Setzen von Zeitreihendaten hinzu:

//+------------------------------------------------------------------+
//| Timeseries class                                                 |
//+------------------------------------------------------------------+
class CSeries : public CBaseObj
  {
private:
   ENUM_TIMEFRAMES   m_timeframe;                                       // Timeframe
   string            m_symbol;                                          // Symbol
   string            m_period_description;                              // Timeframe string description
   datetime          m_firstdate;                                       // The very first date by a period symbol at the moment
   datetime          m_lastbar_date;                                    // Time of opening the last bar by period symbol
   uint              m_amount;                                          // Amount of applied timeseries data
   uint              m_required;                                        // Required 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
//--- Set the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol
   void              SetServerDate(void)
                       {
                        this.m_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE);
                        this.m_lastbar_date=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE);
                       }
public:

Wir schreiben die Beschreibung des Zeitraums des Zeitreihendiagramms in die Variable m_period_description unmittelbar beim Anlegen des Klassenobjekts im Konstruktor und in den Methoden, die einen Zeitrahmen für das Zeitreihenobjekt setzen. Dies geschieht, um einen konstanten Zugriff auf die Funktion TimeframeDescription() aus der Servicefunktionsdatei DELib.mqh der Bibliothek zu vermeiden — die Funktion sucht nach einer Teilzeichenkette in der Beschreibung der Zeitrahmen-Zeichenkette aus der ENUM_TIMEFRAMES-Enumeration, die die Ausführung behindert. Daher wäre es besser, "langsame" Funktionen sofort bei der Konstruktion von Objekten auszuführen, falls die Daten nicht oder nur selten auf Anforderung eines Programms geändert werden sollen.

Die Variable m_firstdate speichert das allererste Datum durch eine Symbolperiode zu dem Zeitpunkt, der durch die Funktion SeriesInfoInteger() mit der Eigenschafts-ID SERIES_FIRSTDATE ermittelt wurde.
Die Variable m_lastbar_date speichert die Eröffnungszeit es letzten Balkens der Symbolperiode, die durch die Funktion SeriesInfoInteger() mit der Eigenschafts-ID SERIES_LASTBAR_DATE ermittelt wurde.
Beide Variablen werden durch Aufruf der Methode SetServerDate() nur zum Zeitpunkt der Erstellung eines Klassenobjekts oder der Änderung von Daten auf einem neuen Balken gesetzt, sowie beim Setzen eines neuen Symbols oder Zeitrahmens für das Zeitreihenobjekt.

Die Variable m_erforderlich dient dazu, die erforderliche (zuletzt angeforderte) Anzahl der verwendeten Zeitreihendaten zu speichern. Bei der Anforderung der erforderlichen Anzahl von Zeitreihentakten kann sich herausstellen, dass die angeforderte Datenmenge für die Erstellung der Zeitreihe nicht auf dem Server vorhanden ist. In diesem Fall wird die Zeitreihe in dem Umfang erstellt, die dem der auf dem Server verfügbaren Historie entspricht. Die Variable speichert immer die zuletzt angeforderte Datenmenge, unabhängig davon, wie viele Daten tatsächlich beschafft und erstellt wurden. Wenn man bedenkt, dass wir das Konzept der "angeforderten Daten" verwenden, hat sich der Name der Methoden in der Klasse, die "Amount" (Umfang) enthält, geändert — jetzt wurde er durch "Required" (verlangt) ersetzt.

In der 'public' Klassensektion wurden auch neuen Methoden hinzugefügt:

public:
//--- Return (1) oneself and (2) the timeseries list
   CSeries          *GetObject(void)                                    { return &this;         }
   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, (2) timeframe, (3) symbol and timeframe, (4) amount of applied timeseries data
   void              SetSymbol(const string symbol);
   void              SetTimeframe(const ENUM_TIMEFRAMES timeframe);
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe);
   bool              SetRequiredUsedData(const uint required,const uint rates_total);

//--- Return (1) symbol, (2) timeframe, number of (3) used and (4) requested timeseries data,
//--- (5) number of bars in the timeseries, (6) the very first date, (7) time of opening the last bar by a symbol period,
//--- new bar flag with (8) automatic and (9) manual time management
   string            Symbol(void)                                          const { return this.m_symbol;                            }
   ENUM_TIMEFRAMES   Timeframe(void)                                       const { return this.m_timeframe;                         }
   ulong             AvailableUsedData(void)                               const { return this.m_amount;                            }
   ulong             RequiredUsedData(void)                                const { return this.m_required;                          }
   ulong             Bars(void)                                            const { return this.m_bars;                              }
   datetime          FirstDate(void)                                       const { return this.m_firstdate;                         }
   datetime          LastBarDate(void)                                     const { return this.m_lastbar_date;                      }
   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, as well as (3) the real list size
   CBar             *GetBarByListIndex(const uint index);
   CBar             *GetBarBySeriesIndex(const uint index);
   int               DataTotal(void)                                       const { return this.m_list_series.Total();               }
//--- 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);

//--- (1) Set and (2) return the sound of a sound file of the "New bar" timeseries event
   void              SetNewBarSoundName(const string name)                       { this.m_new_bar_obj.SetSoundName(name);           }
   string            NewBarSoundName(void)                                 const { return this.m_new_bar_obj.GetSoundName();        }

//--- Save the new bar time during the manual time management
   void              SaveNewBarTime(const datetime time)                         { this.m_new_bar_obj.SaveNewBarTime(time);         }
//--- Synchronize symbol and timeframe data with server data
   bool              SyncData(const uint required,const uint rates_total);
//--- (1) Create and (2) update the timeseries list
   int               Create(const uint required=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);
                             
//--- Return the timeseries name
   string            Header(void);
//--- Display (1) the timeseries description and (2) the brief timeseries description in the journal
   void              Print(void);
   void              PrintShort(void);


//--- Constructors
                     CSeries(void);
                     CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0);
  };
//+------------------------------------------------------------------+

Die Methode GetObject() gibt den Zeiger auf das gesamte Zeitreihenobjekt an das Steuerprogramm zurück. Sie ermöglicht es, das gesamte Zeitreihenobjekt zu erhalten und mit ihm in einem nutzerdefinierten Programm zu arbeiten.

Die Methode RequiredUsedData() gibt den Wert der oben erwähnten Variablen m_required an das aufrufende Programm zurück.

Die Methoden FirstDate() und LastBarDate() geben die Werte der Variablen m_firstdate und m_lastbar_date zurück, die ebenfalls oben beschrieben wurden.  

Die Methode SetNewBarSoundName() setzt den Sounddateinamen für das Objekt CNewBarObj "New bar", das Teil des Zeitreihenobjekts ist.
Die Methode NewBarSoundName() gibt den Sounddateinamen zurück, der dem CNewBarObjekt "New bar" zugeordnet ist, das Teil des Zeitreihenobjekts ist.
Die Methode erlaubt es, den Ton einem beliebigen Zeitreihenobjekt zuzuordnen. Der Ton soll abgespielt werden, wenn das Ereignis "New bar" erkannt wird.

Die Methode Header() erzeugt und liefert einen kurzen Namen des Zeitreihenobjekts:

//+------------------------------------------------------------------+
//| Return the timeseries name                                       |
//+------------------------------------------------------------------+
string CSeries::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_TS_TEXT_TIMESERIES)+" \""+this.m_symbol+"\" "+this.m_period_description;
  }
//+------------------------------------------------------------------+

Die textliche Beschreibung der Zeitreihe wird von der Methode in der folgenden Form zurückgegeben

Timeseries "SYMBOL" TIMEFRAME_DESCRIPTION

Zum Beispiel:

Timeseries "AUDUSD" M15

Die Methode Print() zeigt die vollständige Zeitreihenbeschreibung im Journal:

//+------------------------------------------------------------------+
//| Display the timeseries description in the journal                |
//+------------------------------------------------------------------+
void CSeries::Print(void)
  {
   string txt=
     (
      CMessage::Text(MSG_LIB_TEXT_TS_REQUIRED_HISTORY_DEPTH)+(string)this.RequiredUsedData()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(string)this.AvailableUsedData()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_AMOUNT_HISTORY_DATA)+(string)this.DataTotal()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_HISTORY_BARS)+(string)this.Bars()
     );
   ::Print(this.Header(),": ",txt);
  }
//+------------------------------------------------------------------+

Die Zeitreihendaten werden in das Journal in folgender Form geschrieben

HEADER: HISTORY_DEPTH: XXXX, ACTUAL_DEPTH: XXXX, AMOUNT_HISTORY_DATA: XXXX, HISTORY_BARS: XXXX

Zum Beispiel:

Timeseries "AUDUSD" W1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 1400

Timeseries "AUDUSD" MN1: Requested history depth: 1000, Actual history depth: 322, Historical data created: 322, History bars on the server: 322

Die Methode PrintShort() zeigt die kurze Zeitreihenbeschreibung in der Zeitschrift an:

//+------------------------------------------------------------------+
//| Display a short timeseries description in the journal            |
//+------------------------------------------------------------------+
void CSeries::PrintShort(void)
  {
   string txt=
     (
      CMessage::Text(MSG_LIB_TEXT_TS_TEXT_REQUIRED)+": "+(string)this.RequiredUsedData()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_TEXT_ACTUAL)+": "+(string)this.AvailableUsedData()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_TEXT_CREATED)+": "+(string)this.DataTotal()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_TEXT_HISTORY_BARS)+": "+(string)this.Bars()
     );
   ::Print(this.Header(),": ",txt);
  }
//+------------------------------------------------------------------+

Die Zeitreihendaten werden in das Journal in folgender Form geschrieben

HEADER: REQUIRED: XXXX, ACTUAL: XXXX, CREATED: XXXX, HISTORY_BARS: XXXX

Zum Beispiel:

Timeseries "USDJPY" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2562

Timeseries "USDJPY" MN1: Requested: 1000, Actual: 589, Created: 589, On the server: 589

Wir fügen das Speichern der Zeitrahmenbeschreibung und Setzen der Zeitreihendaten zu beiden Klassenkonstruktoren hinzu:

//+------------------------------------------------------------------+
//| Constructor 1 (current symbol and period timeseries)             |
//+------------------------------------------------------------------+
CSeries::CSeries(void) : m_bars(0),m_amount(0),m_required(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(NULL,(ENUM_TIMEFRAMES)::Period());
   this.m_period_description=TimeframeDescription(this.m_timeframe);
   this.SetServerDate();
  }
//+------------------------------------------------------------------+
//| Constructor 2 (specified symbol and period timeseries)           |
//+------------------------------------------------------------------+
CSeries::CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0) : m_bars(0), m_amount(0),m_required(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.SetRequiredUsedData(required,0);
   this.m_period_description=TimeframeDescription(this.m_timeframe);
   this.SetServerDate();
  }
//+------------------------------------------------------------------+

In der Methode zum Setzen von Symbolen fügen wir die Prüfung für dasselbe Symbol hinzu und das Setzen von Zeitreihendaten:

//+------------------------------------------------------------------+
//| Set a symbol                                                     |
//+------------------------------------------------------------------+
void CSeries::SetSymbol(const string symbol)
  {
   if(this.m_symbol==symbol)
      return;
   this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);
   this.m_new_bar_obj.SetSymbol(this.m_symbol);
   this.SetServerDate();
  }
//+------------------------------------------------------------------+

Wenn hier ein bereits im Objekt verwendetes Symbol an die Methode übergeben wird, muss nichts gesetzt werden — verlassen Sie einfach die Methode.

In der Methode zum Setzen des Zeitrahmens fügen wir das Prüfen für den gleichen Zeitrahmen hinzu und das Setzen der Zeitreihendaten:

//+------------------------------------------------------------------+
//| Set a timeframe                                                  |
//+------------------------------------------------------------------+
void CSeries::SetTimeframe(const ENUM_TIMEFRAMES timeframe)
  {
   if(this.m_timeframe==timeframe)
      return;
   this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe);
   this.m_new_bar_obj.SetPeriod(this.m_timeframe);
   this.m_period_description=TimeframeDescription(this.m_timeframe);
   this.SetServerDate();
  }
//+------------------------------------------------------------------+

Wenn hier ein bereits im Objekt verwendeter Zeitrahmen an die Methode übergeben wird, muss nichts eingestellt werden — verlassen Sie einfach die Methode.

Lassen Sie uns die Methode zum Setzen eines Symbols und Zeitrahmens ändern:

//+------------------------------------------------------------------+
//| Set a symbol and timeframe                                       |
//+------------------------------------------------------------------+
void CSeries::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   if(this.m_symbol==symbol && this.m_timeframe==timeframe)
      return;
   this.SetSymbol(symbol);
   this.SetTimeframe(timeframe);
  }
//+------------------------------------------------------------------+

Hier gilt: Wenn das gleiche Symbol und der gleiche Zeitrahmen an die Methode übergeben werden, dann muss nichts geändert werden — wir verlassen die Methode einfach.
Als Nächstes rufen wir die Methoden zum Setzen eines Symbols und Zeitrahmen auf.

Bei der Methode zur Einstellung der erforderlichen Zeitreihen-Historietiefe speichern wir die angeforderte Historietiefe:

//+------------------------------------------------------------------+
//| Set the number of required data                                  |
//+------------------------------------------------------------------+
bool CSeries::SetRequiredUsedData(const uint required,const uint rates_total)
  {
   this.m_required=(required==0 ? SERIES_DEFAULT_BARS_COUNT : required);
//--- Set the number of available timeseries bars
   this.m_bars=(uint)
     (
      //--- 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 : 
      ::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_BARS_COUNT)
     );
//--- If managed to set the number of available history, set the amount of data in the list:
   if(this.m_bars>0)
     {
      //--- if zero 'required' 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 'required' value is passed,
      //--- use either the 'required' value or the number of available history bars - the least one of them
      this.m_amount=(required==0 ? ::fmin(SERIES_DEFAULT_BARS_COUNT,this.m_bars) : ::fmin(required,this.m_bars));
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Wenn für den erforderlichen Wert Null übergeben wird, wird die Anforderungshistorie auf die Standardanzahl (1000 Balken), die durch die Makro-Substitution SERIES_DEFAULT_BARS_COUNT in der Datei Define.mqh angegeben wird gesetzt, andernfalls der übergebene Wert.

Die Methode zur Aktualisierung der Zeitreihe erhält die Prüfung für die Verwendung der Zeitreihe in einem Programm. Wenn die Zeitreihe nicht verwendet wird, braucht nichts aktualisiert zu werden:

//+------------------------------------------------------------------+
//| 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)
  {
//--- If the timeseries is not used, exit
   if(!this.m_available)
      return;
   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;
        }
      //--- Write the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol 
      this.SetServerDate();
      //--- if the timeseries exceeds the requested number of bars, remove the earliest bar
      if(this.m_list_series.Total()>(int)this.m_required)
         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]);
  }
//+------------------------------------------------------------------+

Zusätzlich gilt: Wenn ein neuer Balken in der Zeitreihe erscheint, werden die Zeitreihen-Daten aktualisiert.

Dies sind die wichtigsten Änderungen in der Klasse. Geringfügige Änderungen in den Namen der Methoden bespreche ich hier nicht. Die vollständige Klassenliste finden Sie in den angehängten Dateien.

Damit ist die Arbeit an der Klasse CSeries im derzeitigen Stadium abgeschlossen.

Lassen Sie uns die Klasse CTimeSeries abschließen, die die Objekte CSeries für alle möglichen Diagrammperioden eines einzelnen Symbols enthält.

Die Klassenauflistung verfügt über die Methoden IndexTimeframe() und TimeframeByIndex(), um einen Zeitrahmenindex in der Liste zu erhalten, der die Zeitreihen des entsprechenden Diagramms und die Zeitrahmenperioden durch den Listenindex speichert. Die Methoden sind recht spezifisch, da sie auf der Tatsache basieren, dass der Index des minimal möglichen Zeitrahmens (PERIOD_M1) im Nullindex der Liste enthalten ist. Innerhalb der Enumeration ENUM_TIMEFRAMES ist der Index М1 bereits gleich eins, da der Index Null die Konstante PERIOD_CURRENT enthält. Mit anderen Worten, alle Indizes sind relativ zu Null um 1 versetzt.
Wir benötigen Funktionen, die den Index der Chartperiodenkonstante innerhalb der Enumeration ENUM_TIMEFRAMES zurückgeben und umgekehrt die Konstante aus der Enumeration um die Chartperiode zurückgeben. Wir werden die entsprechenden Funktionen in der Datei der Servicefunktionen erstellen — IndexEnumTimeframe() und TimeframeByEnumIndex(), während in der Auflistung der Klasse CTimeSeries die Implementierung der Methoden IndexTimeframe() und TimeframeByIndex() entfernt wird, indem der Aufruf der Funktionen IndexEnumTimeframe() und TimeframeByEnumIndex() mit dem Offset von eins zum Klassenkörper hinzugefügt wird.

Hinzufügen von drei Funktionen zur Datei DELib.mqh der Dienstfunktionen:

//+------------------------------------------------------------------+
//| Return the timeframe index in the ENUM_TIMEFRAMES enumeration    |
//+------------------------------------------------------------------+
char IndexEnumTimeframe(ENUM_TIMEFRAMES timeframe)
  {
   int statement=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
   switch(statement)
     {
      case PERIOD_M1    :  return 1;
      case PERIOD_M2    :  return 2;
      case PERIOD_M3    :  return 3;
      case PERIOD_M4    :  return 4;
      case PERIOD_M5    :  return 5;
      case PERIOD_M6    :  return 6;
      case PERIOD_M10   :  return 7;
      case PERIOD_M12   :  return 8;
      case PERIOD_M15   :  return 9;
      case PERIOD_M20   :  return 10;
      case PERIOD_M30   :  return 11;
      case PERIOD_H1    :  return 12;
      case PERIOD_H2    :  return 13;
      case PERIOD_H3    :  return 14;
      case PERIOD_H4    :  return 15;
      case PERIOD_H6    :  return 16;
      case PERIOD_H8    :  return 17;
      case PERIOD_H12   :  return 18;
      case PERIOD_D1    :  return 19;
      case PERIOD_W1    :  return 20;
      case PERIOD_MN1   :  return 21;
      default           :  Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME)); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+
//| Return the timeframe by the ENUM_TIMEFRAMES enumeration index    |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES TimeframeByEnumIndex(const uchar index)
  {
   if(index==0) return(ENUM_TIMEFRAMES)Period();
   switch(index)
     {
      case 1   :  return PERIOD_M1;
      case 2   :  return PERIOD_M2;
      case 3   :  return PERIOD_M3;
      case 4   :  return PERIOD_M4;
      case 5   :  return PERIOD_M5;
      case 6   :  return PERIOD_M6;
      case 7   :  return PERIOD_M10;
      case 8   :  return PERIOD_M12;
      case 9   :  return PERIOD_M15;
      case 10  :  return PERIOD_M20;
      case 11  :  return PERIOD_M30;
      case 12  :  return PERIOD_H1;
      case 13  :  return PERIOD_H2;
      case 14  :  return PERIOD_H3;
      case 15  :  return PERIOD_H4;
      case 16  :  return PERIOD_H6;
      case 17  :  return PERIOD_H8;
      case 18  :  return PERIOD_H12;
      case 19  :  return PERIOD_D1;
      case 20  :  return PERIOD_W1;
      case 21  :  return PERIOD_MN1;
      default  :  Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_DATAS),"... ",CMessage::Text(MSG_SYM_STATUS_INDEX),": ",(string)index); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+
//| Return the timeframe by its description                          |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES TimeframeByDescription(const string timeframe)
  {
   return
     (
      timeframe=="M1"   ?  PERIOD_M1   :
      timeframe=="M2"   ?  PERIOD_M2   :
      timeframe=="M3"   ?  PERIOD_M3   :
      timeframe=="M4"   ?  PERIOD_M4   :
      timeframe=="M5"   ?  PERIOD_M5   :
      timeframe=="M6"   ?  PERIOD_M6   :
      timeframe=="M10"  ?  PERIOD_M10  :
      timeframe=="M12"  ?  PERIOD_M12  :
      timeframe=="M15"  ?  PERIOD_M15  :
      timeframe=="M20"  ?  PERIOD_M20  :
      timeframe=="M30"  ?  PERIOD_M30  :
      timeframe=="H1"   ?  PERIOD_H1   :
      timeframe=="H2"   ?  PERIOD_H2   :
      timeframe=="H3"   ?  PERIOD_H3   :
      timeframe=="H4"   ?  PERIOD_H4   :
      timeframe=="H6"   ?  PERIOD_H6   :
      timeframe=="H8"   ?  PERIOD_H8   :
      timeframe=="H12"  ?  PERIOD_H12  :
      timeframe=="D1"   ?  PERIOD_D1   :
      timeframe=="W1"   ?  PERIOD_W1   :
      timeframe=="MN1"  ?  PERIOD_MN1  :
      PERIOD_CURRENT
     );
  }
//+------------------------------------------------------------------+

Entfernen Sie in der Klassendatei CTimeSeries die Implementierung der Methoden IndexTimeframe() und TimeframeByIndex() aus dem Code:

//+------------------------------------------------------------------+
//| Return the timeframe index in the list                           |
//+------------------------------------------------------------------+
char CTimeSeries::IndexTimeframe(ENUM_TIMEFRAMES timeframe) const
  {
   int statement=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
   switch(statement)
     {
      case PERIOD_M1    :  return 0;
      case PERIOD_M2    :  return 1;
      case PERIOD_M3    :  return 2;
      case PERIOD_M4    :  return 3;
      case PERIOD_M5    :  return 4;
      case PERIOD_M6    :  return 5;
      case PERIOD_M10   :  return 6;
      case PERIOD_M12   :  return 7;
      case PERIOD_M15   :  return 8;
      case PERIOD_M20   :  return 9;
      case PERIOD_M30   :  return 10;
      case PERIOD_H1    :  return 11;
      case PERIOD_H2    :  return 12;
      case PERIOD_H3    :  return 13;
      case PERIOD_H4    :  return 14;
      case PERIOD_H6    :  return 15;
      case PERIOD_H8    :  return 16;
      case PERIOD_H12   :  return 17;
      case PERIOD_D1    :  return 18;
      case PERIOD_W1    :  return 19;
      case PERIOD_MN1   :  return 20;
      default           :  ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME)); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+
//| Return a timeframe by index                                      |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTimeSeries::TimeframeByIndex(const uchar index) const
  {
   switch(index)

     {
      case 0   :  return PERIOD_M1;
      case 1   :  return PERIOD_M2;
      case 2   :  return PERIOD_M3;
      case 3   :  return PERIOD_M4;
      case 4   :  return PERIOD_M5;
      case 5   :  return PERIOD_M6;
      case 6   :  return PERIOD_M10;
      case 7   :  return PERIOD_M12;
      case 8   :  return PERIOD_M15;
      case 9   :  return PERIOD_M20;
      case 10  :  return PERIOD_M30;
      case 11  :  return PERIOD_H1;
      case 12  :  return PERIOD_H2;
      case 13  :  return PERIOD_H3;
      case 14  :  return PERIOD_H4;
      case 15  :  return PERIOD_H6;
      case 16  :  return PERIOD_H8;
      case 17  :  return PERIOD_H12;
      case 18  :  return PERIOD_D1;
      case 19  :  return PERIOD_W1;
      case 20  :  return PERIOD_MN1;
      default  :  ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_DATAS),"... ",CMessage::Text(MSG_SYM_STATUS_INDEX),": ",(string)index); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+

Anstelle dieser Methoden rufen wir nun die Funktionen aus der Datei DELib.mqh der Dienstfunktionen auf:

//--- Return (1) the timeframe index in the list and (2) the timeframe by the list index
   char              IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1;                            }
   ENUM_TIMEFRAMES   TimeframeByIndex(const uchar index)             const { return TimeframeByEnumIndex(uchar(index+1));                       }

Da die Liste der Klassenzeitreihen alle möglichen Chartperioden enthält, enthält der Listenindex Null die Zeitreihen des Zeitrahmens von M1. Die Enumeration ENUM_TIMEFRAMES enthält beim Index Null PERIOD_CURRENT und bei Eins М1, daher müssen wir den Indexwert um 1 verschieben, um den richtigen Index in der Liste zu erhalten. Das ist genau das, was wir hier tun.

Der 'private' Klassenabschnitt erhält zwei Klassenvariablen zum Setzen des allerersten Datums in der Historie auf dem Server und im Terminal, sowie die Methode zum Setzen der Werte dieser Daten in den Variablen:

//+------------------------------------------------------------------+
//| Symbol timeseries class                                          |
//+------------------------------------------------------------------+
class CTimeSeries : public CBaseObj
  {
private:
   string            m_symbol;                                             // Timeseries symbol
   CArrayObj         m_list_series;                                        // List of timeseries by timeframes
   datetime          m_server_firstdate;                                   // The very first date in history by a server symbol
   datetime          m_terminal_firstdate;                                 // The very first date in history by a symbol in the client terminal
//--- Return (1) the timeframe index in the list and (2) the timeframe by the list index
   char              IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1;                            }
   ENUM_TIMEFRAMES   TimeframeByIndex(const uchar index)             const { return TimeframeByEnumIndex(uchar(index+1));                       }
//--- Set the very first date in history by symbol on the server and in the client terminal
   void              SetTerminalServerDate(void)
                       {
                        this.m_server_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_SERVER_FIRSTDATE);
                        this.m_terminal_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_TERMINAL_FIRSTDATE);
                       }
public:

Die Variable m_server_firstdate speichert das allererste Datum in der Historie des Symbols auf dem Server, das durch die Funktion SeriesInfoInteger() mit der Eigenschafts-ID SERIES_SERVER_FIRSTDATE erhalten wird.

Die Variable m_terminal_firstdate speichert die allerersten Daten in der Historie eines Symbols im Terminal, die durch die Funktion SeriesInfoInteger() mit der Eigenschafts-ID SERIES_TERMINAL_FIRSTDATE abgerufen werden.

Sechs neue Methoden und der parametrische Konstruktor wurden in der Klassensektion 'public' hinzugefügt:

public:
//--- Return (1) oneself, (2) the full list of timeseries, (3) specified timeseries object and (4) timeseries object by index
   CTimeSeries      *GetObject(void)                                       { return &this;                                                      }
   CArrayObj        *GetListSeries(void)                                   { return &this.m_list_series;                                        }
   CSeries          *GetSeries(const ENUM_TIMEFRAMES timeframe)            { return this.m_list_series.At(this.IndexTimeframe(timeframe));      }
   CSeries          *GetSeriesByIndex(const uchar index)                   { return this.m_list_series.At(index);                               }
//--- Set/return timeseries symbol
   void              SetSymbol(const string symbol)                        { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);  }
   string            Symbol(void)                                    const { return this.m_symbol;                                              }
//--- Set the history depth (1) of a specified timeseries and (2) of all applied symbol timeseries
   bool              SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool              SetRequiredAllUsedData(const uint required=0,const int rates_total=0);
//--- Return the flag of data synchronization with the server data of the (1) specified timeseries, (2) all timeseries
   bool              SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool              SyncAllData(const uint required=0,const int rates_total=0);
//--- Return the very first date in history by symbol (1) on the server and (2) in the client terminal
   datetime          ServerFirstDate(void)                           const { return this.m_server_firstdate;                                    }
   datetime          TerminalFirstDate(void)                         const { return this.m_terminal_firstdate;                                  }
//--- Create (1) the specified timeseries list and (2) all timeseries lists
   bool              Create(const ENUM_TIMEFRAMES timeframe,const uint required=0);
   bool              CreateAll(const uint required=0);
//--- Update (1) the specified timeseries list and (2) all timeseries lists
   void              Refresh(const ENUM_TIMEFRAMES timeframe,
                             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);
   void              RefreshAll(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);
//--- Compare CTimeSeries objects (by symbol)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Display (1) description and (2) short symbol timeseries description in the journal
   void              Print(const bool created=true);
   void              PrintShort(const bool created=true);
   
//--- Constructors
                     CTimeSeries(void){;}
                     CTimeSeries(const string symbol);
  };
//+------------------------------------------------------------------+

Die Methode GetObject() gibt den Zeiger auf das Klassenobjekt zurück. Sie ermöglicht es, das Klassenobjekt der Zeitreihen eines Symbols zu empfangen und mit ihm in einem nutzerdefinierten Programm zu arbeiten.

Die Methoden ServerFirstDate() und TerminalFirstDate() geben die Werte der oben beschriebenen Variablen m_server_firstdate und m_terminal_firstdate zurück.

Die virtuelle Methode Compare() erlaubt den Vergleich zweier Zeitreihenobjekte anhand des Namens des Zeitreihensymbols:

//+------------------------------------------------------------------+
//| Compare CTimeSeries objects                                      |
//+------------------------------------------------------------------+
int CTimeSeries::Compare(const CObject *node,const int mode=0) const
  {
   const CTimeSeries *compared_obj=node;
   return(this.Symbol()>compared_obj.Symbol() ? 1 : this.Symbol()<compared_obj.Symbol() ? -1 : 0);
  }
//+------------------------------------------------------------------+

Die Methode gibt Null zurück, wenn die Symbole von zwei verglichenen Zeitreihenobjekten gleich sind. Andernfalls gibt sie +/- 1 zurück. Die Methode ist in der Klasse CObject der Standardbibliothek deklariert und sollte in ihren Ableitungen neu definiert werden.

Die Methode Print() zeigt die vollständigen Beschreibungen aller Symbolzeitreihen im Journal an:

//+------------------------------------------------------------------+
//| Display descriptions of all symbol timeseries in the journal     |
//+------------------------------------------------------------------+
void CTimeSeries::Print(const bool created=true)
  {
   ::Print(CMessage::Text(MSG_LIB_TEXT_TS_TEXT_SYMBOL_TIMESERIES)," ",this.m_symbol,": ");
   for(int i=0;i<this.m_list_series.Total();i++)
     {
      CSeries *series=this.m_list_series.At(i);
      if(series==NULL || (created && series.DataTotal()==0))
         continue;
      series.Print();
     }
  }
//+------------------------------------------------------------------+

Das Journal zeigt die Liste aller erstellten (created=true) oder erstellten und deklarierten (created=false) Symbolzeitreihen im entsprechenden Format an, z. B.

created=true:

GBPUSD symbol timeseries: 
Timeseries "GBPUSD" M1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6296
Timeseries "GBPUSD" M5: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3921
Timeseries "GBPUSD" M15: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3227
Timeseries "GBPUSD" M30: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3053
Timeseries "GBPUSD" H1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6187
Timeseries "GBPUSD" H4: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5298
Timeseries "GBPUSD" D1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5288
Timeseries "GBPUSD" W1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 1398
Timeseries "GBPUSD" MN1: Requested history depth: 1000, Actual history depth: 321, Historical data created: 321, History bars on the server: 321

created=false:

GBPUSD symbol timeseries: 
Timeseries "GBPUSD" M1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6296
Timeseries "GBPUSD" M2: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5483
Timeseries "GBPUSD" M3: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 4616
Timeseries "GBPUSD" M4: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 4182
Timeseries "GBPUSD" M5: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3921
Timeseries "GBPUSD" M6: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3748
Timeseries "GBPUSD" M10: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3401
Timeseries "GBPUSD" M12: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3314
Timeseries "GBPUSD" M15: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3227
Timeseries "GBPUSD" M20: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3140
Timeseries "GBPUSD" M30: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3053
Timeseries "GBPUSD" H1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6187
Timeseries "GBPUSD" H2: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5047
Timeseries "GBPUSD" H3: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5031
Timeseries "GBPUSD" H4: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5298
Timeseries "GBPUSD" H6: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 6324
Timeseries "GBPUSD" H8: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 6301
Timeseries "GBPUSD" H12: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5762
Timeseries "GBPUSD" D1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5288
Timeseries "GBPUSD" W1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 1398
Timeseries "GBPUSD" MN1: Requested history depth: 1000, Actual history depth: 321, Historical data created: 321, History bars on the server: 321

Die Methode PrintShort() zeigt kurze Beschreibungen aller Symbolzeitreihen im Journal an:

//+-------------------------------------------------------------------+
//| Display short descriptions of all symbol timeseries in the journal|
//+-------------------------------------------------------------------+
void CTimeSeries::PrintShort(const bool created=true)
  {
   ::Print(CMessage::Text(MSG_LIB_TEXT_TS_TEXT_SYMBOL_TIMESERIES)," ",this.m_symbol,": ");
   for(int i=0;i<this.m_list_series.Total();i++)
     {
      CSeries *series=this.m_list_series.At(i);
      if(series==NULL || (created && series.DataTotal()==0))
         continue;
      series.PrintShort();
     }
  }
//+------------------------------------------------------------------+

Das Journal zeigt die Liste aller erstellten (created=true) oder erstellten und deklarierten (created=false) Symbolzeitreihen im entsprechenden Format an, z. B.

created=true:

USDJPY symbol timeseries: 
Timeseries "USDJPY" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2880
Timeseries "USDJPY" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3921
Timeseries "USDJPY" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3227
Timeseries "USDJPY" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3053
Timeseries "USDJPY" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5095
Timeseries "USDJPY" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5023
Timeseries "USDJPY" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5305
Timeseries "USDJPY" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2562
Timeseries "USDJPY" MN1: Requested: 1000, Actual: 589, Created: 589, On the server: 589

created=false:

USDJPY symbol timeseries: 
Timeseries "USDJPY" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2880
Timeseries "USDJPY" M2: Requested: 1000, Actual: 1000, Created: 0, On the server: 3608
Timeseries "USDJPY" M3: Requested: 1000, Actual: 1000, Created: 0, On the server: 4616
Timeseries "USDJPY" M4: Requested: 1000, Actual: 1000, Created: 0, On the server: 4182
Timeseries "USDJPY" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3921
Timeseries "USDJPY" M6: Requested: 1000, Actual: 1000, Created: 0, On the server: 3748
Timeseries "USDJPY" M10: Requested: 1000, Actual: 1000, Created: 0, On the server: 3401
Timeseries "USDJPY" M12: Requested: 1000, Actual: 1000, Created: 0, On the server: 3314
Timeseries "USDJPY" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3227
Timeseries "USDJPY" M20: Requested: 1000, Actual: 1000, Created: 0, On the server: 3140
Timeseries "USDJPY" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3053
Timeseries "USDJPY" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5095
Timeseries "USDJPY" H2: Requested: 1000, Actual: 1000, Created: 0, On the server: 5047
Timeseries "USDJPY" H3: Requested: 1000, Actual: 1000, Created: 0, On the server: 5031
Timeseries "USDJPY" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5023
Timeseries "USDJPY" H6: Requested: 1000, Actual: 1000, Created: 0, On the server: 6390
Timeseries "USDJPY" H8: Requested: 1000, Actual: 1000, Created: 0, On the server: 6352
Timeseries "USDJPY" H12: Requested: 1000, Actual: 1000, Created: 0, On the server: 5796
Timeseries "USDJPY" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5305
Timeseries "USDJPY" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2562
Timeseries "USDJPY" MN1: Requested: 1000, Actual: 589, Created: 589, On the server: 589

Der Klassenkonstruktor hat die Einstellungen der Zeitreihen-Daten erhalten:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTimeSeries::CTimeSeries(const string symbol) : m_symbol(symbol)
  {
   this.m_list_series.Clear();
   this.m_list_series.Sort();
   for(int i=0;i<21;i++)
     {
      ENUM_TIMEFRAMES timeframe=this.TimeframeByIndex((uchar)i);
      CSeries *series_obj=new CSeries(this.m_symbol,timeframe);
      this.m_list_series.Add(series_obj);
     }
   this.SetTerminalServerDate();
  }
//+------------------------------------------------------------------+

Die spezifizierte Methode zur Zeitreihenaktualisierung zeichnet sich dadurch aus, dass Zeitseriendaten gesetzt werden, wenn das Ereignis "New bar" der aktualisierten Zeitreihe erkannt wird:

//+------------------------------------------------------------------+
//| Update a specified timeseries list                               |
//+------------------------------------------------------------------+
void CTimeSeries::Refresh(const ENUM_TIMEFRAMES timeframe,
                          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)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL || series_obj.DataTotal()==0)
      return;
   series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
   if(series_obj.IsNewBar(time))
      this.SetTerminalServerDate();
  }
//+------------------------------------------------------------------+

Die Methode zur Aktualisierung aller Zeitreihen umfasst auch die Aktualisierung der Zeitreihendaten:

//+------------------------------------------------------------------+
//| Update all timeseries lists                                      |
//+------------------------------------------------------------------+
void CTimeSeries::RefreshAll(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)
  {
   bool upd=false;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
      if(series_obj.IsNewBar(time))
         upd &=true;
     }
   if(upd)
      this.SetTerminalServerDate();
  }
//+------------------------------------------------------------------+

Hier sollten wir die Daten jedoch nur einmal aktualisieren — beim Erkennen des Ereignisses "New bar" in einer der aktualisierten Zeitreihen in der Liste (es gibt 21 Zeitreihen, und wir wollen nicht 21 Mal die gleichen Daten setzen). Daher wird das Flag der Notwendigkeit, die Daten zu aktualisieren, auf true gesetzt, wenn das Ereignis "New bar" in einer Schleife in allen Zeitreihen erfüllt wird. Nach Abschluss der Schleife und im Falle des aktivierten Flags aktualisieren wir die Daten.

Damit ist die Verbesserung der Klasse CTimeSeries abgeschlossen. Kleinere Korrekturen werden hier nicht besprochen. Sie können sie alle in den angehängten Dateien sehen.

Derzeit haben wir drei Klassen, die alle notwendigen Daten für die Erstellung der Zeitreihensammlung enthalten:

  1. Der "Einzelperiodenbalken eines einzelnen Symbols" CBar umfasst die Daten eines einzelnen Balkens eines bestimmten Symbols eines bestimmten Zeitraums;
  2. Die "Einzelperiodenzeitreihe eines einzelnen Symbols" CSeries ermöglicht die Kollektionsliste der Balken (1) eines einzelnen Zeitrahmens eines einzelnen Symbols;
  3. CTimeSeries mit "allen Zeitrahmen eines einzelnen Symbols" umfasst die Liste der Zeitreihen (2) für jede Periode eines einzelnen Symbols

Lassen Sie uns nun die Kollektion der Zeitreihen erstellen, die eine im Programm verwendete Kollektionsliste der Zeitreihen (3) für jedes Symbol ist.

Kollektionsklasse von Zeitreihenobjekten nach Symbolen und Zeitrahmen

Die Zeitreihenkollektion soll aus dem dynamischen Array von Zeigern auf CObject-Klasseninstanzen und deren Nachkommen (die Zeiger auf die CTimeSeries Klassenobjekte) bestehen.

Erstellen wir im Bibliotheksordner \MQL5\Include\DoEasy\Collections\ die Datei TimeSeriesCollection.mqh der Klasse CTimeSeriesCollection.
Das Basisklassenobjekt ist ein Basisobjekt zum Aufbau der CObject-Standardbibliothek.

Werfen wir einen Blick auf den Code der Klasse:

//+------------------------------------------------------------------+
//|                                         TimeSeriesCollection.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\Objects\Series\TimeSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Symbol timeseries collection                                     |
//+------------------------------------------------------------------+
class CTimeSeriesCollection : public CObject
  {
private:
   CArrayObj               m_list;                    // List of applied symbol timeseries
//--- Return the timeseries index by symbol name
   int                     IndexTimeSeries(const string symbol);
public:
//--- Return (1) oneself and (2) the timeseries list
   CTimeSeriesCollection  *GetObject(void)            { return &this;         }
   CArrayObj              *GetList(void)              { return &this.m_list;  }
   
//--- Create the symbol timeseries list collection
   bool                    CreateCollection(const CArrayObj *list_symbols);
//--- Set the flag of using (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   void                    SetAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag=true);
   void                    SetAvailable(const ENUM_TIMEFRAMES timeframe,const bool flag=true);
   void                    SetAvailable(const string symbol,const bool flag=true);
   void                    SetAvailable(const bool flag=true);
//--- Get the flag of using (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                    IsAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe);
   bool                    IsAvailable(const ENUM_TIMEFRAMES timeframe);
   bool                    IsAvailable(const string symbol);
   bool                    IsAvailable(void);

//--- Set the history depth of (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                    SetRequiredUsedData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SetRequiredUsedData(const string symbol,const uint required=0,const int rates_total=0);
   bool                    SetRequiredUsedData(const uint required=0,const int rates_total=0);
//--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol,
//--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                    SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SyncData(const string symbol,const uint required=0,const int rates_total=0);
   bool                    SyncData(const uint required=0,const int rates_total=0);
//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                    CreateSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0);
   bool                    CreateSeries(const ENUM_TIMEFRAMES timeframe,const uint required=0);
   bool                    CreateSeries(const string symbol,const uint required=0);
   bool                    CreateSeries(const uint required=0);
//--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   void                    Refresh(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                   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);
   void                    Refresh(const ENUM_TIMEFRAMES timeframe,
                                   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);
   void                    Refresh(const string symbol,
                                   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);
   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);

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(const bool created=true);
   void                    PrintShort(const bool created=true);
   
   
//--- Constructor
                           CTimeSeriesCollection();
  };
//+------------------------------------------------------------------+

Gegenwärtig ist die Klasse eine Liste von Zeitreihenobjekten und Methoden zur Erstellung/Setzung von Parametern und zur Aktualisierung der notwendigen Zeitreihen nach Symbolen und Perioden:

Das Array von Zeigern auf die Objekte der Klasse CObject m_list soll die Zeiger auf die Objekte der Klasse CTimeSeries enthalten. Dies ist eine Liste, aus der wir die Daten der notwendigen Zeitreihen entnehmen, um damit zu arbeiten.

Die Methode IndexTimeSeries(), die den Zeitreihenindex nach Symbolnamen zurückgibt, erlaubt den Zugriff auf das notwendige CTimeSeries-Objekt nach Symbolnamen:

//+------------------------------------------------------------------+
//| Return the timeseries index by symbol name                       |
//+------------------------------------------------------------------+
int CTimeSeriesCollection::IndexTimeSeries(const string symbol)
  {
   CTimeSeries *tmp=new CTimeSeries(symbol);
   if(tmp==NULL)
      return WRONG_VALUE;
   this.m_list.Sort();
   int index=this.m_list.Search(tmp);
   delete tmp;
   return index;
  }
//+------------------------------------------------------------------+

Erzeugen wir ein neues temporäres Zeitreihenobjekt mit dem der Methode übergebenen Symbol, setzen das Flag für die sortierte Liste m_list list und verwenden die Methode Search(), um den Objektindex in der Liste zu erhalten.
Wir müssen sicherstellen, dass das temporäre Zeitreihenobjekt gelöscht und der ermittelte Index zurückgegeben wird.
Wenn ein solches Objekt mit einem angegebenen Symbol nicht in der Liste enthalten ist, gibt die Methode -1 zurück, andernfalls den ermittelten Objektindexwert.

Die Methode GetObject() gibt den Zeiger auf das Zeitserien-Sammelobjekt an das Steuerprogramm zurück. Sie ermöglicht es, das gesamte Sammelobjekt zu erhalten und mit ihm in einem nutzerdefinierten Programm zu arbeiten.

Die Methode GetList() gibt den Zeiger auf die Kollektionsliste CTimeSeries der Zeitreihen zurück. Sie ermöglicht es, die Liste der Zeitserien mit allen Symbolen zu erhalten und mit ihr in einem nutzerdefinierten Programm zu arbeiten.

Die Methode CreateCollection() erzeugt eine leere Kollektion von Zeitserienobjekten:

//+------------------------------------------------------------------+
//| Create the symbol timeseries collection list                     |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::CreateCollection(const CArrayObj *list_symbols)
  {
//--- If an empty list of symbol objects is passed, exit
   if(list_symbols==NULL)
      return false;
//--- Get the number of symbol objects in the passed list
   int total=list_symbols.Total();
//--- Clear the timeseries collection list
   this.m_list.Clear();
//--- In a loop by all symbol objects
   for(int i=0;i<total;i++)
     {
      //--- get the next symbol object
      CSymbol *symbol_obj=list_symbols.At(i);
      //--- if failed to get a symbol object, move on to the next one in the list
      if(symbol_obj==NULL)
         continue;
      //--- Create a new timeseries object with the current symbol name
      CTimeSeries *timeseries=new CTimeSeries(symbol_obj.Name());
      //--- If failed to create the timeseries object, move on to the next symbol in the list
      if(timeseries==NULL)
         continue;
      //--- Set the sorted list flag for the timeseries collection list
      this.m_list.Sort();
      //--- If the object with the same symbol name is already present in the timeseries collection list, remove the timeseries object
      if(this.m_list.Search(timeseries)>WRONG_VALUE)
         delete timeseries;
      //--- if failed to add the timeseries object to the collection list, remove the timeseries object
      else 
         if(!this.m_list.Add(timeseries))
            delete timeseries;
     }
//--- Return the flag indicating that the created collection list has a size greater than zero
   return this.m_list.Total()>0;
  }
//+-----------------------------------------------------------------------+

Jeder Methodenname wird in ihrem Code mit Kommentaren versehen.
Die zuvor erzeugte Liste aller im Programm verwendeten Symbole wird an die Methode übergeben, und die Zeitreihenobjekte CTimeSeries werden in einer Schleife entlang der Liste erzeugt. Für sie werden sofort Symbolnamen angegeben. Auf diese Weise erhalten wir die Kollektionsliste der Zeitreihen durch die Anzahl der im Programm verwendeten Symbole. Alle übrigen Daten der erzeugten Zeitreihenobjekte bleiben leer. Sie sollten separat gesetzt werden.
Dies geschieht, damit die Bibliothek stets über die Sammlungsliste der leeren Zeitreihen in der Menge verfügt, die der Anzahl der für die Arbeit angegebenen Symbole entspricht. Die Zeitrahmen sowie deren Zeitreihenobjekte (mit denen die Nutzer im Programm arbeiten sollen) werden im nächsten Schritt oder bei Bedarf festgelegt.
Es ist besser, sie im OnInit() des Programms oder unmittelbar nach dessen Start zu erstellen, da die Erstellung einer großen Anzahl von Zeitreihen Zeit braucht, insbesondere im Falle eines "kalten" Programmstarts.

Die vier überladenen Methoden SetAvailable() werden verwendet, um das Flag zu setzen, das anzeigt, dass im Programm mit der angegebenen Zeitreihe gearbeitet werden muss.

Die Methode zum Setzen des Flags für die Verwendung der angegebenen Zeitreihe des angegebenen Symbols:

//+-----------------------------------------------------------------------+
//|Set the flag of using the specified timeseries of the specified symbol |
//+-----------------------------------------------------------------------+
void CTimeSeriesCollection::SetAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag=true)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return;
   series.SetAvailable(flag);
  }
//+------------------------------------------------------------------+

Die Methode erhält das Symbol, den Zeitrahmen und das Flag, das für die dem Zeitrahmensymbol entsprechende Zeitreihe zu setzen ist.

Zuerst holen wir uns den Zeitreihenindex CTimeSeries der Liste m_list durch ein Symbol mit Hilfe der oben beschriebenen Methode IndexTimeSeries(). Abfragen der Zeitreihe aus der Liste durch den Index. Aus dem erhaltenen Zeitreihen-Objekt holen wir uns die benötigte Zeitreihe CSeries der angegebenen Diagrammperiode mit der Methode GetSeries(), die im vorherigen Artikel beschrieben wurde, und setzen das der Methode übergebene Flag für diese mit der Methode SetAvailable() der Klasse CBaseObj.

Die Methode zum Setzen des Flags für die Verwendung der angegebenen Zeitreihe für alle Symbole:

//+------------------------------------------------------------------+
//|Set the flag of using the specified timeseries of all symbols     |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::SetAvailable(const ENUM_TIMEFRAMES timeframe,const bool flag=true)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CSeries *series=timeseries.GetSeries(timeframe);
      if(series==NULL)
         continue;
      series.SetAvailable(flag);
     }
  }
//+------------------------------------------------------------------+

Die Methode erhält den Zeitrahmen und das Flag, das für die angegebene Zeitreihe aller Symbole zu setzen ist.

In der Schleife durch die Liste aller Symbolzeitreihen, holen wir uns die nächste Zeitreihe CTimeSeries durch den Schleifenindex. Aus dem erhaltenen Zeitreihen-Objekt holen wir uns die angegebene Zeitreihe CSeries der angegebenen Diagrammperiode unter Verwendung der Methode GetSeries(), die wir im vorigen Artikel betrachtet haben, und setzen Sie das an die Methode übergebene Flag für diese unter Verwendung der Methode SetAvailable() der Klasse CBaseObj.

Die Methode zum Setzen des Flags für die Verwendung der angegebenen Zeitreihe des angegebenen Symbols:

//+------------------------------------------------------------------+
//|Set the flag of using all timeseries of the specified symbol      |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::SetAvailable(const string symbol,const bool flag=true)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   CArrayObj *list=timeseries.GetListSeries();
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CSeries *series=list.At(i);
      if(series==NULL)
         continue;
      series.SetAvailable(flag);
     }
  }
//+------------------------------------------------------------------+

Die Methode erhält das Symbol und das Flag, das für alle Zeitreihen des angegebenen Symbols zu setzen ist.

Zuerst holen wir uns den Zeitreihenindex CTimeSeries der Liste m_list durch ein Symbol mit Hilfe der oben beschriebenen Methode IndexTimeSeries(). Abfragen der Zeitreihe aus der Liste durch den Index. Aus dem erhaltenen Zeitreihenobjekt erhalten wir die vollständige Liste aller Zeitreihen CSeries mit der Methode GetListSeries(). In der Schleife durch die erhaltene Liste, erhalten wir die nächste Zeitreihe CSeries und setzen das der Methode übergebene Flag dafür unter Verwendung der Methode SetAvailable() der Klasse CBaseObj.

Methode zum Setzen des Flags für die Verwendung aller Zeitreihen aller Symbole:

//+------------------------------------------------------------------+
//| Set the flag of using all timeseries of all symbols              |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::SetAvailable(const bool flag=true)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CArrayObj *list=timeseries.GetListSeries();
      if(list==NULL)
         continue;
      int total_series=list.Total();
      for(int j=0;j<total_series;j++)
        {
         CSeries *series=list.At(j);
         if(series==NULL)
            continue;
         series.SetAvailable(flag);
        }
     }
  }
//+--------------------------------------------------------------------+

Die Methode erhält das Flag, das zusätzlich für alle Zeitreihen aller Symbole gesetzt werden sollte.

In der Schleife durch die Zeitserienliste, holen wir uns das nächste Zeitserienobjekt CTimeSeries durch den Schleifenindex. Wir verwenden die Methode GetListSeries(), um die Liste aller seiner CSeries Zeitreihen vom erhaltenen Objekt zu erhalten. In der Schleife durch die Zeitreihenliste CSeries,holen wir uns die nächste Zeitserie durch den Schleifenindex und setzen das an die Methode übergebene Flag dafür unter Verwendung der Methode SetAvailable() der Klasse CBaseObj.

Die vier Methoden geben das Flag der Verwendung der angegebenen oder aller Zeitreihen zurück:

//+-------------------------------------------------------------------------+
//|Return the flag of using the specified timeseries of the specified symbol|
//+-------------------------------------------------------------------------+
bool CTimeSeriesCollection::IsAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return false;
   return series.IsAvailable();
  }
//+------------------------------------------------------------------+
//| Return the flag of using the specified timeseries of all symbols |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::IsAvailable(const ENUM_TIMEFRAMES timeframe)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CSeries *series=timeseries.GetSeries(timeframe);
      if(series==NULL)
         continue;
      res &=series.IsAvailable();
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Return the flag of using all timeseries of the specified symbol  |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::IsAvailable(const string symbol)
  {
   bool res=true;
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   CArrayObj *list=timeseries.GetListSeries();
   if(list==NULL)
      return false;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CSeries *series=list.At(i);
      if(series==NULL)
         continue;
      res &=series.IsAvailable();
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Return the flag of using all timeseries of all symbols           |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::IsAvailable(void)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CArrayObj *list=timeseries.GetListSeries();
      if(list==NULL)
         continue;
      int total_series=list.Total();
      for(int j=0;j<total_series;j++)
        {
         CSeries *series=list.At(j);
         if(series==NULL)
            continue;
         res &=series.IsAvailable();
        }
     }
   return res;
  }
//+--------------------------------------------------------------------+

Die Methoden funktionieren ähnlich wie die Methoden zum Setzen der Flags für Zeitreihen, die ich gerade oben beschrieben habe. Der Unterschied besteht darin, dass die lokale Variable res mit dem Anfangszustand true verwendet wird, um das "kollektive" Flag von allen Zeitreihen in den Methoden zu erhalten, die das gemeinsame Flag der Verwendung mehrerer Zeitreihen zurückgeben. In der Schleife durch die Zeitreihen CSeries wird der Flag-Status jeder überprüften Zeitserie in die Variable geschrieben. Wenn mindestens eine der Zeitserien gleich false ist, wird das Flag false in der Variable gesetzt. Der Wert der Variablen wird von der Methode nach Abschluss aller Schleifen in allen Zeitreihen zurückgegeben.

Die vier Methoden zur Einstellung der erforderlichen Tiefenhistorie entweder für bestimmte Zeitreihen oder für alle Zeitreihen auf einmal:

//+--------------------------------------------------------------------------+
//|Set the history depth for the specified timeseries of the specified symbol|
//+--------------------------------------------------------------------------+
bool CTimeSeriesCollection::SetRequiredUsedData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return false;
   return series.SetRequiredUsedData(required,rates_total);
  }
//+------------------------------------------------------------------+
//| Set the history depth of the specified timeseries of all symbols |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CSeries *series=timeseries.GetSeries(timeframe);
      if(series==NULL)
         continue;
      res &=series.SetRequiredUsedData(required,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Set the history depth for all timeseries of the specified symbol |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SetRequiredUsedData(const string symbol,const uint required=0,const int rates_total=0)
  {
   bool res=true;
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   CArrayObj *list=timeseries.GetListSeries();
   if(list==NULL)
      return false;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CSeries *series=list.At(i);
      if(series==NULL)
         continue;
      res &=series.SetRequiredUsedData(required,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Set the history depth for all timeseries of all symbols          |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SetRequiredUsedData(const uint required=0,const int rates_total=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CArrayObj *list=timeseries.GetListSeries();
      if(list==NULL)
         continue;
      int total_series=list.Total();
      for(int j=0;j<total_series;j++)
        {
         CSeries *series=list.At(j);
         if(series==NULL)
            continue;
         res &=series.SetRequiredUsedData(required,rates_total);
        }
     }
   return res;
  }
//+------------------------------------------------------------------+

Die Methoden funktionieren ähnlich wie die Methoden, die Nutzung der Flags der Zeitreihen zurückgeben. Wir geben das Ergebnis des Setzens der angeforderten Datenmenge für die Zeitreihenobjekte CSeries unter Verwendung der Methode SetRequiredUsedData(), die den booleschen Wert zurückgeben. Daher wird das gemeinsame Flag, das beim Setzen der Historientiefe für mehrere Zeitreihen CSeries verwendet wird, auch hier angewendet.

Die vier Methoden, die das Flag zum Synchronisieren entweder einer angegebenen oder aller Zeitserien zurückgeben:

//+------------------------------------------------------------------+
//| Return the flag of data synchronization with the server data     |
//| for a specified timeseries of a specified symbol                 |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return false;
   return series.SyncData(required,rates_total);
  }
//+------------------------------------------------------------------+
//| Return the flag of data synchronization with the server data     |
//| for a specified timeseries of all symbols                        |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CSeries *series=timeseries.GetSeries(timeframe);
      if(series==NULL)
         continue;
      res &=series.SyncData(required,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Return the flag of data synchronization with the server data     |
//| for all timeseries of a specified symbol                         |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SyncData(const string symbol,const uint required=0,const int rates_total=0)
  {
   bool res=true;
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   CArrayObj *list=timeseries.GetListSeries();
   if(list==NULL)
      return false;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CSeries *series=list.At(i);
      if(series==NULL)
         continue;
      res &=series.SyncData(required,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Return the flag of data synchronization with the server data     |
//| for all timeseries of all symbols                                |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::SyncData(const uint required=0,const int rates_total=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      CArrayObj *list=timeseries.GetListSeries();
      if(list==NULL)
         continue;
      int total_series=list.Total();
      for(int j=0;j<total_series;j++)
        {
         CSeries *series=list.At(j);
         if(series==NULL)
            continue;
         res &=series.SyncData(required,rates_total);
        }
     }
   return res;
  }
//+------------------------------------------------------------------+

Die Methoden funktionieren ähnlich wie die oben genannten. Sie geben das Ergebnis der Überprüfung der Zeitreihensynchronisation mit der Methode SyncData() der Klasse CSeries zurück.

Die vier Methoden zur Erstellung der angegebenen oder aller Zeitreihen.

Die Methode zur Erstellung der angegebenen Zeitreihen des angegebenen Symbols:

//+------------------------------------------------------------------+
//| Create the specified timeseries of the specified symbol          |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::CreateSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   return timeseries.Create(timeframe,required);
  }
//+------------------------------------------------------------------+

Die Methode erhält das Symbol, für das die Zeitreihe erstellt werden soll. Die Zeitreihenperiode wird ebenfalls an die Methode übergeben.

Abrufen des Zeitreihenindex in der Liste durch einen Symbolnamen mit der Methode IndexTimeSeries(). Wir nutzen den erhaltenen Index, um die Zeitreihe CTimeSeries aus der Liste zu erhalten und geben das Ergebnis der Erzeugung einer bestimmten Zeitreihe mit der Methode Create() der CTimeSeries-Klasse zurück.

Die Methode zur Erzeugung der angegebenen Zeitreihe aller Symbole:

//+------------------------------------------------------------------+
//| Create the specified timeseries of all symbols                   |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::CreateSeries(const ENUM_TIMEFRAMES timeframe,const uint required=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      res &=timeseries.Create(timeframe,required);
     }
   return res;
  }
//+------------------------------------------------------------------+

Die Methode erhält den Zeitrahmen, dessen Zeitreihen für alle Symbole erstellt werden sollen.

In der Schleife über alle Objekte aller Objektzeitreihen, holen wir uns das nächste Zeitreihenobjekt CTimeSeries durch den Schleifenindex. Zu der Variablen res fügen wir das Ergebnis der Erzeugung einer bestimmten Zeitserie mit der Methode Create() der Klasse CTimeSeries hinzu. Nach dem Ende der Schleife geben wir das Ergebnis der Erzeugung einer angegebenen Zeitreihe für alle Symbole zurück. Wenn mindestens eine Zeitreihe nicht erzeugt wurde, ist das Ergebnis false.

Die Methode zur Erzeugung aller Zeitreihen des angegebenen Symbols:

//+------------------------------------------------------------------+
//| Create all timeseries of the specified symbol                    |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::CreateSeries(const string symbol,const uint required=0)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
   return timeseries.CreateAll(required);
  }
//+------------------------------------------------------------------+

Die Methode erhält das Symbol, für das alle Zeitreihen erstellt werden sollen.

Abrufen des Zeitreihenindex in der Liste durch einen Symbolnamen mit der Methode IndexTimeSeries(). Unter Verwendung des erhaltenen Index erhalten wir die Zeitreihe CTimeSeries aus der Liste und geben das Ergebnis der Erzeugung aller Zeitreihen mit der Methode CreateAll() Methode der CTimeSeries Klasse.

Die Methode zur Erzeugung aller Zeitreihen aller Symbole:

//+------------------------------------------------------------------+
//| Create all timeseries of all symbols                             |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::CreateSeries(const uint required=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      res &=timeseries.CreateAll(required);
     }
   return res;
  }
//+------------------------------------------------------------------+

Die Methode erhält nur die Anzahl der erzeugten Historietiefe (wie bei allen oben genannten Methoden). Standardmäßig wird Null übergeben. Dies ist die Tiefe der Historie von 1000 Balken, die durch die Makro-Substitution SERIES_DEFAULT_BARS_COUNT in der Datei Defines.mqh festgelegt wurde.

In der Schleife durch die Zeitserienliste, holen wir uns das nächste Zeitserienobjekt CTimeSeries durch den Schleifenindex. Die Variable res erhält das Ergebnis der Erzeugung aller Zeitreihen für das Symbol des aktuellen CTimeSeries-Objektes mit der Methode CreateAll(), die das Flag zur Erzeugung aller Symbol-Zeitreihen des CTimeSeries-Objektes zurückgibt.
Nach Abschluss der Schleife geben wir das Ergebnis der Erstellung aller Zeitreihen für alle Symbole zurück. Wenn mindestens eine Zeitreihe nicht erzeugt wurde, ist das Ergebnis false.

Die vier Methoden zur Aktualisierung der angegebenen Zeitreihe des angegebenen Symbols oder aller Zeitreihen:

//+------------------------------------------------------------------+
//| Update the specified timeseries of the specified symbol          |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    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)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+
//| Update the specified timeseries of all symbols                   |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const ENUM_TIMEFRAMES timeframe,
                                    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)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+
//| Update all timeseries of the specified symbol                    |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const string symbol,
                                    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)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+
//| Update all timeseries of all symbols                             |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::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)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+

Hier erhalten wir das erforderliche Zeitreihenobjekt CTimeSeries auf die eine oder andere Weise (alle Möglichkeiten wurden in den vorherigen Methoden berücksichtigt) und rufen die Methoden zur Aktualisierung einer einzelnen spezifizierten Refresh()-Zeitreihe oder aller RefreshAll()-Zeitreihen der CTimeSeries-Klasse auf einmal auf.

Die aktuellen Daten aus den Zeitreihen-Arrays werden an alle Methoden übergeben. Dies ist notwendig, um in Indikatoren mit der aktuellen Periode des aktuellen Symbols zu arbeiten. In den übrigen Fällen sind die übergebenen Werte ohne Bedeutung. Daher setzen wir ihre Standardwerte auf 0.

Die Methode, die die vollständige Kollektionsliste an die Zeitschrift zurückgibt:

//+------------------------------------------------------------------+
//| Display complete collection description to the journal           |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Print(const bool created=true)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      timeseries.Print(created);
     }
  }
//+------------------------------------------------------------------+

Die Methode erhält das Flag, das die Notwendigkeit anzeigt, nur erstellte Zeitreihen im Journal anzuzeigen.

In der Schleife durch die Liste der Zeitreihenobjekte, holen wir uns das nächste CTimeSeries-Zeitreihenobjekt und rufen dessen gleichnamige Methode auf, deren Operationsergebnis oben betrachtet wurde. Als Ergebnis stehen die Daten aller aktuellen Zeitreihen aller Kollektionssymbole im Journal zur Verfügung.

Die Methode, die die kurze Kollektionsliste in das Journal schreibt:

//+------------------------------------------------------------------+
//| Display the short collection description in the journal          |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::PrintShort(const bool created=true)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      timeseries.PrintShort(created);
     }
  }
//+------------------------------------------------------------------+

Die Methode erhält das Flag, das die Notwendigkeit anzeigt, nur erstellte Zeitreihen im Journal anzuzeigen.

In der Schleife durch die Liste der Zeitreihenobjekte, holen wir uns das nächste CTimeSeries-Zeitreihenobjekt und rufen dessen gleichnamige Methode auf, deren Operationsergebnis oben betrachtet wurde. Als Ergebnis stehen die Daten aller gegenwärtigen Zeitreihen aller Kollektionssymbole im Journal in der Kurzform zur Verfügung.

Die aktuelle Version der Klasse der Zeitreihenkollektion ist fertig. Die vollständige Code der Klasse finden Sie in den angehängten Dateien.

Jetzt müssen wir den externen Zugriff auf die erstellte Zeitreihensammlung und die bequeme Einstellung der Parameter der erstellten Zeitreihen organisieren.

Jedes Programm erhält Zugriff auf die Bibliotheksmethoden über das Hauptobjekt CEngine der Bibliothek.
Fügen Sie die Möglichkeit, auf die Zeitreihensammlung zuzugreifen, zur CEngine-Klassendatei.
Öffnen Sie die Datei Engine.mqh aus dem Bibliotheksverzeichnis \MQL5\Include\DoEasy\ und nehmen Sie die notwendigen Änderungen vor.

Da die Klassendatei der CTimeSeries in der vorherigen Version in die Klasse CEngine aufgenommen wurde, nur um ihre Funktionsweise zu überprüfen, entfernen Sie sie aus der Liste der aufgenommenen Dateien

//+------------------------------------------------------------------+
//| 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\TimeSeries.mqh"
//+------------------------------------------------------------------+

und inkludieren Sie die Datei der Klasse der Zeitreihenkollektion:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/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 "Collections\TimeSeriesCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

Im 'private' Abschnitt der Klasse deklarieren Sie die Variable mit der Klasse der Zeitreihenkollektion:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_series;                       // Timeseries collection
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CArrayObj            m_list_counters;                 // List of timer counters

Im 'public' Teil der Klasse ändern Sie die Implementierung der Methode zum Setzen der Liste der verwendeten Symbole:

//--- Set the list of (1) used symbols
   bool                 SetUsedSymbols(const string &array_symbols[])   { return this.m_symbols.SetUsedSymbols(array_symbols);}

In der aktuellen Implementierung ruft die Methode die gleichnamige Methode zum Setzen der Liste der Symbole für die Symbolsammelklasse auf. Hier wäre es für uns am bequemsten, die Zeitreihensammlung auf der Grundlage der erstellten Liste der Symbolkollektion unmittelbar nach dem Setzen der Symbolliste in der Symbolsammlung zu erstellen.

Lassen Sie hier nur die Methodendeklaration stehen:

//--- Set the list of used symbols in the symbol collection and create the collection of symbol timeseries
   bool                 SetUsedSymbols(const string &array_symbols[]);

und schreiben Sie die Implementierung außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Set the list of used symbols in the symbol collection            |
//| and create the symbol timeseries collection                      |
//+------------------------------------------------------------------+
bool CEngine::SetUsedSymbols(const string &array_symbols[])
  {
   bool res=this.m_symbols.SetUsedSymbols(array_symbols);
   CArrayObj *list=this.GetListAllUsedSymbols();
   if(list==NULL)
      return false;
   res&=this.m_series.CreateCollection(list);
   return res;
  }
//+------------------------------------------------------------------+

Hier deklarieren Sie die Variable res und initialisieren sie durch das Ergebnis der Methode zum Setzen der Symbolliste in der Symbolkollektion.

Als Nächstes holen Sie die Liste der verwendeten Symbole aus der Kollektionsklasse der Symbole. Wenn die Liste nicht erstellt wird, wird false zurückgegeben, andernfalls addieren Sie das Ergebnis der Erstellung der Kollektionsliste der Zeitreihen zu der Variablen res basierend auf der Kollektionsliste der Symbole.
Das Endergebnis wird von der Methode zurückgeben
.

Fügen Sie die Methoden für die Arbeit mit der Kollektion der Zeitreihen dem 'public' Klassenabschnitt hinzu:

//--- Return the list of pending requests
   CArrayObj           *GetListPendingRequests(void)                          { return this.m_trading.GetListRequests();                        }

//--- Return (1) the timeseries collection and (2) the list of timeseries from the timeseries collection
   CTimeSeriesCollection *GetTimeSeriesCollection(void)                       { return &this.m_series;                                          }
   CArrayObj           *GetListTimeSeries(void)                               { return this.m_series.GetList();                                 }
//--- Set the flag of using (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   void                 SeriesSetAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag=true)
                          { this.m_series.SetAvailable(symbol,timeframe,flag);}
   void                 SeriesSetAvailable(const ENUM_TIMEFRAMES timeframe,const bool flag=true)
                          { this.m_series.SetAvailable(timeframe,flag);       }
   void                 SeriesSetAvailable(const string symbol,const bool flag=true)
                          { this.m_series.SetAvailable(symbol,flag);          }
   void                 SeriesSetAvailable(const bool flag=true)              
                          { this.m_series.SetAvailable(flag);                 }
//--- Set the history depth of (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                 SeriesSetRequiredUsedData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
                          { return this.m_series.SetRequiredUsedData(symbol,timeframe,required,rates_total);}
   bool                 SeriesSetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
                          { return this.m_series.SetRequiredUsedData(timeframe,required,rates_total);       }
   bool                 SeriesSetRequiredUsedData(const string symbol,const uint required=0,const int rates_total=0)
                          { return this.m_series.SetRequiredUsedData(symbol,required,rates_total);          }
   bool                 SeriesSetRequiredUsedData(const uint required=0,const int rates_total=0)
                          { return this.m_series.SetRequiredUsedData(required,rates_total);                 }
//--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol,
//--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                 SeriesSyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
                          { return this.m_series.SyncData(symbol,timeframe,required,rates_total);  }
   bool                 SeriesSyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
                          { return this.m_series.SyncData(timeframe,required,rates_total);         }
   bool                 SeriesSyncData(const string symbol,const uint required=0,const int rates_total=0)
                          { return this.m_series.SyncData(symbol,required,rates_total);            }
   bool                 SeriesSyncData(const uint required=0,const int rates_total=0)
                          { return this.m_series.SyncData(required,rates_total);                   }
//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                 SeriesCreate(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0)
                          { return this.m_series.CreateSeries(symbol,timeframe,required);          }
   bool                 SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint required=0)
                          { return this.m_series.CreateSeries(timeframe,required);                 }
   bool                 SeriesCreate(const string symbol,const uint required=0)
                          { return this.m_series.CreateSeries(symbol,required);                    }
   bool                 SeriesCreate(const uint required=0)
                          { return this.m_series.CreateSeries(required);                           }
//--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   void                 SeriesRefresh(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                      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)
                          { this.m_series.Refresh(symbol,timeframe,time,open,high,low,close,tick_volume,volume,spread); }
   void                 SeriesRefresh(const ENUM_TIMEFRAMES timeframe,
                                      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)
                          { this.m_series.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);        }
   void                 SeriesRefresh(const string symbol,
                                      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)
                          { this.m_series.Refresh(symbol,time,open,high,low,close,tick_volume,volume,spread);           }
   void                 SeriesRefresh(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)
                          { this.m_series.Refresh(time,open,high,low,close,tick_volume,volume,spread);                  }

Die Methode GetTimeSeriesCollection() gibt den Zeiger auf das Zeitserien-Sammelobjekt an das aufrufende Programm zurück. Sie ermöglicht es, den Zeiger auf die Kollektion im Programm zu empfangen und effizient mit ihr zu arbeiten.

Die Methode GetListTimeSeries() gibt den Zeiger auf die Kollektionsliste der Zeitreihen an das aufrufende Programm zurück. Sie ermöglicht es, den Zeiger auf die Zeitserien-Objektliste CTimeSeries zu empfangen und effizient damit zu arbeiten.

Die überladenen Methoden SeriesSetAvailable() ermöglichen den Zugriff auf die Methoden SetAvailable() der Kollektionsklasse der Zeitreihen CTimeSeriesCollection(), die wir oben besprochen haben.

Die überladenen Methoden SeriesSetRequiredUsedData() bieten Zugriff auf die Methoden SetRequiredUsedData() der oben betrachteten Kollektionsklasse der Zeitreihen CTimeSeriesCollection().

Die überladenen Methoden SeriesSyncData() ermöglichen den Zugriff auf die Methoden SyncData() der Kollektionsklasse der Zeitreihen CTimeSeriesCollection(), die wir oben besprochen haben.

Die überladenen Methoden SeriesCreate() bieten Zugriff auf die Methoden CreateSeries() der oben betrachteten Kollektionsklasse der Zeitreihen CTimeSeriesCollection().

Die überladenen Methoden SeriesRefresh() bieten Zugriff auf die Methoden Refresh() der oben betrachteten Kollektionsklasse der Zeitreihen CTimeSeriesCollection().

Dies sind alle derzeit notwendigen Aufgaben zur Handhabung aller Klassen zum Testen einer neuen Kollektionsklasse der Zeitreihen.

Tests

Verwenden wir den EA aus dem vorigen Artikel, um die Erstellung und das Ausfüllen der Zeitreihenkollektion zu testen. Speichern Sie ihn im neuen Ordner \MQL5\Experts\TestDoEasy\Part37\ unter dem Namen TestDoEasyPart37.mq5.

Um den Modus der Arbeit mit den Symbolen auszuwählen, haben wir die Enumeration in der Datei Datas.mqh.

//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                              // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                         // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                   // Work with the full symbol list
  };
//+------------------------------------------------------------------+

Die EA-Eingänge verfügen über die Variable, mit der wir Symbole für die Arbeit mit ihnen auswählen können:

sinput   ENUM_SYMBOLS_MODE InpModeUsedSymbols   =  SYMBOLS_MODE_CURRENT;            // Mode of used symbols list

Abhängig vom Variablenwert wird das Array von Symbolen erstellt und während seiner Initialisierung in der EA-Funktion OnInitDoEasy() an die Bibliothek übergeben. Die Liste der Arbeitssymbole wird dann in der Bibliothek erstellt.

Wir müssen die gleichen Operationen durchführen, um die Liste der EA-Arbeitszeitrahmen auszuwählen und zu erstellen.
Erstellen Sie eine neue Enumeration in der Datei Datas.mqh, um Betriebsarten mit Symboldiagramm-Perioden auszuwählen:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define INPUT_SEPARATOR                (",")          // Separator in the inputs string
#define TOTAL_LANG                     (2)            // Number of used languages
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                              // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                         // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                   // Work with the full symbol list
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work with the current timeframe only
   TIMEFRAMES_MODE_LIST,                              // Work with the specified timeframe list
   TIMEFRAMES_MODE_ALL                                // Work with the full timeframe list
  };
//+------------------------------------------------------------------+

Die Datei der Hilfsfunktionen enthält die Funktion zur Vorbereitung des Arrays der verwendeten Bibliothekssymbole:

//+------------------------------------------------------------------+
//| Prepare the symbol array for a symbol collection                 |
//+------------------------------------------------------------------+
bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[])
  {
   //--- When working with the current symbol
   if(mode_used_symbols==SYMBOLS_MODE_CURRENT)
     {
      //--- Write the name of the current symbol to the only array cell
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=Symbol();
      return true;
     }
   //--- If working with a predefined symbol set (from the defined_used_symbols string)
   else if(mode_used_symbols==SYMBOLS_MODE_DEFINES)
     {
      //--- Set a comma as a separator
      string separator=",";
      //--- Replace erroneous separators with correct ones
      if(StringFind(defined_used_symbols,";")>WRONG_VALUE)  StringReplace(defined_used_symbols,";",separator);   
      if(StringFind(defined_used_symbols,":")>WRONG_VALUE)  StringReplace(defined_used_symbols,":",separator); 
      if(StringFind(defined_used_symbols,"|")>WRONG_VALUE)  StringReplace(defined_used_symbols,"|",separator);   
      if(StringFind(defined_used_symbols,"/")>WRONG_VALUE)  StringReplace(defined_used_symbols,"/",separator); 
      if(StringFind(defined_used_symbols,"\\")>WRONG_VALUE) StringReplace(defined_used_symbols,"\\",separator);  
      if(StringFind(defined_used_symbols,"'")>WRONG_VALUE)  StringReplace(defined_used_symbols,"'",separator); 
      if(StringFind(defined_used_symbols,"-")>WRONG_VALUE)  StringReplace(defined_used_symbols,"-",separator);   
      if(StringFind(defined_used_symbols,"`")>WRONG_VALUE)  StringReplace(defined_used_symbols,"`",separator);
      //--- Delete as long as there are spaces
      while(StringFind(defined_used_symbols," ")>WRONG_VALUE && !IsStopped()) 
         StringReplace(defined_used_symbols," ","");
      //--- As soon as there are double separators (after removing spaces between them), replace them with a separator
      while(StringFind(defined_used_symbols,separator+separator)>WRONG_VALUE && !IsStopped())
         StringReplace(defined_used_symbols,separator+separator,separator);
      //--- If a single separator remains before the first symbol in the string, replace it with a space
      if(StringFind(defined_used_symbols,separator)==0) 
         StringSetCharacter(defined_used_symbols,0,32);
      //--- If a single separator remains after the last symbol in the string, replace it with a space
      if(StringFind(defined_used_symbols,separator)==StringLen(defined_used_symbols)-1)
         StringSetCharacter(defined_used_symbols,StringLen(defined_used_symbols)-1,32);
      //--- Remove all redundant things to the left and right
      #ifdef __MQL5__
         StringTrimLeft(defined_used_symbols);
         StringTrimRight(defined_used_symbols);
      //---  __MQL4__
      #else 
         defined_used_symbols=StringTrimLeft(defined_used_symbols);
         defined_used_symbols=StringTrimRight(defined_used_symbols);
      #endif 
      //--- Prepare the array 
      ArrayResize(used_symbols_array,0);
      ResetLastError();
      //--- divide the string by separators (comma) and add all found substrings to the array
      int n=StringSplit(defined_used_symbols,StringGetCharacter(separator,0),used_symbols_array);
      //--- if nothing is found, display the appropriate message (working with the current symbol is selected automatically)
      if(n<1)
        {
         string err=
           (n==0  ?  
            DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_STRING)+Symbol() :
            DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY)+(string)GetLastError()
           );
         Print(err);
         return false;
        }
     }
   //--- If working with the Market Watch window or the full list
   else
     {
      //--- Add the (mode_used_symbols) working mode to the only array cell
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=EnumToString(mode_used_symbols);
     }
   return true;
  }
//+------------------------------------------------------------------+

Wir sollten eine ähnliche Funktion haben, um das Spektrum der verwendeten Zeitrahmen vorzubereiten. Es wird deutlich, dass wir in zwei ähnlichen Funktionen denselben Codeblock (farbig hervorgehoben in der bereitgestellten Auflistung) haben werden.
Lassen Sie uns diesen Codeblock in eine separate Funktion verschieben:

//+------------------------------------------------------------------+
//| Prepare the passed string of parameters                          |
//+------------------------------------------------------------------+
int StringParamsPrepare(string defined_used,string separator,string &array[])
  {
//--- Replace erroneous separators with correct ones
   if(separator!=";" && StringFind(defined_used,";")>WRONG_VALUE)  StringReplace(defined_used,";",separator);   
   if(separator!=":" && StringFind(defined_used,":")>WRONG_VALUE)  StringReplace(defined_used,":",separator); 
   if(separator!="|" && StringFind(defined_used,"|")>WRONG_VALUE)  StringReplace(defined_used,"|",separator);   
   if(separator!="/" && StringFind(defined_used,"/")>WRONG_VALUE)  StringReplace(defined_used,"/",separator); 
   if(separator!="\\"&& StringFind(defined_used,"\\")>WRONG_VALUE) StringReplace(defined_used,"\\",separator);  
   if(separator!="'" && StringFind(defined_used,"'")>WRONG_VALUE)  StringReplace(defined_used,"'",separator); 
   if(separator!="-" && StringFind(defined_used,"-")>WRONG_VALUE)  StringReplace(defined_used,"-",separator);   
   if(separator!="`" && StringFind(defined_used,"`")>WRONG_VALUE)  StringReplace(defined_used,"`",separator);
//--- Delete as long as there are spaces
   while(StringFind(defined_used," ")>WRONG_VALUE && !IsStopped()) 
      StringReplace(defined_used," ","");
//--- As soon as there are double separators (after removing spaces between them), replace them with a separator
   while(StringFind(defined_used,separator+separator)>WRONG_VALUE && !IsStopped())
      StringReplace(defined_used,separator+separator,separator);
//--- If a single separator remains before the first symbol in the string, replace it with a space
   if(StringFind(defined_used,separator)==0) 
      StringSetCharacter(defined_used,0,32);
//--- If a single separator remains after the last symbol in the string, replace it with a space
   if(StringFind(defined_used,separator)==StringLen(defined_used)-1)
      StringSetCharacter(defined_used,StringLen(defined_used)-1,32);
//--- Remove all redundant things to the left and right
   #ifdef __MQL5__
      StringTrimLeft(defined_used);
      StringTrimRight(defined_used);
//---  __MQL4__
   #else 
      defined_used=StringTrimLeft(defined_used);
      defined_used=StringTrimRight(defined_used);
   #endif 
//--- Prepare the array 
   ArrayResize(array,0);
   ResetLastError();
//--- divide the string by separators (comma), write all detected substrings into the array and return the number of obtained substrings
   return StringSplit(defined_used,StringGetCharacter(separator,0),array);
  }
//+------------------------------------------------------------------+

In diesem Fall sieht die Funktion zur Erstellung des Arrays der verwendeten Symbole wie folgt aus:

//+------------------------------------------------------------------+
//| Prepare the symbol array for a symbol collection                 |
//+------------------------------------------------------------------+
bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[])
  {
//--- When working with the current symbol
   if(mode_used_symbols==SYMBOLS_MODE_CURRENT)
     {
      //--- Write the name of the current symbol to the only array cell
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=Symbol();
      return true;
     }
//--- If working with a predefined symbol set (from the defined_used_symbols string)
   else if(mode_used_symbols==SYMBOLS_MODE_DEFINES)
     {
      //--- Set comma as a separator (defined in the Datas.mqh file, page 11)
      string separator=INPUT_SEPARATOR;
      int n=StringParamsPrepare(defined_used_symbols,separator,used_symbols_array);
      //--- if nothing is found, display the appropriate message (working with the current symbol is selected automatically)
      if(n<1)
        {
         int err_code=GetLastError();
         string err=
           (n==0  ?  
            DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_SYMBOLS_STRING)+Symbol() :
            DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY)+(string)err_code+": "+CMessage::Text(err_code)
           );
         Print(err);
         return false;
        }
     }
//--- If working with the Market Watch window or the full list
   else
     {
      //--- Add the (mode_used_symbols) working mode to the only array cell
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=EnumToString(mode_used_symbols);
     }
//--- All is successful
   return true;
  }
//+------------------------------------------------------------------+

Der Codeblock, der in eine separate Funktion verschoben wurde, wird durch die Funktion zur Vorbereitung der Parameterfolge ersetzt.

Lassen Sie uns die Funktion zur Vorbereitung der im Programm verwendeten Zeitrahmen auf ähnliche Weise erstellen:

//+------------------------------------------------------------------+
//| Prepare the array of timeframes for the timeseries collection    |
//+------------------------------------------------------------------+
bool CreateUsedTimeframesArray(const ENUM_TIMEFRAMES_MODE mode_used_periods,string defined_used_periods,string &used_periods_array[])
  {
//--- If working with the current chart period, set the current timeframe flag
   if(mode_used_periods==TIMEFRAMES_MODE_CURRENT)
     {
      ArrayResize(used_periods_array,1,21);
      used_periods_array[0]=TimeframeDescription((ENUM_TIMEFRAMES)Period());
      return true;
     }
//--- If working with a predefined set of chart periods (from the defined_used_periods string)
   else if(mode_used_periods==TIMEFRAMES_MODE_LIST)
     {
      //--- Set comma as a separator (defined in the Datas.mqh file, page 11)
      string separator=INPUT_SEPARATOR;
      //--- Fill in the array of parameters from the string with predefined timeframes
      int n=StringParamsPrepare(defined_used_periods,separator,used_periods_array);
      //--- if nothing is found, display the appropriate message (working with the current period is selected automatically)
      if(n<1)
        {
         int err_code=GetLastError();
         string err=
           (n==0  ?  
            DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_PERIODS_STRING)+TimeframeDescription((ENUM_TIMEFRAMES)Period()) :
            DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_PERIODS_ARRAY)+(string)err_code+": "+CMessage::Text(err_code)
           );
         Print(err);
         //--- Set the current period to the array
         ArrayResize(used_periods_array,1,21);
         used_periods_array[0]=TimeframeDescription((ENUM_TIMEFRAMES)Period());
         return false;
        }
     }
//--- If working with the full list of timeframes, fill in the array with strings describing all timeframes
   else
     {
      ArrayResize(used_periods_array,21,21);
      for(int i=0;i<21;i++)
         used_periods_array[i]=TimeframeDescription(TimeframeByEnumIndex(uchar(i+1)));
     }
//--- All is successful
   return true;
  }
//+------------------------------------------------------------------+

Die Funktion zur Vorbereitung des Parameterstrings wird hier in ähnlicher Weise aufgerufen. Der entsprechende Code ist für die Funktionen CreateUsedSymbolsArray() und CreateUsedTimeframesArray() identisch.

Die Datei DELib.mqh enthält auch die Funktion, die die Zeit mit Millisekunden anzeigt:

//+------------------------------------------------------------------+
//| Return time with milliseconds                                    |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

Die Funktion zeigt die Zeit immer im folgenden Format an: YYYY.MM.TT HH:MM:SS.MSC

Fügen wir die Möglichkeit hinzu, das Zeitanzeigeformat auszuwählen:

//+------------------------------------------------------------------+
//| Return time with milliseconds                                    |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS)
  {
   return TimeToString(time_msc/1000,flags)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

Jetzt ist es möglich, das Zeitanzeigeformat + Millisekunden einzustellen.

Fügen Sie eine Auswahl der angewandten Zeitrahmen zum Block der EA-Datei-Eingaben hinzu:

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   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;            // Mode of used timeframes list
sinput   string            InpUsedTFs           =  "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator)
sinput   bool              InpUseSounds         =  true; // Use sounds

InpModeUsedTFs erlaubt die Auswahl des Zeitrahmen-Verwendungsmodus

  • Nur mit dem aktuellen Zeitrahmen arbeiten
  • Arbeiten mit der angegebenen Zeitrahmenliste
  • Arbeiten mit der ganzen Liste der Zeitrahmen

Wenn der zweite Modus ausgewählt wird, verwendet das Programm die Liste der Zeitrahmen, die in der Zeichenfolge beschrieben sind, die durch die Variablen InpUsedTFs angegeben ist.

Aus dem Block der globalen Variablen EA entfernen Sie die Variable und deklarieren das Objekt der Klasse CTimeSeries. Dies ist nicht mehr erforderlich. Jetzt erfolgt der Zugriff auf die Zeitreihe durch Zugriff auf das Bibliotheksobjekt engine.

//--- global variables
CEngine        engine;
CTimeSeries    timeseries;
SDataButt      butt_data[TOTAL_BUTT];

Derselbe Block globaler Variablen erhält das neue Array in das die Namen der Zeitrahmen, die vom Programm benutzt werden, geschrieben werden sollen:

//--- global variables
CEngine        engine;
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         array_used_symbols[];
string         array_used_periods[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;
//+------------------------------------------------------------------+

Entfernen Sie aus dem OnInit() des EAs den Code zur Erstellung von zwei Zeitreihen, der von den vorherigen Tests übrig geblieben ist:

//+------------------------------------------------------------------+
//| 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 a symbol for created timeseries
   timeseries.SetSymbol(Symbol());
//#define TIMESERIES_ALL
//--- Create two timeseries
   #ifndef TIMESERIES_ALL
      timeseries.SyncData(PERIOD_CURRENT,10);
      timeseries.Create(PERIOD_CURRENT);
      timeseries.SyncData(PERIOD_M15,2);
      timeseries.Create(PERIOD_M15);
//--- Create all timeseries
   #else 
      timeseries.SyncAllData();
      timeseries.CreateAll();
   #endif 
//--- Check created timeseries
   CArrayObj *list=timeseries.GetList();
   Print(TextByLanguage("Данные созданных таймсерий:","Data of created timeseries:"));
   for(int i=0;i<list.Total();i++)
     {
      CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i);
      if(series_obj==NULL || series_obj.AmountUsedData()==0 || series_obj.DataTotal()==0)
         continue;
      Print(
            DFUN,i,": ",series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe()),
            ": AmountUsedData=",series_obj.AmountUsedData(),", DataTotal=",series_obj.DataTotal(),", Bars=",series_obj.Bars()
           );
     }
   Print("");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Entfernen Sie aus OnTick() des EAs den Code der Aktualisierung der erstellten Zeitreihen (die Zeitreihen sollen im nächsten Artikel aktualisiert werden):

//+------------------------------------------------------------------+
//| 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
     }
//--- Update created timeseries
   CArrayObj *list=timeseries.GetList();
   for(int i=0;i<list.Total();i++)
     {
      CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh();
      if(series_obj.IsNewBar(0))
        {
         Print(TextByLanguage("Новый бар на ","New bar on "),series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe())," ",TimeToString(series_obj.Time(0)));
         if(series_obj.Timeframe()==Period())
            engine.PlaySoundByDescription(SND_NEWS);
        }
     }
  }
//+------------------------------------------------------------------+

Fügen Sie in der Bibliotheksinitialisierungsfunktion OnInitDoEasy() die Erstellung und Darstellung der Liste der verwendeten Zeitrahmen vom EA und die Darstellung des Zeitpunkts der Bibliotheksinitialisierung im Journal hinzu.
Unten finden Sie die vollständige Auflistung mit den farblich hervorgehobenen Änderungen und Zeichenketten, die zum besseren Verständnis mit Kommentaren versehen sind:

//+------------------------------------------------------------------+
//| Initializing DoEasy library                                      |
//+------------------------------------------------------------------+
void OnInitDoEasy()
  {
//--- Check if working with the full list is selected
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Внимание!","Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списков коллекций символов и таймсерий может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of lists of symbol collections and timeseries can take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Set the counter start point to measure the approximate library initialization time
   ulong begin=GetTickCount();
   Print(TextByLanguage("--- Инициализация библиотеки \"DoEasy\" ---","--- Initializing the \"DoEasy\" library ---"));
//--- Fill in the array of used symbols
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,InpUsedSymbols,array_used_symbols);
//--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries
   engine.SetUsedSymbols(array_used_symbols);
//--- Displaying the selected mode of working with the symbol object collection in the journal
   string num=
     (
      used_symbols_mode==SYMBOLS_MODE_CURRENT ? ": \""+Symbol()+"\"" : 
      TextByLanguage(". Количество используемых символов: ",". The number of symbols used: ")+(string)engine.GetSymbolsCollectionTotal()
     );
   Print(engine.ModeSymbolsListDescription(),num);
//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,1000);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   
//--- Set used timeframes
   CreateUsedTimeframesArray(InpModeUsedTFs,InpUsedTFs,array_used_periods);
//--- Display the selected mode of working with the timeseries object collection
   string mode=
     (
      InpModeUsedTFs==TIMEFRAMES_MODE_CURRENT   ? 
         TextByLanguage("Работа только с текущим таймфреймом: ","Work only with the current Period: ")+TimeframeDescription((ENUM_TIMEFRAMES)Period())   :
      InpModeUsedTFs==TIMEFRAMES_MODE_LIST      ? 
         TextByLanguage("Работа с заданным списком таймфреймов:","Work with a predefined list of Periods:")                                              :
      TextByLanguage("Работа с полным списком таймфреймов:","Work with the full list of all Periods:")
     );
   Print(mode);
//--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT)
      ArrayPrint(array_used_periods);
#endif 
//--- Create timeseries of all used symbols
   CArrayObj *list_timeseries=engine.GetListTimeSeries();
   int total=list_timeseries.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=list_timeseries.At(i);
      if(list_timeseries==NULL)
         continue;
      int total_periods=ArraySize(array_used_periods);
      for(int j=0;j<total_periods;j++)
        {
         ENUM_TIMEFRAMES timeframe=TimeframeByDescription(array_used_periods[j]);
         engine.SeriesSyncData(timeseries.Symbol(),timeframe);
         engine.SeriesCreate(timeseries.Symbol(),timeframe);
        }
     }
//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(true); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(false);   // Full descriptions

//--- Create resource text files
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red);

//--- Pass all existing collections to the trading class
   engine.TradingOnInit();

//--- Set the default magic number for all used symbols
   engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number));
//--- Set synchronous passing of orders for all used symbols
   engine.TradingSetAsyncMode(false);
//--- Set the number of trading attempts in case of an error
   engine.TradingSetTotalTry(InpTotalAttempts);
//--- Set correct order expiration and filling types to all trading objects
   engine.TradingSetCorrectTypeExpiration();
   engine.TradingSetCorrectTypeFilling();

//--- Set standard sounds for trading objects of all used symbols
   engine.SetSoundsStandart();
//--- Set the general flag of using sounds
   engine.SetUseSounds(InpUseSounds);
//--- Set the spread multiplier for symbol trading objects in the symbol collection
   engine.SetSpreadMultiplier(InpSpreadMultiplier);
      
//--- Set controlled values for symbols
   //--- Get the list of all collection symbols
   CArrayObj *list=engine.GetListAllUsedSymbols();
   if(list!=NULL && list.Total()!=0)
     {
      //--- In a loop by the list, set the necessary values for tracked symbol properties
      //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" 
      //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program
      /*
      for(int i=0;i<list.Total();i++)
        {
         CSymbol* symbol=list.At(i);
         if(symbol==NULL)
            continue;
         //--- Set control of the symbol price increase by 100 points
         symbol.SetControlBidInc(100000*symbol.Point());
         //--- Set control of the symbol price decrease by 100 points
         symbol.SetControlBidDec(100000*symbol.Point());
         //--- Set control of the symbol spread increase by 40 points
         symbol.SetControlSpreadInc(400);
         //--- Set control of the symbol spread decrease by 40 points
         symbol.SetControlSpreadDec(400);
         //--- Set control of the current spread by the value of 40 points
         symbol.SetControlSpreadLevel(400);
        }
      */
     }
//--- Set controlled values for the current account
   CAccount* account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      //--- Set control of the profit increase to 10
      account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0);
      //--- Set control of the funds increase to 15
      account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0);
      //--- Set profit control level to 20
      account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0);
     }
//--- Get the end of the library initialization time counting and display it in the journal
   ulong end=GetTickCount();
   Print(TextByLanguage("Время инициализации библиотеки: ","Library initialization time: "),TimeMSCtoString(end-begin,TIME_MINUTES|TIME_SECONDS));
  }
//+------------------------------------------------------------------+

Dies sind alle Verbesserungen des Test-EAs.
Kompilieren und starten Sie ihn unter Angabe der Verwendung des aktuellen Symbols und des aktuellen Zeitrahmens in den Parametern.
Die folgenden Meldungen werden im Journal angezeigt:

--- Initializing "DoEasy" library ---
Working with the current symbol only: "EURUSD"
Working with the current timeframe only: H4
EURUSD symbol timeseries: 
Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5330
Library initialization time: 00:00:00.141

Konfigurieren Sie unter Verwendung des aktuellen Symbols und der angegebenen Zeitrahmenliste in den Einstellungen (die Liste enthält die Hauptzeitrahmen).
Die folgenden Meldungen werden im Journal angezeigt:

--- Initializing "DoEasy" library ---
Working with the current symbol only: "EURUSD"
Working with the specified timeframe list:
"M1"  "M5"  "M15" "M30" "H1"  "H4"  "D1"  "W1"  "MN1"
EURUSD symbol timeseries: 
Timeseries "EURUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3286
Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3566
Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3109
Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2894
Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5505
Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5330
Timeseries "EURUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5087
Timeseries "EURUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2564
Timeseries "EURUSD" MN1: Requested: 1000, Actual: 590, Created: 590, On the server: 590
Library initialization time: 00:00:00.032

Konfigurieren Sie unter Verwendung des aktuellen Symbols und der vollständigen Liste der Zeitrahmen.
Die folgenden Meldungen werden im Journal angezeigt:

--- Initializing "DoEasy" library ---
Working with the current symbol only: "EURUSD"
Working with the full list of timeframes:
"M1"  "M2"  "M3"  "M4"  "M5"  "M6"  "M10" "M12" "M15" "M20" "M30" "H1"  "H2"  "H3"  "H4"  "H6"  "H8"  "H12" "D1"  "W1"  "MN1"
EURUSD symbol timeseries: 
Timeseries "EURUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3390
Timeseries "EURUSD" M2: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5626
Timeseries "EURUSD" M3: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4713
Timeseries "EURUSD" M4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4254
Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3587
Timeseries "EURUSD" M6: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4805
Timeseries "EURUSD" M10: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4035
Timeseries "EURUSD" M12: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3842
Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3116
Timeseries "EURUSD" M20: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3457
Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2898
Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5507
Timeseries "EURUSD" H2: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6303
Timeseries "EURUSD" H3: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6263
Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5331
Timeseries "EURUSD" H6: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5208
Timeseries "EURUSD" H8: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5463
Timeseries "EURUSD" H12: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5205
Timeseries "EURUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5087
Timeseries "EURUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2564
Timeseries "EURUSD" MN1: Requested: 1000, Actual: 590, Created: 590, On the server: 590
Library initialization time: 00:00:00.094

Konfigurieren Sie unter Verwendung der angegebenen Symbolliste und setzen Sie in den Einstellungen drei Symbole EURUSD,AUDUSD,EURAUD. Konfigurieren Sie auch unter Verwendung der angegebenen Zeitrahmenliste (Hauptzeitrahmen werden in der Liste angegeben).
Die folgenden Meldungen werden im Journal angezeigt:

--- Initializing "DoEasy" library ---
Working with predefined symbol list. The number of used symbols: 3
"AUDUSD" "EURUSD" "EURAUD"
Working with the specified timeframe list:
"M1"  "M5"  "M15" "M30" "H1"  "H4"  "D1"  "W1"  "MN1"
AUDUSD symbol timeseries: 
Timeseries "AUDUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3394
Timeseries "AUDUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4024
Timeseries "AUDUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3262
Timeseries "AUDUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3071
Timeseries "AUDUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5104
Timeseries "AUDUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5026
Timeseries "AUDUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5289
Timeseries "AUDUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 1401
Timeseries "AUDUSD" MN1: Requested: 1000, Actual: 323, Created: 323, On the server: 323
EURAUD symbol timeseries: 
Timeseries "EURAUD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3393
Timeseries "EURAUD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4025
Timeseries "EURAUD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3262
Timeseries "EURAUD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3071
Timeseries "EURAUD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5104
Timeseries "EURAUD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5026
Timeseries "EURAUD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4071
Timeseries "EURAUD" W1: Requested: 1000, Actual: 820, Created: 820, On the server: 820
Timeseries "EURAUD" MN1: Requested: 1000, Actual: 189, Created: 189, On the server: 189
EURUSD symbol timeseries: 
Timeseries "EURUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3394
Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3588
Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3116
Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2898
Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5507
Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5331
Timeseries "EURUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5087
Timeseries "EURUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2564
Timeseries "EURUSD" MN1: Requested: 1000, Actual: 590, Created: 590, On the server: 590
Library initialization time: 00:00:00.266

Wie wir sehen können, werden die erforderlichen Zeitreihen in Abhängigkeit von den in den EA-Einstellungen angegebenen Symbolen und Zeitrahmen erstellt. Die Erstellungszeit der Zeitreihen hängt vom EA-Start (kalt/warm) sowie davon ab, ob die ausgewählten Symbole und deren Zeitrahmen zuvor verwendet wurden.

Was kommt als Nächstes?

Im nächsten Artikel werden wir die Funktionsweise für Echtzeit-Updates der erstellten Zeitserien und für das Senden von Nachrichten über das Ereignis "New bar" an das Programm für alle verwendeten Zeitserien sowie für den Empfang der erforderlichen Daten aus den vorhandenen Zeitserien 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

Frühere Artikel dieser Serie:

Zeitreihen in der Bibliothek DoEasy (Teil 35): das Bar-Objekt und die Liste der Zeitreihen eines Symbols
Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolperioden


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7663

Beigefügte Dateien |
MQL5.zip (3693.82 KB)
MQL4.zip (3693.83 KB)
Anwendung von OLAP im Handel (Teil 4): Quantitative und visuelle Analyse der Testberichte Anwendung von OLAP im Handel (Teil 4): Quantitative und visuelle Analyse der Testberichte

Der Artikel bietet grundlegende Werkzeuge für die OLAP-Analyse von Testberichten in Bezug auf einzelne Durchläufe und Optimierungsergebnisse. Das Werkzeug kann mit Dateien im Standardformat (tst und opt) arbeiten und bietet auch eine grafische Schnittstelle. MQL-Quellcodes sind unten angefügt.

Kontinuierliche Walk-Forward-Optimierung (Teil 5): Projektübersicht Auto-Optimizer und Erstellen einer GUI Kontinuierliche Walk-Forward-Optimierung (Teil 5): Projektübersicht Auto-Optimizer und Erstellen einer GUI

Dieser Artikel bietet eine weitere Beschreibung der Walk-Forward-Optimierung im MetaTrader 5-Terminal. In früheren Artikeln betrachteten wir Methoden zur Erstellung und Filterung des Optimierungsberichts und begannen mit der Analyse der internen Struktur der für den Optimierungsprozess verantwortlichen Anwendung. Der Auto-Optimizer ist als C#-Anwendung implementiert und verfügt über eine eigene grafische Oberfläche. Der fünfte Artikel ist der Erstellung dieser grafischen Oberfläche gewidmet.

Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen

Im vorherigen Artikel haben wir den visuellen Teil der Anwendung sowie die grundlegende Interaktion der GUI-Elementen entwickelt. Diesmal fügen wir die interne Logik und den Algorithmus der Vorbereitung der Handelssignaldaten hinzu, sowie die Fähigkeit, Signale einzurichten, sie zu durchsuchen und auf dem Monitor zu visualisieren.

Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm

Der Artikel befasst sich mit der Echtzeit-Aktualisierung von Zeitreihendaten und dem Senden von Meldungen über das Ereignis "New bar" an die Kontrollprogramm auf dem Chart aus allen Zeitreihen aller Symbole, um diese Ereignisse in benutzerdefinierten Programmen handhaben zu können. Die Klasse "New tick" wird verwendet, um die Notwendigkeit der Aktualisierung der Zeitreihen von Symbolen und Perioden zu bestimmen, die nicht dem aktuellen Chart entsprechen.