![Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen](https://c.mql5.com/i/articles/overlay.png)
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen
Inhalt
- Konzept
- Verbesserung der zuvor erstellten Zeitreihenobjekte
- Kollektionsklasse von Zeitreihenobjekten nach Symbolen und Zeitrahmen
- Tests
- Was kommt als Nächstes?
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:
- Der "Einzelperiodenbalken eines einzelnen Symbols" CBar umfasst die Daten eines einzelnen Balkens eines bestimmten Symbols eines bestimmten Zeitraums;
- Die "Einzelperiodenzeitreihe eines einzelnen Symbols" CSeries ermöglicht die Kollektionsliste der Balken (1) eines einzelnen Zeitrahmens eines einzelnen Symbols;
- 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.
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 Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7663
![Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen](https://c.mql5.com/2/38/Article_Logo.png)
![Anwendung von OLAP im Handel (Teil 4): Quantitative und visuelle Analyse der Testberichte](https://c.mql5.com/2/38/OLAP_in_trading.png)
![Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm](https://c.mql5.com/2/38/MQL5-avatar-doeasy-library__3.png)
![Kontinuierliche Walk-Forward-Optimierung (Teil 5): Projektübersicht Auto-Optimizer und Erstellen einer GUI](https://c.mql5.com/2/38/MQL5-avatar-continuous_optimization__1.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.