Zeitreihen in der Bibliothek DoEasy (Teil 52): Plattformübergreifende Eigenschaft für Standardindikatoren mit einem Puffer für mehrere Symbole und Perioden
Inhaltsverzeichnis
Konzept
Beginnend mit haben wir Schritt für Schritt die Funktionen geschaffen, um eigene Multi-Symbol-Multi-Perioden-Indikatoren zu konstruieren. Auf der so geschaffenen Basis war es naheliegend, auch Standard-Indikatoren in Multimodi arbeiten zu lassen. Und beginnend mit Artikel 47 haben wir solche Funktionen geschaffen (es gibt Mängel, die wir nach und nach finden und beheben müssen). Aber alles, was wir gemacht haben, wurde für die Plattform MetaTrader 5 gemacht.
Leichte Verbesserung der Bibliotheksklassen in Bezug auf Indikatoren, sodass die für die veraltete Plattform MetaTrader 4 entwickelten Programme, die auf dieser Bibliothek basieren, beim Umstieg auf MetaTrader 5 normal funktionieren können.
Im Gegensatz zu MQL5 können wir in MQL4 nicht mehrere Farben für einen einzigen Puffer zeichnen. In MetaTrader 4 sind alle Indikatorpuffer monochrom. Diese Einschränkung wirkt sich auch auf das Konzept der Konstruktion von mehreren Puffern für MetaTrader 4 aus. Hier können wir nicht seine Zeichenfarbe für einen bestimmten Balken festlegen. In MetaTrader 5 hingegen ist dies einfach. Hier müssen wir für jede Farbe einen monochromen Indikatorpuffer verwenden. Wohingegen wir keine zusätzlichen Berechnungspuffer erstellen müssen, um Indikatordaten aus dem angegebenen Symbol/Periode des Charts zu speichern - alle Funktionen der Anfrage an den Indikator in MQL4 geben den Wert auf dem angegebenen Indikatorbalken aus dem angegebenen Symbol/Periode zurück. Und in MQL5 muss man ein Indikator-Handle erstellen und über dieses Handle die Daten anfordern, indem man die benötigte Menge in das Ziel-Array kopiert. Für Indikatoren ist es sein Berechnungspuffer. Und danach holt man die Daten des spezifizierten Indikators aus diesem Array durch den erforderlichen Balkenindex. Im Gegensatz dazu funktioniert in MQL5 der Zugriff auf Indikatordaten schneller.
So unterscheidet sich die Konstruktion des Pufferobjekts für Standardindikatoren in MQL4. Es besteht keine Notwendigkeit, zusätzliche Berechnungspuffer zu erstellen, um Informationen mit Daten des Indikators zu speichern, die auf dem aktuellen Chart angezeigt werden sollen. Aber bei der scheinbaren Vereinfachung verlieren wir an Flexibilität: um einen farbigen Puffer für jede Farbe zu erstellen, müssen wir einen eigenen einfarbigen Indikatorpuffer haben und jetzt, bei der Angabe der notwendigen Balkenfarbe soll der Puffer, der der Farbe entspricht, angezeigt werden. Die restlichen Puffer werden ausgeblendet. Und das ist eine Komplikation.
Basierend auf dem oben genannten wird das Konzept der Konstruktion von mehreren Puffern für MQL4 wie folgt sein:
- Wir werden einen individuellen Indikatorpuffer für jede Farbe einer Indikatorlinie haben.
- Zum Umschalten der Linienfarbe wird eine Indikatorlinie (Puffer), die der erforderlichen Farbe entspricht, angezeigt, während alle übrigen Linien desselben Indikators, die sich auf andere Farben beziehen, ausgeblendet werden.
Um das oben Gesagte zusammenzufassen, erhalten wir für den farbigen Multi-Symbol-Multi-Perioden-Indikator Gleitender Durchschnitt, der drei Farben für die Anzeige seiner Linie hat:
In MQL5 werden wir drei Daten-Arrays (drei Puffer) haben:
- zu zeichnender Puffer (Daten werden im Datenfenster angezeigt)
- Farbpuffer (wird nicht im Datenfenster angezeigt, aber er gibt an, mit welchem Farbpuffer 1 Linie auf jedem Balken gezeichnet wird)
- Berechnungspuffer für die Datenspeicherung Gleitender Durchschnitt aus dem angegebenen Symbol/Periode (wird nicht im Datenfenster angezeigt)
In MQL4 werden wir drei Daten-Arrays (drei Puffer) haben:
- zu zeichnender Puffer für Farbe 1 (Daten werden im Datenfenster angezeigt)
- zu zeichnender Puffer für Farbe 2 (Daten werden im Datenfenster angezeigt)
- zu zeichnender Puffer für Farbe 3 (Daten werden im Datenfenster angezeigt)
Wenn die Anzahl der Farben reduziert wird, wird die Anzahl der Puffer für MQL4 reduziert, bei einer Erhöhung wird die Anzahl erhöht. In MQL5 wird die Anzahl der Puffer für dieses Beispiel immer gleich 3 sein. Wohingegen MQL5 es ermöglicht, die Anzahl der Farben dynamisch bis zu 64 zu ändern. In MQL4 ist das Übermalen von Indikatorlinien nicht so einfach. Der Grund ist, dass 64 Puffer für 64 Farben erstellt werden müssen, und das nur für eine Linie. Wenn ein Indikator 4 Linien hat, werden 256 Indikatorpuffer-Arrays benötigt. Für acht Linien würden 512 Puffer benötigt werden, was die Obergrenze ist. Wie bei MQL5 wird für jeden Balken der Index der entsprechenden Farbe angegeben und auf diesem Balken wird die Linie in der angegebenen Farbe gemalt. Bei MQL4 wird der Puffer, der der Farbe entspricht, angezeigt, die anderen werden ausgeblendet. Und in MQL4 werden alle Puffer für jede Farbe im Datenfenster des Terminals sichtbar sein. In MQL5, wie für dieses Beispiel, wird ein Puffer im Terminaldatenfenster sichtbar sein, was korrekt ist: eine Indikatorlinie = ein Wert im Terminaldatenfenster.
Wir werden nicht sofort alles korrigieren, was bereits getan wird. Stattdessen allmählich, Schritt für Schritt von einfach bis komplex, verbessern wie die Bibliothek Klassen, um die Kompatibilität bestimmter Aspekte der Arbeit mit Indikatoren in der Bibliothek mit MQL4 zu erreichen. Heute wird am Beispiel des Indikators Akkumulation/Ausschüttung das Erstellen eines einpufferigen, monochromen Multi-Symbol-Multi-Perioden-Standard-Indikators in MQL4 unter Verwendung der Bibliothek geprüft.
Verbesserung der Bibliothek der Klasse
Wie gewöhnlich, fügen wir zuerst die notwendigen Textmeldungen hinzu. Früher haben wir, gleich im endgültigen Indikatorprogramm, die Übereinstimmung der von der Bibliothek erzeugten Indikatorpuffer mit den Einträgen im Indikatorcode über die notwendige Anzahl von Indikatorpuffern geprüft:
#property indicator_buffers 3 #property indicator_plots 1
Weiterhin wird im Code, nachdem die Bibliothek alle benötigten Puffer angelegt hat, eine Überprüfung durchgeführt:
//--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
Verschieben wir diese Prüfung, die für die MQL4-Kompatibilität leicht verbessert wurde, in die Bibliothek. Wir legen die Texte, die innerhalb der Prüfung angezeigt werden, an die entsprechende Stelle in der Bibliothek - in die Datei \MQL5\Include\DoEasy\Data.mqh und fügen die Indizes der neuen Meldungen hinzu:
Übersetzt mit www.DeepL.com/Translator (kostenlose Version) Value \"indicator_plots\" must be
MSG_ENG_ERR_VALUE_ORDERS, // Attention! Value \"indicator_buffers\" must be
und Nachrichtentexte, die den neu hinzugefügten Indizes entsprechen:
//--- CEngine {"There have been no trade events since the last launch of EA"}, {"Failed to get the description of the last trading event"}, {"Failed to get open positions list"}, {"Failed to get pending orders list"}, {"No open positions"}, {"No placed orders"}, {"Attention! Value of \"indicator_plots\" should be "}, {"Attention! Value of \"indicator_buffers\" should be "},
Die Datei, die Daten für Programmeingaben enthält, heißt InpDatas.mqh... Wir ändern diesen Namen in einen korrekten Namen im Sinne der englischen Grammatik (ich habe einen Fehler bei der Benennung der Datei gemacht). Nun heißt diese Datei wie folgt: \MQL5\Include\DoEasy\InpData.mqh.
Benennen Sie sie einfach in dem Ordner um, in dem sie sich befindet.
Diese Datei wird mit der Bibliothek in der Datei Data.mqh (die wir jetzt bearbeiten) verbunden und wir passen die Einbindung an:
//+------------------------------------------------------------------+ //| Data.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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "InpData.mqh" //+------------------------------------------------------------------+
Beginnen wir mit der Implementierung der plattformübergreifenden Eigenschaft.
Wenn wir versuchen, die Bibliothek im MetaEditor von MetaTrader 4 zu kompilieren (F7 auf der Datei Engine.mqh), erhalten wir eine Vielzahl von Fehlern:
Nun, das ist keine Überraschung. Fangen wir einfach der Reihe nach an. Zunächst fällt auf, dass MQL4 einige Konstanten und Enumerationen nicht kennt.
Wir fügen die neuen Konstanten und Enumerationen zur Datei \MQL5\Include\DoEasy\zuMQL4.mqh ein:
//+------------------------------------------------------------------+ //| ToMQL4.mqh | //| Copyright 2017, Artem A. Trishkin, Skype artmedia70 | //| https://www.mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70" #property link "https://www.mql5.com/en/users/artmedia70" #property strict #ifdef __MQL4__ //+------------------------------------------------------------------+ //| Error codes | //+------------------------------------------------------------------+ #define ERR_SUCCESS (ERR_NO_ERROR) #define ERR_MARKET_UNKNOWN_SYMBOL (ERR_UNKNOWN_SYMBOL) #define ERR_ZEROSIZE_ARRAY (ERR_ARRAY_INVALID) #define ERR_MAIL_SEND_FAILED (ERR_SEND_MAIL_ERROR) #define ERR_NOTIFICATION_SEND_FAILED (ERR_NOTIFICATION_ERROR) #define ERR_FTP_SEND_FAILED (ERR_FTP_ERROR) //+------------------------------------------------------------------+ //| Order types, filling policy, period, reasons | //+------------------------------------------------------------------+ #define ORDER_TYPE_CLOSE_BY (8) #define ORDER_TYPE_BUY_STOP_LIMIT (9) #define ORDER_TYPE_SELL_STOP_LIMIT (10) #define ORDER_REASON_EXPERT (3) #define ORDER_REASON_SL (4) #define ORDER_REASON_TP (5) #define ORDER_REASON_BALANCE (6) #define ORDER_REASON_CREDIT (7) //+------------------------------------------------------------------+ //| Flags of allowed order expiration modes | //+------------------------------------------------------------------+ #define SYMBOL_EXPIRATION_GTC (1) #define SYMBOL_EXPIRATION_DAY (2) #define SYMBOL_EXPIRATION_SPECIFIED (4) #define SYMBOL_EXPIRATION_SPECIFIED_DAY (8) //+------------------------------------------------------------------+ //| Flags of allowed order filling modes | //+------------------------------------------------------------------+ #define SYMBOL_FILLING_FOK (1) #define SYMBOL_FILLING_IOC (2) //+------------------------------------------------------------------+ //| The flags of allowed order types | //+------------------------------------------------------------------+ #define SYMBOL_ORDER_MARKET (1) #define SYMBOL_ORDER_LIMIT (2) #define SYMBOL_ORDER_STOP (4) #define SYMBOL_ORDER_STOP_LIMIT (8) #define SYMBOL_ORDER_SL (16) #define SYMBOL_ORDER_TP (32) #define SYMBOL_ORDER_CLOSEBY (64) //+------------------------------------------------------------------+ //| Indicator lines IDs | //+------------------------------------------------------------------+ #define TENKANSEN_LINE (0) #define KIJUNSEN_LINE (1) #define SENKOUSPANA_LINE (2) #define SENKOUSPANB_LINE (3) #define CHIKOUSPAN_LINE (4) //+------------------------------------------------------------------+ //| Deal types of MQL5 | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Position change method | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Open position direction | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Order status | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Margin calculation mode | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Prices on which basis chart by symbol is constructed | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Life span of pending orders and | //| set levels of StopLoss/TakeProfit | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Options types | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Right provided by an option | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Method of margin value calculation for an instrument | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Swap charge methods at position move | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Trading operations types | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Order filling policies | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Order life span | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Integer properties of selected position | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Real properties of selected position | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| String properties of selected position | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Technical indicator types | //+------------------------------------------------------------------+ enum ENUM_INDICATOR { IND_AC = 5, IND_AD = 6, IND_ALLIGATOR = 7, IND_ADX = 8, IND_ADXW = 9, IND_ATR = 10, IND_AO = 11, IND_BEARS = 12, IND_BANDS = 13, IND_BULLS = 14, IND_CCI = 15, IND_DEMARKER = 16, IND_ENVELOPES = 17, IND_FORCE = 18, IND_FRACTALS = 19, IND_GATOR = 20, IND_ICHIMOKU = 21, IND_BWMFI = 22, IND_MACD = 23, IND_MOMENTUM = 24, IND_MFI = 25, IND_MA = 26, IND_OSMA = 27, IND_OBV = 28, IND_SAR = 29, IND_RSI = 30, IND_RVI = 31, IND_STDDEV = 32, IND_STOCHASTIC = 33, IND_VOLUMES = 34, IND_WPR = 35, IND_DEMA = 36, IND_TEMA = 37, IND_TRIX = 38, IND_FRAMA = 39, IND_AMA = 40, IND_CHAIKIN = 41, IND_VIDYA = 42, IND_CUSTOM = 43, }; //+------------------------------------------------------------------+ //| Drawing style | //+------------------------------------------------------------------+ enum ENUM_DRAW_TYPE { DRAW_COLOR_LINE = DRAW_LINE, // MQL5 = 1, MQL4 = 0 DRAW_COLOR_HISTOGRAM = DRAW_HISTOGRAM, // MQL5 = 2, MQL4 = 2 DRAW_COLOR_ARROW = DRAW_ARROW, // MQL5 = 3, MQL4 = 3 DRAW_COLOR_SECTION = DRAW_SECTION, // MQL5 = 4, MQL4 = 1 DRAW_COLOR_HISTOGRAM2 = DRAW_NONE, // MQL5 = 0, MQL4 = 12 DRAW_COLOR_ZIGZAG = DRAW_ZIGZAG, // MQL5 = 6, MQL4 = 4 DRAW_COLOR_BARS = DRAW_NONE, // MQL5 = 0, MQL4 = 12 DRAW_COLOR_CANDLES = DRAW_NONE, // MQL5 = 0, MQL4 = 12 // DRAW_FILLING MQL4 = 5 DRAW_COLOR_NONE = DRAW_NONE, // MQL5 = 0, MQL4 = 12 }; //+------------------------------------------------------------------+ //| Volume type | //+------------------------------------------------------------------+ enum ENUM_APPLIED_VOLUME { VOLUME_TICK, VOLUME_REAL }; //+------------------------------------------------------------------+ //| Trade request structure | //+------------------------------------------------------------------+ //--- the code has been further removed for the sake of space #endif
Weiterhin kommt es bei der folgenden Kompilierung zu Fehlern durch das Fehlen von mql5-Funktionen in MQL4. Insbesondere BarsCalculated(). Diese Funktion gibt die Anzahl der vom Indikator berechneten Balken durch seinen Handle zurück. MQL4 ist dies unbekannt. Die mql4-Funktion Bars(), die die Anzahl der vorhandenen Balken der angegebenen Zeitreihe zurückgibt, ist der Bedeutung der Funktion BarsCalculated() am nächsten.
Da in MQL4 gilt, dass der Indikator bei der Abfrage an ihn bereits berechnet ist, können wir die Menge der berechneten Daten des Indikators ( MQL5 BarsCalculated() ) durch die Anzahl der verfügbaren Balken der Zeitreihe ( MQL4 Bars() ) ersetzen. In jedem Fall geben die Bibliotheksmethoden beim Abrufen von Indikatordaten die empfangenen Daten zurück und verifizieren sie. Besprechen wir also, dass die Angabe der verfügbaren Balken der Zeitreihe die Menge der berechneten Daten des Indikators in MQL4 ersetzen kann.
Die Methode IndicatorBarsCalculated(), die die Funktion BarsCalculated() verwendet, befindet sich in der Datei \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh. Und sofort werden wir an gleicher Stelle eine große Anzahl von Verbesserungen an anderen Methoden der Arbeit mit Indikatoren vornehmen müssen.
Früher wurde die Methode vollständig in den Klassenkörper geschrieben, wo die Menge der berechneten Daten sofort zurückgegeben wurde:
ENUM_INDICATOR IndicatorType(void) const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE); } string IndicatorName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME); } string IndicatorShortName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } int IndicatorBarsCalculated(void) const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));} int IndicatorLineAdditionalNumber(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM); } int IndicatorLineMode(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE); }
Lassen wir jetzt nur die Methodendeklaration stehen
ENUM_INDICATOR IndicatorType(void) const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE); } string IndicatorName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME); } string IndicatorShortName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } int IndicatorLineAdditionalNumber(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM); } int IndicatorLineMode(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE); } int IndicatorBarsCalculated(void);
... und verschieben seine Implementierung aus dem Klassenkörper:
//+------------------------------------------------------------------+ //| Return the number of standard indicator calculated bars | //+------------------------------------------------------------------+ int CBuffer::IndicatorBarsCalculated(void) { return(#ifdef __MQL5__ ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE)) #else ::Bars(this.Symbol(),this.Timeframe()) #endif); } //+------------------------------------------------------------------+
Hier geben für MQL5 die Menge der berechneten Indikatordaten zurück, und für MQL4 — die Menge der verfügbaren Daten in der Zeitreihe.
Teilen wir jetzt noch den Konstruktor der geschlossenen parametrischen Klasse in zwei Teile.
Der erste Teil, der bereits vorhandene, bleibt nur für MQL5, und für MQL4 machen wir eine Kopie des mql5-Codes und löschen Unnötiges:
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status, ENUM_BUFFER_TYPE buffer_type, const uint index_plot, const uint index_base_array, const int num_datas, const uchar total_arrays, const int width, const string label) { #ifdef __MQL5__ this.m_type=COLLECTION_BUFFERS_ID; this.m_act_state_trigger=true; this.m_total_arrays=total_arrays; //--- Save integer properties this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_ID] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM] = WRONG_VALUE; ENUM_DRAW_TYPE type= ( !this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8) ); this.m_long_prop[BUFFER_PROP_DRAW_TYPE] = type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = true; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0x9F; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = 0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = width; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0); this.m_long_prop[BUFFER_PROP_COLOR] = clrRed; this.m_long_prop[BUFFER_PROP_NUM_DATAS] = num_datas; this.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; this.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; this.m_long_prop[BUFFER_PROP_INDEX_COLOR] = this.GetProperty(BUFFER_PROP_INDEX_BASE)+ (this.TypeBuffer()!=BUFFER_TYPE_CALCULATE ? this.GetProperty(BUFFER_PROP_NUM_DATAS) : 0); this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE] = index_base_array+this.m_total_arrays; this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot); //--- Save real properties this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0); //--- Save string properties this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)] = ::Symbol(); this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL); this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)] = NULL; this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]=NULL; //--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- If failed to change the size of the color array (only for a non-calculated buffer), display the appropriate message indicating the string if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE) if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- For DRAW_FILLING, fill in the color array with two default colors if(this.Status()==BUFFER_STATUS_FILLING) { this.SetColor(clrBlue,0); this.SetColor(clrRed,1); } //--- Bind indicator buffers with arrays //--- In a loop by the number of indicator buffers int total=::ArraySize(DataBuffer); for(int i=0;i<total;i++) { //--- calculate the index of the next array and //--- bind the indicator buffer by the calculated index with the dynamic array //--- located by the i loop index in the DataBuffer array int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i; ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS)); //--- Set indexation flag as in the timeseries to all buffer arrays ::ArraySetAsSeries(this.DataBuffer[i].Array,true); } //--- Bind the color buffer with the array (only for a non-calculated buffer and not for the filling buffer) if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) { ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX); ::ArraySetAsSeries(this.ColorBufferArray,true); } //--- Done if this is a calculated buffer if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Set integer parameters of the graphical series ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH)); this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR)); //--- Set real parameters of the graphical series ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Set string parameters of the graphical series ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL)); //--- MQL4 #else this.m_type=COLLECTION_BUFFERS_ID; this.m_act_state_trigger=true; this.m_total_arrays=1; //--- Save integer properties this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_ID] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM] = WRONG_VALUE; ENUM_DRAW_TYPE type=DRAW_COLOR_NONE; switch((int)this.Status()) { case BUFFER_STATUS_LINE : type=DRAW_COLOR_LINE; break; case BUFFER_STATUS_HISTOGRAM : type=DRAW_COLOR_HISTOGRAM; break; case BUFFER_STATUS_ARROW : type=DRAW_COLOR_ARROW; break; case BUFFER_STATUS_SECTION : type=DRAW_COLOR_SECTION; break; case BUFFER_STATUS_ZIGZAG : type=DRAW_COLOR_ZIGZAG; break; case BUFFER_STATUS_NONE : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_FILLING : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_HISTOGRAM2 : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_BARS : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_CANDLES : type=DRAW_COLOR_NONE; break; default : type=DRAW_COLOR_NONE; break; } this.m_long_prop[BUFFER_PROP_DRAW_TYPE] = type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = true; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0x9F; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = 0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = width; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0); this.m_long_prop[BUFFER_PROP_COLOR] = clrRed; this.m_long_prop[BUFFER_PROP_NUM_DATAS] = num_datas; this.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; this.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; this.m_long_prop[BUFFER_PROP_INDEX_COLOR] = this.GetProperty(BUFFER_PROP_INDEX_BASE); this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE] = index_base_array+this.m_total_arrays; this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot); //--- Save real properties this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0); //--- Save string properties this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)] = ::Symbol(); this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL); this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)] = NULL; this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]=NULL; //--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- Bind indicator buffers with arrays //--- In a loop by the number of indicator buffers int total=::ArraySize(DataBuffer); for(int i=0;i<total;i++) { //--- calculate the index of the next array and //--- bind the indicator buffer by the calculated index with the dynamic array //--- located by the i loop index in the DataBuffer array int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i; ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS)); //--- Set indexation flag as in the timeseries to all buffer arrays ::ArraySetAsSeries(this.DataBuffer[i].Array,true); } //--- Done if this is a calculated buffer if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Set integer parameters of the graphical series this.SetDrawType(type); ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR)); ::SetIndexArrow((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::SetIndexDrawBegin((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); //--- Set real parameters of the graphical series ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Set string parameters of the graphical series ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_LABEL)); #endif } //+------------------------------------------------------------------+
Hier liegt der Hauptunterschied in der Berechnung des Zeichnungstyps. Bei MQL5 berechnen wir ihn aus dem Puffertyp (seinem Status), während hier das Setzen der entsprechenden Werte einfacher war. Um die benötigten Werte für den Indikatorpuffer zu setzen, verwenden wir die entsprechenden MQL4-Funktionen, da die MQL5-Funktionen PlotIndexSetInteger(), PlotIndexSetDouble() und PlotIndexSetString() zwar keine Kompilierungsfehler verursachen, aber gleichzeitig die benötigten Werte für den Indikatorpuffer in MQL4 nicht setzen.
In gleicher Weise trennen wir den mql5- und mql4-Code der Methoden für das Setzen bestimmter Eigenschaften für Indikator-Puffer für die Verwendung der entsprechenden Funktionen für beide Sprachen:
//+------------------------------------------------------------------+ //--- Set the graphical construction type by type and status | //+------------------------------------------------------------------+ void CBuffer::SetDrawType(void) { ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? (ENUM_DRAW_TYPE)DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? (ENUM_DRAW_TYPE)DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8)); this.SetProperty(BUFFER_PROP_DRAW_TYPE,type); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),type,EMPTY,EMPTY,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the passed graphical construction type | //+------------------------------------------------------------------+ void CBuffer::SetDrawType(const ENUM_DRAW_TYPE draw_type) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_DRAW_TYPE,draw_type); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,draw_type); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),draw_type,EMPTY,EMPTY,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the number of initial bars | //| without drawing and values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetDrawBegin(const int value) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value); #else ::SetIndexDrawBegin((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),value); #endif } //+------------------------------------------------------------------+ //| Set the flag of displaying | //| construction values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetShowData(const bool flag) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_SHOW_DATA,flag); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,flag); } //+------------------------------------------------------------------+ //| Set the indicator graphical construction shift | //+------------------------------------------------------------------+ void CBuffer::SetShift(const int shift) { this.SetProperty(BUFFER_PROP_SHIFT,shift); if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift); #else ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),shift); #endif } //+------------------------------------------------------------------+ //| Set the line style | //+------------------------------------------------------------------+ void CBuffer::SetStyle(const ENUM_LINE_STYLE style) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_LINE_STYLE,style); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,style); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),style,EMPTY,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the line width | //+------------------------------------------------------------------+ void CBuffer::SetWidth(const int width) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_LINE_WIDTH,width); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,width); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),EMPTY,width,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the number of colors | //+------------------------------------------------------------------+ void CBuffer::SetColorNumbers(const int number) { if(number>IND_COLORS_TOTAL || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : 2); this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n); ::ArrayResize(this.ArrayColors,n); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n); } //+------------------------------------------------------------------+ //| Set a single specified drawing color for the buffer | //+------------------------------------------------------------------+ void CBuffer::SetColor(const color colour) { if(this.Status()==BUFFER_STATUS_FILLING || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetColorNumbers(1); this.SetProperty(BUFFER_PROP_COLOR,colour); this.ArrayColors[0]=colour; #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,0,this.ArrayColors[0]); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),EMPTY,EMPTY,this.ArrayColors[0]); #endif } //+------------------------------------------------------------------+ //| Set the drawing color to the specified color index | //+------------------------------------------------------------------+ void CBuffer::SetColor(const color colour,const uchar index) { #ifdef __MQL5__ if(index>IND_COLORS_TOTAL-1 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; if(index>this.ColorsTotal()-1) this.SetColorNumbers(index+1); this.ArrayColors[index]=colour; if(index==0) this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,index,this.ArrayColors[index]); #else #endif } //+------------------------------------------------------------------+ //| Set drawing colors from the color array | //+------------------------------------------------------------------+ void CBuffer::SetColors(const color &array_colors[]) { #ifdef __MQL5__ //--- Exit if the passed array is empty if(::ArraySize(array_colors)==0 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Copy the passed array to the array of buffer object colors ::ArrayCopy(this.ArrayColors,array_colors,0,0,IND_COLORS_TOTAL); //--- Exit if the color array was empty and not copied for some reason int total=::ArraySize(this.ArrayColors); if(total==0) return; //--- If the drawing style is not DRAW_FILLING if(this.Status()!=BUFFER_STATUS_FILLING) { //--- if the new number of colors exceeds the currently set one, //--- set the new value for the number of colors if(total>this.ColorsTotal()) this.SetColorNumbers(total); } //--- If the drawing style is DRAW_FILLING, set the number of colors equal to 2 else total=2; //--- Set the very first color from the color array (for a single color) to the buffer object color property this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]); //--- Set the new number of colors for the indicator buffer ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,total); //--- In the loop by the new number of colors, set all colors by their indices for the indicator buffer for(int i=0;i<total;i++) ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,i,this.ArrayColors[i]); #else #endif } //+------------------------------------------------------------------+ //| Set the "empty" value for construction | //| without drawing | //+------------------------------------------------------------------+ void CBuffer::SetEmptyValue(const double value) { this.SetProperty(BUFFER_PROP_EMPTY_VALUE,value); if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) #ifdef __MQL5__ ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value); #else ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),value); #endif } //+------------------------------------------------------------------+ //| Set the name for the graphical indicator series | //+------------------------------------------------------------------+ void CBuffer::SetLabel(const string label) { this.SetProperty(BUFFER_PROP_LABEL,label); if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) #ifdef __MQL5__ ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label); #else ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),label); #endif } //+------------------------------------------------------------------ //+------------------------------------------------------------------+ //| Set the color index to the specified timeseries index | //| of the color buffer array | //+------------------------------------------------------------------+ void CBuffer::SetBufferColorIndex(const uint series_index,const uchar color_index) { #ifdef __MQL4__ return; #endif if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING || this.Status()==BUFFER_STATUS_NONE) return; int data_total=this.GetDataTotal(0); int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); if(::ArraySize(this.ColorBufferArray)==0) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF)); if(data_index<0) return; this.ColorBufferArray[data_index]=color_index; } //+------------------------------------------------------------------+
Im Berechnungspuffer, in der Klasse CBufferCalculate in der Datei \MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqh gibt es drei Methoden, die Daten vom Indikator-Handle in das Datenarray des Berechnungspuffers kopieren. Die Methoden geben die Menge der kopierten Daten zurück. Da für MQL4 das Kopieren von Daten aus dem Indikator-Handle nicht benötigt wird und wir sie einfach über entsprechende Standard-Mql4-Funktionen mit Angabe von Symbol, Timeframe und Balken-Nummer erhalten, müssen wir in diesen Methoden einen bestimmten Dummy-Wert zurückgeben, der das erfolgreiche Kopieren anzeigt.
In der Methode übergeben wir die Anzahl der für das Kopieren benötigten Balken und geben das Flag zurück, das anzeigt, dass die kopierten Daten gleich diesem Wert sind.
Für MQL4 geben wir einfach den Wert zurück:
//+------------------------------------------------------------------+ //| Copy data of the specified indicator to the buffer object array | //+------------------------------------------------------------------+ int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count) { return(#ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,-start_pos,count,this.DataBuffer[0].Array) #else count #endif ); } //+------------------------------------------------------------------+ int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count) { return(#ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,start_time,count,this.DataBuffer[0].Array) #else count #endif ); } //+------------------------------------------------------------------+ int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time) { return ( #ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,start_time,stop_time,this.DataBuffer[0].Array) #else int(::fabs(start_time-stop_time)/::PeriodSeconds(this.Timeframe())+1) #endif ); } //+------------------------------------------------------------------+
Für die allerletzte Methode, bei der wir nicht die Menge der kopierten Daten angeben, sondern die Startzeit und Endzeit der benötigten Daten,
für MQL4 die Anzahl der Balken zwischen den Werten von Startzeitpunkt und Endezeitpunkt der gewünschten Daten berechnen und den berechneten Wert zurückgeben.
Erzeugen Sie alle Pufferobjekte für Standardindikatoren in der Klasse Indikatorpuffersammlung
im Ordner \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh.
Auch diese Klasse wurde für die MQL4-Kompatibilität verbessert.
In MQL5 wird in Methoden zur Erzeugung von Standard-Indikatorobjekten zunächst ein Handle des gewünschten Indikators erzeugt, und bei dessen erfolgreicher Erzeugung wird das Objekt selbst konstruiert. In MQL4 muss kein Handle erzeugt werden, deshalb ein Dummy-Handle des erzeugten Indikators hinzufügen zu all diesen Methoden:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period AC | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE) { //--- Create indicator handle and set default ID int handle= #ifdef __MQL5__ ::iAC(symbol,timeframe) #else 0 #endif ; int identifier=(id==WRONG_VALUE ? IND_AC : id); color array_colors[3]={clrGreen,clrRed,clrGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create histogram buffer from the zero line this.CreateHistogram();
In der Zwischenzeit haben wir Null als Handle-Wert hinzugefügt. Weiterhin werden wir wahrscheinlich das Erzeugen von Indikator-Handles emulieren, um die Erzeugung von zwei gleichen Standard-Indikatorobjekten mit den gleichen Eingängen auszuschließen. Die Praxis wird jedoch zeigen, ob dies tatsächlich durchgeführt werden sollte.
Eine Zeichenkette mit Emulation der Handle-Erzeugung ist bereits zu allen Methoden der Erzeugung von Standard-Indikatorobjekten hinzugefügt.
Wir werden uns hier nicht mit ihnen beschäftigen. Besprechen wir stattdessen die Änderungen, die benötigt werden, um einen monochromen Einpuffer-Standardindikator für MQL4 zu erstellen, indem Sie die AD-Indikator-Erstellungsmethode als Beispiel verwenden:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period AD | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id=WRONG_VALUE) { //--- Create indicator handle and set default ID int handle= #ifdef __MQL5__ ::iAD(symbol,timeframe,applied_volume) #else 0 #endif ; int identifier=(id==WRONG_VALUE ? IND_AD : id); color array_colors[1]={clrLightSeaGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create line buffer this.CreateLine(); //--- Get the last created buffer object (drawn) and set to it all necessary parameters buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AD); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accumulation/Distribution"); buff.SetIndicatorShortName("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); #ifdef __MQL5__ buff.SetColors(array_colors); #else buff.SetColor(array_colors[0]); #endif //--- MQL5 #ifdef __MQL5__ //--- Create calculated buffer, in which standard indicator data will be stored this.CreateCalculate(); //--- Get the last created buffer object (calculated) and set to it all necessary parameters buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AD); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accumulation/Distribution"); buff.SetIndicatorShortName("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); #endif } return handle; } //+------------------------------------------------------------------+
Hier für MQL5 setzen wir für den Puffer einen Satz seiner Farben durch die Methode ihrer Übergabe an das Objekt mit Hilfe des Farbarrays, und für MQL4 setzen wir nur eine Farbe - die erste im Farbarray. Den Berechnungspuffer brauchen wir nur für MQL5: er wird die Daten des erstellten Indikators AD auf dem angegebenen Symbol und der Chartperiode speichern. Für MQL4 wird ein solcher Puffer nicht benötigt, da wir alle Daten direkt von der Funktion des Aufrufs des Indikators iAD() erhalten.
Die Methode, die Daten des angegebenen Standardindikators für das Setzen von Werten auf dem aktuellen Symbolchart vorbereitet, für MQL5 liest sie Daten aus Berechnungspuffer. Für MQL4 ist es ganz einfach, die angeforderten Daten aus der Funktion des Aufrufs der Standardindikator-Daten zu holen:
//+------------------------------------------------------------------+ //| Prepare data of the specified standard indicator | //| for setting values on the current symbol chart | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,CBuffer *buffer_data3,CBuffer *buffer_data4, CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,CBuffer *buffer_calc3,CBuffer *buffer_calc4, const ENUM_INDICATOR ind_type, const int series_index, const datetime series_time, int &index_period, int &num_bars, double &value00, double &value01, double &value10, double &value11, double &value20, double &value21, double &value30, double &value31, double &value40, double &value41) { //--- Find bar index on a period which corresponds to the time of current bar start index_period=::iBarShift(buffer_data0.Symbol(),buffer_data0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || #ifdef __MQL5__ index_period>buffer_calc0.GetDataTotal()-1 #else index_period>buffer_data0.GetDataTotal()-1 #endif ) return WRONG_VALUE; //--- For MQL5 #ifdef __MQL5__ //--- Get the value by this index from indicator buffer if(buffer_calc0!=NULL) value00=buffer_calc0.GetDataBufferValue(0,index_period); if(buffer_calc1!=NULL) value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc2!=NULL) value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc3!=NULL) value30=buffer_calc3.GetDataBufferValue(0,index_period); if(buffer_calc4!=NULL) value40=buffer_calc4.GetDataBufferValue(0,index_period); //--- for MQL4 #else switch((int)ind_type) { //--- Single-buffer standard indicators case IND_AC : value00=::iAC(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_AD : value00=::iAD(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_AMA : break; case IND_AO : value00=::iAO(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_ATR : break; case IND_BEARS : break; case IND_BULLS : break; case IND_BWMFI : value00=::iBWMFI(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_CCI : break; case IND_CHAIKIN : break; case IND_DEMA : break; case IND_DEMARKER : break; case IND_FORCE : break; case IND_FRAMA : break; case IND_MA : break; case IND_MFI : break; case IND_MOMENTUM : break; case IND_OBV : break; case IND_OSMA : break; case IND_RSI : break; case IND_SAR : break; case IND_STDDEV : break; case IND_TEMA : break; case IND_TRIX : break; case IND_VIDYA : break; case IND_VOLUMES : value00=(double)::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_WPR : break; //--- Multi-buffer standard indicators case IND_ENVELOPES : break; case IND_FRACTALS : break; case IND_ADX : break; case IND_ADXW : break; case IND_BANDS : break; case IND_MACD : break; case IND_RVI : break; case IND_STOCHASTIC : break; case IND_ALLIGATOR : break; case IND_ICHIMOKU : break; case IND_GATOR : break; default: break; } #endif int series_index_start=series_index; //--- For the current chart we don’t need to calculate the number of bars processed - only one bar is available if(buffer_data0.Symbol()==::Symbol() && buffer_data0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer datetime time_period=::iTime(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the current chart bar which corresponds to the time series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return WRONG_VALUE; //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data num_bars=::PeriodSeconds(buffer_data0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values for color calculation if(buffer_data0!=NULL) value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data1!=NULL) value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data2!=NULL) value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data3!=NULL) value31=(series_index_start+num_bars>buffer_data3.GetDataTotal()-1 ? value30 : buffer_data3.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data4!=NULL) value41=(series_index_start+num_bars>buffer_data4.GetDataTotal()-1 ? value40 : buffer_data4.GetDataBufferValue(0,series_index_start+num_bars)); return series_index_start; } //+------------------------------------------------------------------+
Im Moment ist für MQL4 nur das Abrufen von Daten von Single-Buffer-Standard-Indikatoren implementiert, die keine Eingaben außer Symbol und Periode des Charts haben. Die restlichen Standardindikatoren werden in den folgenden Artikeln implementiert.
Geringfügige Änderungen zum Ausschluss der Prüfung für MQL4 werden in der Methode des Setzens eines Wertes für den aktuellen Chart auf Puffer des angegebenen Standardindikators durch den Zeitreihenindex in Übereinstimmung mit dem Pufferobjekt Symbol/Periode vorgenommen:
//+------------------------------------------------------------------+ //| Set values for the current chart to buffers of the specified | //| standard indicator by the timeseries index in accordance | //| with buffer object symbol/period | //+------------------------------------------------------------------+ bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE) { //--- Get the list of buffer objects by type and ID CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return false; } //--- Get the list of drawn buffers with ID CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Get the list of calculated buffers with ID CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Leave if any of the lists is empty if(list_data.Total()==0 #ifdef __MQL5__ || list_calc.Total()==0 #endif ) return false; //--- Declare necessary objects and variables CBuffer *buffer_data0=NULL,*buffer_data1=NULL,*buffer_data2=NULL,*buffer_data3=NULL,*buffer_data4=NULL,*buffer_tmp0=NULL,*buffer_tmp1=NULL; CBuffer *buffer_calc0=NULL,*buffer_calc1=NULL,*buffer_calc2=NULL,*buffer_calc3=NULL,*buffer_calc4=NULL; double value00=EMPTY_VALUE, value01=EMPTY_VALUE; double value10=EMPTY_VALUE, value11=EMPTY_VALUE; double value20=EMPTY_VALUE, value21=EMPTY_VALUE; double value30=EMPTY_VALUE, value31=EMPTY_VALUE; double value40=EMPTY_VALUE, value41=EMPTY_VALUE; double value_tmp0=EMPTY_VALUE,value_tmp1=EMPTY_VALUE; long vol0=0,vol1=0; int series_index_start=series_index,index_period=0, index=0,num_bars=1; uchar clr=0; //--- Depending on standard indicator type switch((int)ind_type) { //--- Single-buffer standard indicators case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); #ifdef __MQL5__ list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); #endif if(buffer_data0==NULL #ifdef __MQL5__ || buffer_calc0==NULL || buffer_calc0.GetDataTotal(0)==0 #endif ) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); if(ind_type!=IND_BWMFI) clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); else { vol0=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); vol1=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period+1); clr= ( value00>value01 && vol0>vol1 ? 0 : value00<value01 && vol0<vol1 ? 1 : value00>value01 && vol0<vol1 ? 2 : value00<value01 && vol0>vol1 ? 3 : 4 ); } buffer_data0.SetBufferColorIndex(index,clr); } return true; //--- Multi-buffer standard indicators case IND_ENVELOPES : case IND_FRACTALS : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_ADX : case IND_ADXW : case IND_BANDS : case IND_MACD : case IND_RVI : case IND_STOCHASTIC : case IND_ALLIGATOR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,2,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,2,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(0,index,value10); buffer_data2.SetBufferValue(0,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_ICHIMOKU : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL); buffer_data3=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL); buffer_data4=list.At(0); //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 0 list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,0,EQUAL); buffer_tmp0=list.At(0); //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 1 list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,1,EQUAL); buffer_tmp1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL); buffer_calc2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL); buffer_calc3=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL); buffer_calc4=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; if(buffer_calc3==NULL || buffer_data3==NULL || buffer_calc3.GetDataTotal(0)==0) return false; if(buffer_calc4==NULL || buffer_data4==NULL || buffer_calc4.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(0,index,value10); buffer_data2.SetBufferValue(0,index,value20); buffer_data3.SetBufferValue(0,index,value30); buffer_data4.SetBufferValue(0,index,value40); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); buffer_data3.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value30>value31 ? 0 : value30<value31 ? 1 : 2) : color_index); buffer_data4.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value40>value41 ? 0 : value40<value41 ? 1 : 2) : color_index); //--- Set values for indicator auxiliary lines depending on mutual position of Senkou Span A and Senkou Span B lines value_tmp0=buffer_data2.GetDataBufferValue(0,index); value_tmp1=buffer_data3.GetDataBufferValue(0,index); if(value_tmp0<value_tmp1) { buffer_tmp0.SetBufferValue(0,index,buffer_tmp0.EmptyValue()); buffer_tmp0.SetBufferValue(1,index,buffer_tmp0.EmptyValue()); buffer_tmp1.SetBufferValue(0,index,value_tmp0); buffer_tmp1.SetBufferValue(1,index,value_tmp1); } else { buffer_tmp0.SetBufferValue(0,index,value_tmp0); buffer_tmp0.SetBufferValue(1,index,value_tmp1); buffer_tmp1.SetBufferValue(0,index,buffer_tmp1.EmptyValue()); buffer_tmp1.SetBufferValue(1,index,buffer_tmp1.EmptyValue()); } } return true; case IND_GATOR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10<value11 ? 0 : value10>value11 ? 1 : 2) : color_index); } return true; default: break; } return false; } //+------------------------------------------------------------------+
Da wir uns entschieden haben, die Korrespondenzprüfung für die Anzahl der von der Bibliothek erzeugten Pufferobjekte mit Werten in #property des Programms zu verschieben, fügen Sie eine solche Methode in die Klasse des Hauptobjekts CEngine der Bibliothek in der Datei \MQL5\Include\DoEasy\Engine.mqh ein.
Deklarieren wir die Methode im öffentlichen Abschnitt der Klasse:
//--- Display short description of all indicator buffers of the buffer collection void BuffersPrintShort(void); //--- Specify the required number of buffers for indicators void CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif ); //--- Return the bar index on the specified timeframe chart by the current chart's bar index
Schreiben wir seine Implementierung außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Specify the required number of buffers for indicators | //+------------------------------------------------------------------+ void CEngine::CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif ) { #ifdef __MQL5__ if(this.BuffersPropertyPlotsTotal()!=plots) ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_PLOTS),this.BuffersPropertyPlotsTotal()); if(this.BuffersPropertyBuffersTotal()!=buffers) ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_ORDERS),this.BuffersPropertyBuffersTotal()); #else if(buffers!=this.BuffersPropertyPlotsTotal()) ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_ORDERS),this.BuffersPropertyPlotsTotal()); ::IndicatorBuffers(this.BuffersPropertyBuffersTotal()); #endif } //+------------------------------------------------------------------+
Anzeige der einfachen Warnhinweise über die Nichtübereinstimmung der erzeugten Anzahl von Indikatorpuffern (zu gezeichneten und berechneten) mit dem angegebenen Wert in #property des Indikatorprogramms für MQL5.
Für MQL4 wird bei Nichtübereinstimmung des in #property angegebenen Wertes des Indikatorpuffers ein Hinweis angezeigt und die Gesamtzahl aller Indikatorpuffer (zu zeichnenden und zu berechnenden) entsprechend der Gesamtzahl aller von der Bibliothek erzeugten Puffer eingestellt.
Richten wir nun die Kapazität der angezeigten Daten für Indikatoren in MQL4 ein. Verbessern wir dazu die Funktion zum Einstellen der Kapazität und der Ebenen für Standardindikatoren in der Datei der Bibliotheks-Servicefunktionen \MQL5\Include\DoEasy\Services\DELib.mqh:
//+------------------------------------------------------------------+ //| Set capacity and levels for standard indicator | //+------------------------------------------------------------------+ void SetIndicatorLevels(const string symbol,const ENUM_INDICATOR ind_type) { int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); switch(ind_type) { case IND_AD : case IND_CHAIKIN : case IND_OBV : case IND_VOLUMES : digits=0; break; case IND_AO : case IND_BEARS : case IND_BULLS : case IND_FORCE : case IND_STDDEV : case IND_AMA : case IND_DEMA : case IND_FRAMA : case IND_MA : case IND_TEMA : case IND_VIDYA : case IND_BANDS : case IND_ENVELOPES : case IND_MACD : digits+=1; break; case IND_AC : case IND_OSMA : digits+=2; break; case IND_MOMENTUM : digits=2; break; case IND_CCI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100); digits=2; break; case IND_DEMARKER : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3); digits=3; break; case IND_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); break; case IND_RSI : IndicatorSetInteger(INDICATOR_LEVELS,3); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50); IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30); digits=2; break; case IND_STOCHASTIC : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); digits=2; break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_ATR : break; case IND_SAR : break; case IND_TRIX : break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } #ifdef __MQL5__ IndicatorSetInteger(INDICATOR_DIGITS,digits); #else IndicatorDigits(digits); #endif } //+------------------------------------------------------------------+
Hier, für MQL4 zum Einstellen der Kapazität der angezeigten Indikatordaten verwenden wir die Standard-Mql4-Funktion IndicatorDigits().
Damit ist die Verbesserung der Bibliotheksklassen zur Erstellung von Einpuffer-Multisymbol-Multiperioden-Standardindikatoren abgeschlossen.
Tests
Um den Test durchzuführen, nehmen wir den zweiten Indikator (TestDoEasyPart51_2.mq5) aus dem vorherigen Artikel und
speichern ihn im Ordner der Terminalindikatoren MetaTrader 4 \MQL4\Indikatoren\TestDoEasy\Part52\ mit dem Namen TestDoEasyPart52.mq4.
Mit dem vorherigen Testindikator wurde der Standardindikator Gator Oscillator für mehrere Symbole und Zeitrahmen erstellt. Wir hingegen wollen den Accumulation/Distribution-Indikator erstellen.
Im Dateikopf stellen wir die Anzahl der Indikatorpuffer ein, die für MQL4 benötigt werden:
//+------------------------------------------------------------------+ //| TestDoEasyPart52.mq4 | //| 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" //--- includes #include <DoEasy\Engine.mqh> //--- properties #property indicator_separate_window #property indicator_buffers 1 #property indicator_plots 1 //--- classes //--- enums //--- defines //--- structures //--- input variables sinput string InpUsedSymbols = "EURUSD"; // Used symbol (one only) sinput ENUM_TIMEFRAMES InpPeriod = PERIOD_H4; // Used chart period //--- sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers //--- global variables ENUM_SYMBOLS_MODE InpModeUsedSymbols= SYMBOLS_MODE_DEFINES; // Mode of used symbols list ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_LIST; // Mode of used timeframes list string InpUsedTFs; // List of used timeframes CEngine engine; // CEngine library main object string prefix; // Prefix of graphical object names int min_bars; // The minimum number of bars for the indicator calculation int used_symbols_mode; // Mode of working with symbols string array_used_symbols[]; // The array for passing used symbols to the library string array_used_periods[]; // The array for passing used timeframes to the library //+------------------------------------------------------------------+
In OnInit() legen wir das Standardindikatorobjekt Akkumulation/Distribution an, und geben an, wo AD-Indikatorentyp benötigt wird:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Write the name of the working timeframe selected in the settings to InpUsedTFs variable InpUsedTFs=TimeframeDescription(InpPeriod); //--- Initialize DoEasy library OnInitDoEasy(); //--- Set indicator global variables prefix=engine.Name()+"_"; //--- Calculate the number of bars of the current period fitting in the maximum used period //--- Use the obtained value if it exceeds 2, otherwise use 2 int num_bars=NumberBarsInTimeframe(InpPeriod); min_bars=(num_bars>2 ? num_bars : 2); //--- Check and remove remaining indicator graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel //--- Check playing a standard sound using macro substitutions engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Create all the necessary buffer objects for constructing the selected standard indicator if(!engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1)) { Print(TextByLanguage("Error. Indicator not created")); return INIT_FAILED; } //--- Check the number of buffers specified in the 'properties' block engine.CheckIndicatorsBuffers(indicator_buffers,indicator_plots); //--- Create the color array and set non-default colors to all buffers within the collection //--- (commented out since default colors are already set in methods of standard indicator creation) //--- (we can always set required colors either for all indicators like here or for each one individually) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Display short descriptions of created indicator buffers engine.BuffersPrintShort(); //--- Set a short name for the indicator, data capacity and levels string label=engine.BufferGetIndicatorShortNameByTypeID(IND_AD,1); IndicatorSetString(INDICATOR_SHORTNAME,label); SetIndicatorLevels(InpUsedSymbols,IND_AD); //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Früher wurde in OnInit() die Übereinstimmung zwischen der Anzahl der angegebenen und der erzeugten Indikatorpuffer überprüft:
//--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
Jetzt wird sie durch den Aufruf der entsprechenden Bibliotheksmethode ersetzt.
In OnCalculate() genügt es, nur das Schreiben der Gator-Oszillator-Daten durch das Schreiben der Akkumulations-/Distributions-Daten in der Schleife des Hauptprogramms zu ersetzen:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //+------------------------------------------------------------------+ //| OnCalculate code block for working with the library: | //+------------------------------------------------------------------+ //--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread); //--- Check for the minimum number of bars for calculation if(rates_total<min_bars || Point()==0) return 0; //--- Handle the Calculate event in the library //--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick if(engine.OnCalculate(rates_data)==0) return 0; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the library timer engine.EventsHandling(); // Working with library events } //+------------------------------------------------------------------+ //| OnCalculate code block for working with the indicator: | //+------------------------------------------------------------------+ //--- Check and calculate the number of calculated bars //--- If limit = 0, there are no new bars - calculate the current one //--- If limit = 1, a new bar has appeared - calculate the first and the current ones //--- If limit > 1 means the first launch or changes in history - the full recalculation of all data int limit=rates_total-prev_calculated; //--- Recalculate the entire history if(limit>1) { limit=rates_total-1; engine.BuffersInitPlots(); engine.BuffersInitCalculates(); } //--- Prepare data //--- Fill in calculated buffers of all created standard indicators with data int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod); int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total)); if(!engine.BufferPreparingDataAllBuffersStdInd()) return 0; //--- Calculate the indicator //--- Main calculation loop of the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { engine.GetBuffersCollection().SetDataBufferStdInd(IND_AD,1,i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Für die mql5-Version des Indikators im Gegensatz zur mql4-Version müssen wir die Anzahl der zu zeichnenden und berechneten Puffer ändern, die in #property angegeben ist:
//+------------------------------------------------------------------+ //| TestDoEasyPart52.mq5 | //| 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" //--- includes #include <DoEasy\Engine.mqh> //--- properties #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 1
Kompilieren Sie den Indikator und starten Sie ihn auf dem EURUSD H1-Chart im MetaTrader 4-Terminal mit Werten in den Indikatoreingaben für das EURUSD-Symbol und für die H4-Periode. So zeigen Sie den für EURUSD H4 berechneten AD-Indikator auf dem Stundenchart EURUSD im MetaTrader 4-Terminal an:
Was kommt als Nächstes?
Im nächsten Artikel setzen Sie die Arbeit mit Indikatoren in MetaTrader 5 fort und arbeiten in aller Ruhe an der plattformübergreifenden Eigenschaft der Bibliothek.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Testindikator-Dateien angehängt. Sie können sie herunterladen und alles testen.
Hinterlassen Sie Ihre Kommentare, Fragen und Anregungen in den Kommentaren zum Artikel.
Bitte beachten Sie, dass alle Arbeiten an der Kompatibilität mit der vorherigen Plattform nur durchgeführt werden, um die Multiplattform-Eigenschaft der Bibliothek zu unterstützen, die ursprünglich für MQL5 erstellt wurde und wo sie mehr Vorteile und Funktionalität hat.
Es waren keine separaten Artikel über die Arbeit in MQL4 mit dieser Bibliothek geplant. Und es wird auch keiner geschrieben werden. Dagegen kann jeder Leser individuell und "maßgeschneidert" alles entwickeln, was bei der Verwendung von MetaTrader 4 mit dieser Bibliothek als unzureichend erscheint. Ich werde die Bibliothek weiterhin mit beiden Plattformen kompatibel machen, aber nur aus dem Grund, dass der Nutzer der Bibliothek in der Lage ist, alle seine auf der Bibliothek basierenden Programme leicht zu verschieben, um in MetaTrader 4 zu arbeiten.
Frühere Artikel dieser Serie:
Zeitreihen in der Bibliothek DoEasy (Teil 35): das Balkenobjekt und die Liste der Zeitreihen eines Symbols
Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolzeitrahmen
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen
Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm
Zeitreihen in der Bibliothek DoEasy (Teil 40): Bibliotheksbasierte Indikatoren - Aktualisierung der Daten in Echtzeit
Zeitreihen in der Bibliothek DoEasy (Teil 41): Beispiel eines Multi-Symbol- und Multi-Zeitrahmen-Indikators
Zeitreihen in der Bibliothek DoEasy (Teil 42): Abstrakte Objektklasse der Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 43): Klassen der Objekte von Indikatorpuffern
Zeitreihen in der Bibliothek DoEasy (Teil 44): Kollektionsklasse der Objekte von Indikatorpuffern
Zeitreihen in der Bibliothek DoEasy (Teil 45): Puffer für Mehrperiodenindikator
Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 47): Standardindikatoren für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 48): Mehrperioden-Multisymbol-Indikatoren mit einem Puffer in einem Unterfenster
Zeitreihen in der Bibliothek DoEasy (Teil 49): Standardindikatoren mit mehreren Puffern für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 50): Verschieben der Standardindikatoren für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 51): Zusammengesetzte Standardindikatoren für mehrere Symbole und Perioden
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/8399
- 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.