Zeitreihen in der Bibliothek DoEasy (Teil 49): Standardindikatoren mit mehreren Puffern für mehrere Symbole und Perioden
Inhalt
Konzept
In diesem Artikel werde ich das Thema der Anzeige von Daten in Indikatoren von einem beliebigen Symbol/einer beliebigen Periode bis zum aktuellen Symbol-Chart fortsetzen, indem ich die Funktionalität zur Anzeige von Standardindikatoren unter Verwendung mehrerer Puffer zum Zeichnen ihrer Daten schaffe. Wir haben bereits gelernt, Daten von jedem beliebigen Chart-Symbol/jeder beliebigen Chart-Periode zu erhalten und sie auf dem aktuellen Symbol-Chart anzuzeigen. Nicht alles läuft reibungslos, aber ich entdecke und behebe nach und nach alle Unzulänglichkeiten. Schließlich lernen wir, uns von Grund auf neu zu entwickeln, was die Möglichkeit nicht ausschließt, etwas nicht zu berücksichtigen. Das Wichtigste dabei ist, bestehende Mängel rechtzeitig zu erkennen und sie nach und nach zu beheben, wobei viel Beobachtungszeit zur Verfügung steht. Im vorliegenden Artikel werde ich die Fähigkeit zur Erstellung von Multi-Puffer-Standardindikatoren implementieren.
Der Hauptunterschied zu Single-Puffer-Indikatoren im Konzept des Aufbaus einer Bibliothek besteht darin, dass wir die zu zeichnenden Puffer und die Berechnungspuffer irgendwie markieren müssen, damit die Bibliothek sie alle auf einen gemeinsamen Indikator beziehen kann.
Im Moment haben wir bereits einen Standardindikatortyp, der in die Eigenschaften von Pufferobjekten und die Pufferkennung geschrieben wurde:
- Der Parameter "Indicator type" zeigt einen Standardindikator, zu dem die zu zeichnenden und Berechnungspuffer eines erstellten Indikators gehören;
- Der Parameter "Indicator identifier" wird verwendet, um anzugeben, zu welchem der beiden erstellten Indikatoren desselben Typs die zu zeichnenden und berechneten Pufferobjekte gehören (z.B. verschiedene МА Indikatoren — einer davon hat die ID 1, der zweite — 2, der dritte — 3, usw. alle mit dem gleichen Indikatortyp IND_MA)
Diese Parameter sind gut geeignet, um die Zugehörigkeit von Pufferobjekten zu Single-Puffer-Standardindikatoren zu identifizieren. Bei Indikatoren mit mhreren Puffern müssen wir auch definieren, zu welcher Standardindikatorlinie die Berechnungspuffer und die zu zeichnenden gehören, da es mehrere Linien gibt. Hier kommen wir zu dem Schluss, dass es notwendig ist, mindestens einen weiteren Parameter einzustellen - den Linientyp (oben, unten, Mitte usw.).
In diesem Fall werden wir in der Lage sein, die Zugehörigkeit von Pufferobjekten zu jedem der erstellten Indikatoren klar zu unterscheiden und leicht auf die notwendige Zeile eines jeden der erstellten Indikatoren über den Zeilentyp zuzugreifen:
- Der Parameter "Indicator type" zeigt einen Standardindikator, zu dem die zu zeichnenden und Berechnungspuffer eines erstellten Indikators gehören;
- Der Parameter "Indicator identifier" wird verwendet, um anzugeben, zu welchem der beiden erstellten Indikatoren desselben Typs die zu zeichnenden und berechneten Pufferobjekte gehören (z.B. verschiedene МА Indikatoren — einer davon hat die ID 1, der zweite — 2, der dritte — 3, usw. alle haben den gleichen IndikatortypIND_MA);
- Der Parameter "Indicator line" gibt eine bestimmte Indikatorlinie an, die durch die Einstellung von zwei vorhergehenden Parametern erhalten wird
Lassen Sie uns noch einen weiteren Parameter hinzufügen: "Short indicator name", der den Namen eines Standardindikators speichern soll, der mit dem von Standardindikatoren in einem Unterfenster übereinstimmt, plus dem Symbol und dem Zeitrahmen. Zum Beispiel sieht dieser Name für Stochastik wie folgt aus:
Abb. 1. Standardindikator Stochastic Oszillator
Abb. 2. Multi-Symbol Multi-Period Standardindikator Stochastic Oszillator
Verbesserung der Bibliothek der Klasse
Ändern wir den Dateiname \MQL5\Include\DoEasy\Datas.mqh in \MQL5\Include\DoEasy\Data.mqh (was aus sprachlicher Sicht korrekter ist) und fügen die Konstanten der neuen Bibliotheksnachrichten-Indizes hinzu:
//--- CBuffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE, // Base data buffer index MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT, // Plotted buffer serial number MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR, // Color buffer index MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS, // Number of data buffers MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE, // Index of the array to be assigned as the next indicator buffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT, // Index of the next drawn buffer MSG_LIB_TEXT_BUFFER_TEXT_ID, // Indicator buffer ID MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE, // Indicator line MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE, // Handle of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE, // Type of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, // Buffer (timeframe) data period MSG_LIB_TEXT_BUFFER_TEXT_STATUS, // Buffer status MSG_LIB_TEXT_BUFFER_TEXT_TYPE, // Buffer type MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE, // Active MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE, // Arrow code MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT, // The vertical shift of the arrows MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE, // Graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA, // Display construction values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_SHIFT, // Indicator graphical construction shift by time axis in bars MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE, // Line style MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, // Line width MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE, // Arrow size MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, // Number of colors MSG_LIB_TEXT_BUFFER_TEXT_COLOR, // Drawing color MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, // Buffer symbol MSG_LIB_TEXT_BUFFER_TEXT_LABEL, // Name of the graphical indicator series displayed in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME, // Name of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME_SHORT, // Short name of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME, // Indicator buffer with graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF, // Invalid number of indicator buffers (#property indicator_buffers) MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED, // Reached maximum possible number of indicator buffers MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ, // No buffer object for standard indicator
Fügen wir noch die Textnachrichten der neu hinzugefügten Indizes hinzu:
{"Индекс базового буфера данных","Index of Base data buffer"}, {"Порядковый номер рисуемого буфера","Plot buffer sequence number"}, {"Индекс буфера цвета","Color buffer index"}, {"Количество буферов данных","Number of data buffers"}, {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"}, {"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"}, {"Идентификатор буферов индикатора","Indicator Buffer Id"}, {"Линия индикатора","Indicator line"}, {"Хэндл индикатора, использующего буфер","Indicator handle that uses the buffer"}, {"Тип индикатора, использующего буфер","Indicator type that uses the buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"}, {"Статус буфера","Buffer status"}, {"Тип буфера","Buffer type"}, {"Активен","Active"}, {"Код стрелки","Arrow code"}, {"Смещение стрелок по вертикали","Vertical shift of arrows"}, {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in DataWindow"}, {"Тип графического построения","Type of graphical construction"}, {"Отображение значений построения в окне DataWindow","Display construction values in DataWindow"}, {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along time axis in bars"}, {"Стиль линии отрисовки","Drawing line style "}, {"Толщина линии отрисовки","Thickness of drawing line"}, {"Размер значка стрелки","Arrow icon size"}, {"Количество цветов","Number of colors"}, {"Цвет отрисовки","Index of buffer containing drawing color"}, {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","Name of indicator graphical series to display in DataWindow"}, {"Наименование индикатора, использующего буфер","Name of indicator that uses buffer"}, {"Короткое наименование индикатора, использующего буфер","Short name of indicator that uses buffer"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"}, {"Нет ни одного объекта-буфера для стандартного индикатора","No buffer object for standard indicator"},
Entfernen Sie die alte Datei Datas.mqh aus dem Bibliotheksordner \MQL5\Include\DoEasy\, da ihre Funktion nun von Data.mqh erfüllt wird.
Fügen Sie alle notwendigen neuen Daten für die Arbeit mit Multi-Puffer-Standardindikatoren zu \MQL5\Include\DoEasy\Defines.mqh hinzu.
Zuerst ändern Sie die Zeile mit dem Namen der umbenannten Datei:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "DataSND.mqh" #include "DataIMG.mqh" #include "Data.mqh" #ifdef __MQL4__ #include "ToMQL4.mqh" #endif //+------------------------------------------------------------------+
Fügen Sie die neue Enumeration der Linientypen der Indikatoren dem Block hinzu, der Daten für die Arbeit mit Indikatorpuffern enthält:
//+------------------------------------------------------------------+ //| Indicator lines | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_LINE_MODE { INDICATOR_LINE_MODE_MAIN, // Main line INDICATOR_LINE_MODE_SIGNAL, // Signal line INDICATOR_LINE_MODE_UPPER, // Upper line INDICATOR_LINE_MODE_MIDDLE, // Middle line INDICATOR_LINE_MODE_LOWER, // Lower line INDICATOR_LINE_MODE_JAWS, // Jaws line INDICATOR_LINE_MODE_TEETH, // Teeth line INDICATOR_LINE_MODE_LIPS, // Lips line INDICATOR_LINE_MODE_DI_PLUS, // +DI line INDICATOR_LINE_MODE_DI_MINUS, // -DI line }; //+------------------------------------------------------------------+
Verschiedene Standardindikatoren haben Linien, die je nachdem, wie der Entwickler des Indikators sie benannt hat, ihre eigenen Namen haben können. Hier habe ich die Enumeration erstellt, die Konstanten enthält, die den Namen einer Indikatorlinie angeben. Die Konstantenwerte sollen dazu dienen, die Zugehörigkeit der erzeugten (berechneten und gezeichneten) Pufferobjekte zu einer bestimmten Standardindikatorlinie zu markieren. Auf diese Weise können wir beim Zugriff auf das Pufferobjekt über den Konstantennamen eindeutig das gewünschte zu behandelnde Objekt erhalten (unter Berücksichtigung des Indikatortyps und seiner ID).
Später werde ich diesen Konstanten genaue Werte zuweisen, um die Menge des Codes, den ich hier generieren werde, zu reduzieren (da viele Indikatoren die Berechnung der Indikatoren desselben Typs mit anderen Zeilennamen vollständig wiederholen).
Hinzufügen des neuen Wertes zur Aufzählung der ganzzahligen Pufferobjekteigenschaften und die Erweiterung der Anzahl der ganzzahligen Eigenschaften von 23 auf 24:
//+------------------------------------------------------------------+ //| Buffer integer properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = 0, // Plotted buffer serial number BUFFER_PROP_STATUS, // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration BUFFER_PROP_TYPE, // Buffer type (from the ENUM_BUFFER_TYPE enumeration) BUFFER_PROP_TIMEFRAME, // Buffer period data (timeframe) BUFFER_PROP_ACTIVE, // Buffer usage flag BUFFER_PROP_DRAW_TYPE, // Graphical construction type (from the ENUM_DRAW_TYPE enumeration) BUFFER_PROP_ARROW_CODE, // Arrow code for DRAW_ARROW style BUFFER_PROP_ARROW_SHIFT, // The vertical shift of the arrows for DRAW_ARROW style BUFFER_PROP_LINE_STYLE, // Line style BUFFER_PROP_LINE_WIDTH, // Line width BUFFER_PROP_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow BUFFER_PROP_SHOW_DATA, // Flag of displaying construction values in DataWindow BUFFER_PROP_SHIFT, // Indicator graphical construction shift by time axis in bars BUFFER_PROP_COLOR_INDEXES, // Number of colors BUFFER_PROP_COLOR, // Drawing color BUFFER_PROP_INDEX_BASE, // Base data buffer index BUFFER_PROP_INDEX_NEXT_BASE, // Index of the array to be assigned as the next indicator buffer BUFFER_PROP_INDEX_NEXT_PLOT, // Index of the next drawn buffer BUFFER_PROP_IND_LINE_MODE, // Indicator line BUFFER_PROP_ID, // ID of multiple buffers of a single indicator BUFFER_PROP_IND_HANDLE, // Handle of an indicator using a buffer BUFFER_PROP_IND_TYPE, // Type of an indicator using a buffer BUFFER_PROP_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_COLOR, // Color buffer index }; #define BUFFER_PROP_INTEGER_TOTAL (24) // Total number of integer bar properties #define BUFFER_PROP_INTEGER_SKIP (2) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+
Auf die gleiche Weise fügen Sie die neue String-Eigenschaft hinzu und erhöhen ihre Anzahl auf 4:
//+------------------------------------------------------------------+ //| Buffer string properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Buffer symbol BUFFER_PROP_LABEL, // Name of the graphical indicator series displayed in DataWindow BUFFER_PROP_IND_NAME, // Name of an indicator using a buffer BUFFER_PROP_IND_NAME_SHORT, // Short name of an indicator using a buffer }; #define BUFFER_PROP_STRING_TOTAL (4) // Total number of string buffer properties //+------------------------------------------------------------------+
Da ich dem Pufferobjekt zwei neue Eigenschaften hinzugefügt habe, muss ich auch zwei neue Kriterien hinzufügen, um Pufferobjekte in ihrer Kollektionsliste nach diesen Eigenschaften zu suchen und zu sortieren:
//+------------------------------------------------------------------+ //| Possible buffer sorting criteria | //+------------------------------------------------------------------+ #define FIRST_BUFFER_DBL_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) #define FIRST_BUFFER_STR_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { //--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = 0, // Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, // Sort by the buffer data period (timeframe) SORT_BY_BUFFER_ACTIVE, // Sort by the buffer usage flag SORT_BY_BUFFER_DRAW_TYPE, // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_ARROW_CODE, // Sort by the arrow code for DRAW_ARROW style SORT_BY_BUFFER_ARROW_SHIFT, // Sort by the vertical shift of the arrows for DRAW_ARROW style SORT_BY_BUFFER_LINE_STYLE, // Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, // Sort by the line width SORT_BY_BUFFER_DRAW_BEGIN, // Sort by the number of initial bars that are not drawn and values in DataWindow SORT_BY_BUFFER_SHOW_DATA, // Sort by the flag of displaying construction values in DataWindow SORT_BY_BUFFER_SHIFT, // Sort by the indicator graphical construction shift by time axis in bars SORT_BY_BUFFER_COLOR_INDEXES, // Sort by a number of attempts SORT_BY_BUFFER_COLOR, // Sort by the drawing color SORT_BY_BUFFER_INDEX_BASE, // Sort by the basic data buffer index SORT_BY_BUFFER_INDEX_NEXT_BASE, // Sort by the index of the array to be assigned as the next indicator buffer SORT_BY_BUFFER_INDEX_NEXT_PLOT, // Sort by the index of the next drawn buffer SORT_BY_BUFFER_IND_LINE_MODE, // Sort by the indicator line SORT_BY_BUFFER_ID, // Sort by ID of multiple buffers of a single indicator SORT_BY_BUFFER_IND_HANDLE, // Sort by handle of an indicator using a buffer SORT_BY_BUFFER_IND_TYPE, // Sort by type of an indicator using a buffer //--- Sort by real properties SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, // Sort by the empty value for plotting where nothing will be drawn //--- Sort by string properties SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, // Sort by the buffer symbol SORT_BY_BUFFER_LABEL, // Sort by the name of the graphical indicator series displayed in DataWindow SORT_BY_BUFFER_IND_NAME, // Sort by name of an indicator using a buffer SORT_BY_BUFFER_IND_NAME_SHORT, // Sort by a short name of an indicator using a buffer }; //+------------------------------------------------------------------+
Schreiben Sie im 'public' Teil der Klasse die Methoden zum Installieren und zum Abfragen neuer Pufferobjekteigenschaften in \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:
public: //--- Send description of buffer properties to the journal (full_prop=true - all properties, false - only supported ones) void Print(const bool full_prop=false); //--- Display a short buffer description in the journal (implementation in the descendants) virtual void PrintShort(void) {;} //--- Set (1) the arrow code, (2) vertical shift of arrows, (3) symbol, (4) timeframe, (5) buffer activity flag //--- (6) drawing type, (7) number of initial bars without drawing, (8) flag of displaying construction values in DataWindow, //--- (9) shift of the indicator graphical construction along the time axis, (10) line style, (11) line width, //--- (12) total number of colors, (13) one drawing color, (14) color of drawing in the specified color index, //--- (15) drawing colors from the color array, (16) empty value, (17) name of the graphical series displayed in DataWindow virtual void SetArrowCode(const uchar code) { return; } virtual void SetArrowShift(const int shift) { return; } void SetSymbol(const string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } void SetActive(const bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } void SetDrawType(const ENUM_DRAW_TYPE draw_type); void SetDrawBegin(const int value); void SetShowData(const bool flag); void SetShift(const int shift); void SetStyle(const ENUM_LINE_STYLE style); void SetWidth(const int width); void SetColorNumbers(const int number); void SetColor(const color colour); void SetColor(const color colour,const uchar index); void SetColors(const color &array_colors[]); void SetEmptyValue(const double value); virtual void SetLabel(const string label); void SetID(const int id) { this.SetProperty(BUFFER_PROP_ID,id); } void SetIndicatorHandle(const int handle) { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle); } void SetIndicatorType(const ENUM_INDICATOR type) { this.SetProperty(BUFFER_PROP_IND_TYPE,type); } void SetIndicatorName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME,name); } void SetIndicatorShortName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME_SHORT,name); } void SetLineMode(const ENUM_INDICATOR_LINE_MODE mode){ this.SetProperty(BUFFER_PROP_IND_LINE_MODE,mode); } //--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index, //--- (4) index of the first free bound array, (5) index of the next drawn buffer, (6) buffer data period, (7) buffer status, //--- (8) buffer type, (9) buffer usage flag, (10) arrow code, (11) arrow shift for DRAW_ARROW style, //--- (12) number of initial bars that are not drawn and values in DataWindow, (13) graphical construction type, //--- (14) flag of displaying construction values in DataWindow, (15) indicator graphical construction shift along the time axis, //--- (16) drawing line style, (17) drawing line width, (18) number of colors, (19) drawing color, (20) number of buffers for construction //--- (21) set empty value, (22) buffer symbol and (23) name of the indicator graphical series displayed in DataWindow //--- (24) buffer ID, (25) indicator handle, (26) standard indicator type, (27) standard indicator name, //--- (28) umber of standard indicator's calculated bars, (29) line type (main, signal, etc) int IndexPlot(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } int IndexBase(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } int IndexColor(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } int IndexNextBaseBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE); } int IndexNextPlotBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(void) const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(void) const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } bool IsActive(void) const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE); } uchar ArrowCode(void) const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } int ArrowShift(void) const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } int DrawBegin(void) const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(void) const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); } bool IsShowData(void) const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA); } int Shift(void) const { return (int)this.GetProperty(BUFFER_PROP_SHIFT); } ENUM_LINE_STYLE LineStyle(void) const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE); } int LineWidth(void) const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH); } int ColorsTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES); } color Color(void) const { return (color)this.GetProperty(BUFFER_PROP_COLOR); } int BuffersTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_NUM_DATAS); } double EmptyValue(void) const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE); } string Symbol(void) const { return this.GetProperty(BUFFER_PROP_SYMBOL); } string Label(void) const { return this.GetProperty(BUFFER_PROP_LABEL); } int ID(void) const { return (int)this.GetProperty(BUFFER_PROP_ID); } int IndicatorHandle(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_HANDLE); } 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));} ENUM_INDICATOR_LINE_MODE LineMode(void) const { return (ENUM_INDICATOR_LINE_MODE)this.GetProperty(BUFFER_PROP_IND_LINE_MODE);}
Setzen Sie im Klassenkonstruktor Standardwerte der neuen Eigenschaften:
//+------------------------------------------------------------------+ //| 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) { 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; 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); } //--- If this is a calculated buffer, all is done 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)); } //+------------------------------------------------------------------+
Implementieren Sie die Rückgabe der Zweckbeschreibung der Indikatorlinie in der Methode, die die Beschreibung der Puffer-Integer-Eigenschaften zurückgibt:
//+------------------------------------------------------------------+ //| Return description of a buffer's integer property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property) { return ( property==BUFFER_PROP_INDEX_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==BUFFER_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeBufferDescription() ) : property==BUFFER_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==BUFFER_PROP_ACTIVE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetActiveDescription() ) : property==BUFFER_PROP_DRAW_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetDrawTypeDescription() ) : property==BUFFER_PROP_ARROW_CODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ARROW_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_LINE_STYLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetLineStyleDescription() ) : property==BUFFER_PROP_LINE_WIDTH ? (this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE) : CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH))+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_BEGIN ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_SHOW_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetShowDataDescription() ) : property==BUFFER_PROP_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR_INDEXES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_LINE_MODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR_LINE_MODE)this.GetProperty(property)),10) ) : property==BUFFER_PROP_IND_HANDLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR)this.GetProperty(property)),4) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetColorsDescription() ) : "" ); } //+------------------------------------------------------------------+
Implementieren Sie die Rückgabe des "Short names" des Indikators in der Methode, die die Beschreibung der Stringpuffereigenschaften zurückgibt:
//+------------------------------------------------------------------+ //| Return description of a buffer's string property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property) { return ( property==BUFFER_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==BUFFER_PROP_LABEL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.Label()+"\"") ) : property==BUFFER_PROP_IND_NAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorName()+"\"") ) : property==BUFFER_PROP_IND_NAME_SHORT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME_SHORT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorShortName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorShortName()+"\"") ) : "" ); } //+------------------------------------------------------------------+
Da ich dem Pufferobjekt zwei neue Eigenschaften hinzugefügt habe, sollte ich sie zu der Liste der erlaubten hinzufügen, da wir Nachkommen des Basisobjekts des abstrakten Puffers haben. Diese Nachkommen wiederum verfügen über virtuelle Methoden, die die Flags des Objekts zurückgeben, das verschiedene Eigenschaften unterstützt. Das Hinzufügen der Eigenschaften zu der Liste der erlaubten Eigenschaften ist die einzige Möglichkeit, die Fähigkeit zum Suchen, Auswählen und Sortieren von Pufferobjekten in der Kollektionsliste der Pufferobjekte zu ermöglichen.
Jeder Zeichnungstyp verfügt über ein eigenes Pufferobjekt, und an den Dateien all dieser Objekte sollten Verbesserungen vorgenommen werden. Da alle Änderungen in allen Objekten ähnlich sind, betrachten wir die Verbesserung am Beispiel des Linienpufferobjekts in \MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh.
Die Änderungen wurden durch zwei virtuelle Methoden realisiert, die die Flags des Objekts zurückgeben, das die Eigenschaften integer und string unterstützt:
//+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || ( this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT_BASE && property!=BUFFER_PROP_IND_LINE_MODE && property!=BUFFER_PROP_IND_HANDLE && property!=BUFFER_PROP_IND_TYPE && property!=BUFFER_PROP_ID ) ) return false; return true; } //+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return false; return true; } //+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| string property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_STRING property) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_IND_NAME_SHORT) return false; return true; } //+------------------------------------------------------------------+
Vorausgesetzt, dass es sich bei dem Pufferobjekt um einen Berechnungspuffer handelt, gibt die Methode, wenn sie eine der Eigenschaften erhält, die nicht in der Enumerationsliste vorhanden sind, false zurück — das Objekt unterstützt eine solche Eigenschaft nicht, andernfalls unterstützt das Objekt eine solche Eigenschaft und die Methode gibt true zurück.
Solche Änderungen (oder ähnliche in BufferCalculate.mqh) werden in allen Dateien der Nachkommenklassen des abstrakten Pufferobjekts implementiert: BufferArrow.mqh, BufferBars.mqh, BufferCalculate.mqh, BufferCandles.mqh, BufferFilling.mqh, BufferHistogram.mqh, BufferHistogram2.mqh, BufferSection.mqh, BufferZigZag.mqh und BufferLine.mqh, die ich bereits berücksichtigt habe. Die Änderungen finden Sie in den angehängten Dateien.
Im vorigen Artikel habe ich die Methoden zum Erstellen von Pufferobjekten für mehrsymbol- und mehrperiodische Standardindikatoren erstellt, die ihre Daten im Hauptunterfenster des Charts anzeigen. Im aktuellen Artikel werde ich die Bibliothek um die Methoden zur Erstellung von Standardindikatoren ergänzen, die ihre Daten im Hauptchart-Fenster anzeigen — diese Methoden unterscheiden sich nicht von den bereits erstellten. Sie sind für alle Standardindikatoren des Hauptfensters implementiert worden. Ich werde eine solche Methode am Beispiel des Gleitenden Durchschnitts betrachten:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period MA | //+------------------------------------------------------------------+ int CBuffersCollection::CreateMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iMA(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price); int identifier=(id==WRONG_VALUE ? IND_MA : id); color array_colors[1]={clrRed}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_MA); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("MA("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Moving Average"); buff.SetColors(array_colors); //--- Create a calculated buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_MA); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("MA("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Moving Average"); } return handle; } //+------------------------------------------------------------------+
Mehr erfahren Sie über die Funktionsweise der Methode im vorherigen Artikel. Der einzige Unterschied zu den zuvor betrachteten ähnlichen Methoden ist das Setzen einer neuen Eigenschaft für das Pufferobjekt — der Linientyp eines Single-Puffer-Indikators wird als Main gesetzt. Solche Ergänzungen wurden zu allen zuvor implementierten Methoden zur Erstellung von Single-Buffer-Standardindikatoren in einem im vorherigen Artikel betrachteten Unterfenster vorgenommen, ebenso wie zu den Methoden zur Erstellung von Single-Buffer-Standardindikatoren im Hauptfenster, die bereits zu des Methoden der Datei der Kollektionsklasse der Puffer BuffersCollection.mqh — these are CreateAMA(), CreateDEMA(), CreateFrAMA(), CreateMA(), CreateSAR(), CreateTEMA() und CreateVIDYA() hinzugefügt wurden.
Dieser Wert der Linie wurde bereits als Standardwert im Klassenkonstruktor festgelegt, aber hier habe ich die unbedingte Einstellung dieser Eigenschaft hinzugefügt, so dass diese Methoden den Methoden zur Erstellung von Multi-Puffer-Standardindikatoren entsprechen, die ich jetzt betrachten werde.
Schauen wir uns die Methoden zur Erstellung von Multi-Puffer-Standardindikatoren am Beispiel der Methode zur Erstellung des Standardindikators Average Directional Movement Index (ADX) an:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period ADX | //+------------------------------------------------------------------+ int CBuffersCollection::CreateADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iADX(symbol,timeframe,adx_period); int identifier=(id==WRONG_VALUE ? IND_ADX : id); color array_colors[1]={clrLightSeaGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary ADX line parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); // This is the main indicator line buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel(buff.IndicatorShortName()); buff.SetColors(array_colors); //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary +DI line parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_PLUS); // This is a +DI line buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel("+DI"); array_colors[0]=clrYellowGreen; buff.SetColors(array_colors); buff.SetStyle(STYLE_DOT); //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary -DI line parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_MINUS); // This is a -DI line buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel("-DI"); array_colors[0]=clrWheat; buff.SetColors(array_colors); buff.SetStyle(STYLE_DOT); //--- Create a calculated ADX line buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary ADX line parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); //--- Create a calculated +DI line buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary +DI line parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_PLUS); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("+DI"); //--- Create a calculated -DI line buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary -DI line parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_MINUS); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("-DI"); } return handle; } //+------------------------------------------------------------------+
Die Logik ist hier völlig identisch mit den zuvor erstellten Methoden. Die Anzahl der Pufferobjekte hängt von der Anzahl der Linien ab, die von jedem spezifischen Standardindikator gezeichnet werden. Um die Zugehörigkeit jedes erstellten Pufferobjekts zu einer entsprechenden Standardindikatorlinie festzulegen, setzen Sie den Linienwert aus der oben hinzugefügten Enumeration ENUM_INDICATOR_LINE_MODE. Auf diese Weise entspricht jedes Pufferobjekt der für es angegebenen Indikatorlinie. Jede Indikatorlinie besteht aus zwei Pufferobjekten — einem zu zeichnenden und einem Berechnungspuffer. In den Berechnungspuffer schreiben Sie Daten aus dem Handle des erstellten Standardindikators, während der zu zeichnende Puffer die Daten des berechneten Puffers in das Hauptdiagramm anzeigt. Dies ist die gesamte Logik der Methode.
Die übrigen Methoden sind fast identisch, mit Ausnahme des Indikatornamens, ihren Kurznamen und den Namen jeder der Linien, da jede Standardindikatorlinie ihren eigenen Namen und ihre eigene Beschreibung hat, die der Art und Weise entspricht, wie der Indikator seine Liniennamen im Datenfenster anzeigt.
Die folgenden Methoden zur Erstellung von Multi-Puffer-Standardindikatorobjekten wurden implementiert:CreateADX(), CreateADXWilder(), CreateMACD(), CreateRVI(), CreateStochastic(), CreateBands(), CreateEnvelopes() and CreateFractals(). Die Methoden sind identisch mit den oben besprochenen. Es hat keinen Sinn, sie erneut zu erläutern. Sie finden sie in den angehängten Dateien.
Im vorigen Artikel habe ich damit begonnen, die Methoden zur Vorbereitung der Daten der Berechnungspuffer zu erstellen, die Pufferdaten zu löschen und die Werte für den zu zeichnenden Puffer einzutragen. Mit Hilfe des Operators switch werden die mit den Puffern ausgeführten Aktionen entsprechend dem Indikatortyp verteilt. Wie sich herausstellt, sind nur geringfügige Verbesserungen erforderlich, um die Handhabung der heute erstellten Standardindikatorobjekte zu implementieren.
Die Methode bereitet die Berechnungspuffer des angegebenen Standardindikators vor:
//+------------------------------------------------------------------+ //| Prepare the calculated buffer data | //| of the specified standard indicator | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy) { CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id); CArrayObj *list0=NULL,*list1=NULL,*list2=NULL; list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); if(list_ind==NULL || list_ind.Total()==0) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return 0; } CBufferCalculate *buffer=NULL; int copied=WRONG_VALUE; int idx0=0,idx1=1,idx2=2; switch((int)std_ind) { //--- 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 : buffer=list_ind.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),0,0,total_copy); return copied; //--- Multi-buffer standard indicators case IND_ALLIGATOR : case IND_GATOR : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_BANDS : idx0=1; idx1=0; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_ENVELOPES : case IND_FRACTALS : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; return copied; //--- Multi-buffer standard indicators in a subwindow case IND_ADX : case IND_ADXW : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_ICHIMOKU : break; default: break; } return 0; } //+------------------------------------------------------------------+
Wie Sie sehen, werden die Aktionen, die bei der Handhabung von Pufferobjekten durchgeführt werden, in separaten Blocken abgearbeitet. Jeder Block hat seine eigenen Indikatoren, die auf ähnliche Weise behandelt werden. Nur Aktionen, die identisch für verschiedene Typen von Standardindikatoren sind, sich aber von anderen Typen von Standardindikatoren unterscheiden, werden an separate Blöcke zur Behandlung gesendet. Im Falle des Indikators Bollinger Bänder musste ich die Indizes seiner Puffer vertauschen, weil sich seine Pufferindizierung (obere Linie, mittlere Linie und untere Linie) aus irgendeinem Grund von der gleichen Indizierung im Datenfenster (obere Linie, untere Linie, mittlere Linie) unterscheidet. Daher musste ich zwei zusätzliche Variablen idx0 und idx1 implementieren, die reale Indizes von Standard-Indikatorlinien für jeden Typ aufweisen. Bei allen anderen Indikatortypen gehen ihre Linienindizes in eine Reihe: 0, 1 und 2, während im Falle der Bollinger Bändern die ersten beiden Indizes umgekehrt sind: 1, 0 und 2.
Die übrige Logik der Methode habe ich bereits im vorigen Artikel besprochen. Hier habe ich einfach die Behandlung neu geschaffener Standardindikatorobjekte hinzugefügt.
Die Methode löscht Pufferdaten des angegebenen Standardindikators durch den Zeitreihenindex:
//+------------------------------------------------------------------+ //| Clear buffer data of the specified standard indicator | //| by the timeseries index | //+------------------------------------------------------------------+ void CBuffersCollection::ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index) { //--- Get the list of buffer objects by type and ID CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id); CArrayObj *list0=NULL,*list1=NULL,*list2=NULL; if(list_ind==NULL || list_ind.Total()==0) return; list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); if(list_ind.Total()==0) return; CBuffer *buffer=NULL; switch((int)std_ind) { //--- 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 : buffer=list_ind.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; //--- Multi-buffer standard indicators case IND_ALLIGATOR: case IND_GATOR : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ADX : case IND_ADXW : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_BANDS : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ENVELOPES : case IND_FRACTALS : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ICHIMOKU : break; default: break; } } //+------------------------------------------------------------------+
Genau wie in der vorherigen Methode habe ich hier eine ähnliche Behandlung für verschiedene Standard-Indikatorobjekte zusammengefasst.
Wenn wir genauer hinsehen, stellen wir fest, dass viele Indikatoren, die sich in getrennten Gruppen befinden, eine identische Behandlungslogik haben, und der ganze Unterschied liegt nur in den Namen ihrer Linienkonstanten. Ich habe dies am Anfang des Artikels erwähnt. Nachdem ich die Objekte der drei verbleibenden Standardindikatoren erstellt habe, deren Linien zunächst auf einem Chart mit einer Verschiebung dargestellt werden (Alligator, Gator und Ishimoku), werde ich diese Methoden optimieren, indem ich ähnliche Linienindexwerte für verschiedene Konstanten setze, die in ihrem Zweck identisch sind. Dadurch wird der Methodencode verkleinert.
Die Methode, bei der die Werte für das aktuelle Chart durch den Zeitreihenindex auf die Puffer des angegebenen Standardindikators gesetzt werden:
//+------------------------------------------------------------------+ //| Set values for the current chart to the buffers of the specified | //| standard indicator by the timeseries index according to | //| the 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 objects 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); //--- Exit if any of the lists is empty if(list_data.Total()==0 || list_calc.Total()==0) return false; //--- Declare the necessary objects and variables CBuffer *buffer_data0=NULL; CBuffer *buffer_data1=NULL; CBuffer *buffer_data2=NULL; CBuffer *buffer_calc0=NULL; CBuffer *buffer_calc1=NULL; CBuffer *buffer_calc2=NULL; int index_period=0; int series_index_start=0; int num_bars=1,index=0; uchar clr=color_index; long vol0=0,vol1=0; datetime time_period=0; double value00=EMPTY_VALUE, value01=EMPTY_VALUE; double value10=EMPTY_VALUE, value11=EMPTY_VALUE; double value20=EMPTY_VALUE, value21=EMPTY_VALUE; //--- Depending on the 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 : //--- Get drawn and calculated buffer objects buffer_data0=list_data.At(0); buffer_calc0=list_calc.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio 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_ADX : case IND_ADXW : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,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; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(2,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio 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_data2.SetBufferValue(2,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_BANDS : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,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; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(2,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio 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_data2.SetBufferValue(2,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_ENVELOPES : case IND_FRACTALS : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,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; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio 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_MACD : case IND_RVI : case IND_STOCHASTIC : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,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; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio 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_ALLIGATOR: break; case IND_GATOR : break; case IND_ICHIMOKU : break; default: break; } return false; } //+------------------------------------------------------------------+
Diese Methode ist identisch mit den beiden oben diskutierten Methoden aufgebaut. Die gleichartige Behandlung verschiedener Standardindikatoren sind hier ebenfalls gruppiert. Auch diese Methode soll nach der Einstellung der Werte für die Konstanten gleichartiger Indikatorlinien optimiert werden.
Die Methode, die die Pufferbeschreibung des Standardindikators nach Typ und ID zurückgibt, wurde zuvor in der Klassendatei der Bibliothek CEngine implementiert. Lassen Sie uns ihre Implementierung in die Kollektionsklasse der Puffer verschieben. Dazu deklarieren Sie diese Methode zusammen mit der anderen Methode, die den Kurznamen des Indikators zurückgibt, ganz am Ende des Klassenkörpers:
//--- Return the standard indicator buffer description by type and ID string GetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode=INDICATOR_LINE_MODE_MAIN); //--- Return the standard indicator short name by type and ID string GetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id); //--- Constructor CBuffersCollection(); //--- Get pointers to the timeseries collection (the method is called in the CollectionOnInit() method of the CEngine object) void OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries; } }; //+------------------------------------------------------------------+
Schreiben wir ihre Implementierung außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Return the standard indicator buffer description | //| by type and ID | //+------------------------------------------------------------------+ string CBuffersCollection::GetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode=INDICATOR_LINE_MODE_MAIN) { CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_MODE,line_mode,EQUAL); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.Label(); } //+------------------------------------------------------------------+ //| Return the standard indicator short name | //| by type and ID | //+------------------------------------------------------------------+ string CBuffersCollection::GetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id) { CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.IndicatorShortName(); } //+------------------------------------------------------------------+
Ich habe mich schon oft mit der Logik solcher Methoden befasst, und ich glaube, hier ist alles klar. Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren stellen.
Jetzt müssen wir den Zugriff auf alle erstellten Methoden in der Hauptklasse der Bibliothek CEngine implementieren. Da ich alle Methoden zur Erstellung von Standardindikatorobjekten bereits im vorigen Artikel implementiert habe, muss ich jetzt nur noch die Änderung beim Aufruf der Methoden vornehmen, die die Beschreibung der Indikatorlinien und ihren "short names" zurückgeben:
//--- Prepare data of the calculated buffer of all created standard indicators bool BufferPreparingDataAllBuffersStdInd(void) { return this.m_buffers.PreparingDataAllBuffersStdInd(); } //--- Return the standard indicator buffer description by type and ID string BufferGetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode) { return this.m_buffers.GetLabelByTypeID(ind_type,id,line_mode); } //--- Return the standard indicator short name by type and ID string BufferGetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id) { return this.m_buffers.GetIndicatorShortNameByTypeID(ind_type,id); } //--- Display short description of all indicator buffers of the buffer collection
Die Methoden geben das Ergebnis des Aufrufs gleichnamiger Methoden der Kollektionsklasse der Puffer zurück.
Jetzt brauche ich nur noch die Implementierung der Methode zu entfernen, die ich von der Klasse CEngine in die Kollektionsklasse der Puffer verschoben habe:
//+------------------------------------------------------------------+
//| Return the standard indicator buffer description |
//| by type and ID |
//+------------------------------------------------------------------+
string CEngine::BufferGetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id)
{
CArrayObj *list=m_buffers.GetListBufferByTypeID(ind_type,id);
if(list==NULL || list.Total()==0)
return "";
CBuffer *buff=list.At(0);
if(buff==NULL)
return "";
return buff.Label();
}
//+------------------------------------------------------------------+
Dies sind derzeit alle Änderungen, die umgesetzt werden müssen, um die Objekte der Multi-Puffer-Standardindikatoren zu schaffen.
Test
Um einen Test durchzuführen, nehmen wir den Indikator aus dem vorherigen Artikel und verwenden ihn, um zwei neue Indikatoren zu erstellen — der erste dient dazu, Standardindikatoren mit mehreren Symbolen und mehreren Perioden in einem Unterfenster anzuzeigen, während der zweite das Gleiche im Hauptfenster eines Symbolcharts tun soll.
Die Logik der Indikatoren hat sich im Vergleich zu dem bereits berücksichtigten Testindikator aus dem vorherigen Artikel in keiner Weise geändert. Ich werde nur den Aufruf der Methoden zur Erstellung der notwendigen Indikatoren in OnInit() hinzufügen.
Speichern Sie den Indikator aus dem vorherigen Artikel in \MQL5\Indikatoren\TestDoEasy\Part49\ als TestDoEasyPart49_1.mq5.
Dieser Indikator soll Standardindikatoren im Unterfenster des aktuellen Symbolcharts erstellen und anzeigen. Sein OnInit() wird wie folgt aussehen:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Write the name of the working timeframe selected in the settings to the 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 a selected standard indicator bool success=false; switch(InpIndType) { //--- Single-buffer standard indicators in a subwindow case IND_AC : success=engine.BufferCreateAC(InpUsedSymbols,InpPeriod,1); break; case IND_AD : success=engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_AO : success=engine.BufferCreateAO(InpUsedSymbols,InpPeriod,1); break; case IND_ATR : success=engine.BufferCreateATR(InpUsedSymbols,InpPeriod,14,1); break; case IND_BEARS : success=engine.BufferCreateBearsPower(InpUsedSymbols,InpPeriod,13,1); break; case IND_BULLS : success=engine.BufferCreateBullsPower(InpUsedSymbols,InpPeriod,13,1); break; case IND_BWMFI : success=engine.BufferCreateBWMFI(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_CHAIKIN : success=engine.BufferCreateChaikin(InpUsedSymbols,InpPeriod,3,10,MODE_EMA,VOLUME_TICK,1); break; case IND_CCI : success=engine.BufferCreateCCI(InpUsedSymbols,InpPeriod,14,PRICE_TYPICAL,1); break; case IND_DEMARKER : success=engine.BufferCreateDeMarker(InpUsedSymbols,InpPeriod,14,1); break; case IND_FORCE : success=engine.BufferCreateForce(InpUsedSymbols,InpPeriod,13,MODE_SMA,VOLUME_TICK,1); break; case IND_MOMENTUM : success=engine.BufferCreateMomentum(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_MFI : success=engine.BufferCreateMFI(InpUsedSymbols,InpPeriod,14,VOLUME_TICK,1); break; case IND_OSMA : success=engine.BufferCreateOsMA(InpUsedSymbols,InpPeriod,12,26,9,PRICE_CLOSE,1); break; case IND_OBV : success=engine.BufferCreateOBV(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_RSI : success=engine.BufferCreateRSI(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_STDDEV : success=engine.BufferCreateStdDev(InpUsedSymbols,InpPeriod,20,0,MODE_SMA,PRICE_CLOSE,1); break; case IND_TRIX : success=engine.BufferCreateTriX(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_WPR : success=engine.BufferCreateWPR(InpUsedSymbols,InpPeriod,14,1); break; case IND_VOLUMES : success=engine.BufferCreateVolumes(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; //--- Multi-buffer standard indicators in a subwindow case IND_ADX : success=engine.BufferCreateADX(InpUsedSymbols,InpPeriod,14,1); break; case IND_ADXW : success=engine.BufferCreateADXWilder(InpUsedSymbols,InpPeriod,14,1); break; case IND_MACD : success=engine.BufferCreateMACD(InpUsedSymbols,InpPeriod,12,26,9,PRICE_CLOSE,1); break; case IND_RVI : success=engine.BufferCreateRVI(InpUsedSymbols,InpPeriod,10,1); break; case IND_STOCHASTIC : success=engine.BufferCreateStochastic(InpUsedSymbols,InpPeriod,5,3,3,MODE_SMA,STO_LOWHIGH,1); break; default: break; } if(!success) { Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created")); return INIT_FAILED; } //--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal()); //--- Create the color array and set non-default colors to all buffers within the collection //--- (commented out since the colors have already been set in the methods of creating default standard indicators) //--- (we can always set necessary colors either for all indicators, like here, or for each of them individually) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Display short descriptions of created indicator buffers engine.BuffersPrintShort(); //--- Set levels where they are required and define the data decimal capacity int digits=(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS); switch(InpIndType) { 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; } //--- Set the short name for the indicator and bit depth string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- CArrayObj *list=engine.GetListBuffers(); int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; buff.Print(); } //return INIT_FAILED; //--- //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Der vollständige Indikatorcode ist in den unten angehängten Dateien enthalten.
Stellen Sie EURUSD und 4 Stunden in den Indikator-Einstellungen ein, kompilieren Sie den Indikator und starten Sie ihn auf EURUSD H1. Somit sollen die in den Einstellungen gewählten Standardindikatoren aus H4 auf H1 angezeigt werden:
Lassen Sie uns nun den Indikator erstellen, der die Standardindikatoren im Hauptfenster des Symbolcharts anzeigt.
Speichern Sie den Indikator aus dem vorherigen Artikel in \MQL5\Indikatoren\TestDoEasy\Part49\ als TestDoEasyPart49_2.mq5.
Sein OnInit() sieht wie folgt aus:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Write the name of the working timeframe selected in the settings to the 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 a selected standard indicator bool success=false; switch(InpIndType) { //--- Single-buffer standard indicators in the main window case IND_AMA : success=engine.BufferCreateAMA(InpUsedSymbols,InpPeriod,9,2,30,0,PRICE_CLOSE,1); break; case IND_DEMA : success=engine.BufferCreateDEMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_FRAMA : success=engine.BufferCreateFrAMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_MA : success=engine.BufferCreateMA(InpUsedSymbols,InpPeriod,10,0,MODE_SMA,PRICE_CLOSE,1); break; case IND_SAR : success=engine.BufferCreateSAR(InpUsedSymbols,InpPeriod,0.02,0.2,1); break; case IND_TEMA : success=engine.BufferCreateTEMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_VIDYA : success=engine.BufferCreateVIDYA(InpUsedSymbols,InpPeriod,9,12,0,PRICE_CLOSE,1); break; //--- Multi-buffer standard indicators in a subwindow case IND_BANDS : success=engine.BufferCreateBands(InpUsedSymbols,InpPeriod,20,0,2.0,PRICE_CLOSE,1); break; case IND_ENVELOPES : success=engine.BufferCreateEnvelopes(InpUsedSymbols,InpPeriod,14,0,MODE_SMA,PRICE_CLOSE,0.1,1); break; case IND_FRACTALS : success=engine.BufferCreateFractals(InpUsedSymbols,InpPeriod,1); break; default: break; } if(!success) { Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created")); return INIT_FAILED; } //--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal()); //--- Create the color array and set non-default colors to all buffers within the collection //--- (commented out since the colors have already been set in the methods of creating default standard indicators) //--- (we can always set necessary colors either for all indicators, like here, or for each of them individually) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Display short descriptions of created indicator buffers engine.BuffersPrintShort(); //--- Set levels where they are required and define the data decimal capacity int digits=(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS); switch(InpIndType) { 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; } //--- Set the short name for the indicator and bit depth string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- CArrayObj *list=engine.GetListBuffers(); int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; buff.Print(); } //return INIT_FAILED; //--- //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Der vollständige Indikatorcode ist in den unten angehängten Dateien enthalten.
Stellen Sie EURUSD und 4 Stunden in den Indikator-Einstellungen ein, kompilieren Sie den Indikator und starten Sie ihn auf EURUSD H1. Somit sollen die in den Einstellungen gewählten Standardindikatoren aus H4 auf H1 angezeigt werden:
Was kommt als Nächstes?
Im nächsten Artikel werde ich die Bibliothek der Klasse für die Erstellung von Standardindikatoren mit mehreren Symbolen und Perioden weiter verbessern, die Methoden optimieren und unnötigen Code im bibliotheksbasierten Hauptindikatorprogramm loswerden.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Testindikatoren angehängt, die Sie testen und herunterladen können.
Hinterlassen Sie Ihre Fragen, Kommentare und Anregungen in den Kommentaren.
Bitte bedenken Sie, dass ich hier den MQL5-Testindikator für MetaTrader 5 entwickelt habe.
Die angehängten Dateien sind nur für MetaTrader 5 bestimmt. Die aktuelle Bibliotheksversion wurde nicht mit dem MetaTrader 4 getestet.
Nachdem ich die Funktionalität für die Arbeit mit Indikatorpuffern entwickelt und getestet habe, werde ich versuchen, einige MQL5-Funktionen in MetaTrader 4 zu implementieren.
Frühere Artikel dieser Serie:
Zeitreihen in der Bibliothek DoEasy (Teil 35): das Balkenobjekt und die Liste der Zeitreihen eines SymbolsZeitreihen 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
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/8292
- 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.