
Andere Klassen in der Bibliothek DoEasy (Teil 71): Ereignisse der Kollektion von Chartobjekten
Inhalt
- Konzept
- Verbesserung der Bibliothek der Klasse
- Kontrollieren von Chart-Ereignissen
- Test
- Was kommt als Nächstes?
Konzept
Im vorigen Artikel habe ich die automatische Aktualisierung einiger Eigenschaften von Chartobjekten und zugehörigen Objekten vorgestellt — das Öffnen eines neuen/schließen eines bestehenden Symbolcharts (Chartobjekt), das Hinzufügen eines neuen/schließen eines bestehenden Indikatorfensters aus dem Chartobjekt, sowie das Hinzufügen eines neuen/entfernen/ändern eines bestehenden Indikators im Chartfenster.
Im aktuellen Artikel werde ich eine Ereignisfunktionalität für Chartobjekte, Chartfensterobjekte und Indikatorobjekte im Chartfenster erstellen. Diese Funktionalität wird es uns ermöglichen, benutzerdefinierte Ereignisse an das Steuerprogramm Chart zu senden, wenn die oben betrachteten Objekt-Ereignisse registriert werden.
Die Arbeit mit Charts ist mit Blick auf die manuelle Chart-Steuerung implementiert, d.h. wenn Benutzer Änderungen in Charts manuell oder mit Hilfe von Steuerelementen ihres Programms vornehmen, werden die Operationen schrittweise, ein Objekt nach dem anderen, durchgeführt. Eine programmatische Änderung von mehreren Objekten auf einmal innerhalb eines einzigen Timer-Ticks kann zu einer falschen Definition der auftretenden Ereignisse führen. Im besten Fall wird nur das letzte Ereignis aufgezeichnet. Im schlimmsten Fall werden geänderte Objekte falsch definiert. Ich werde eine solche Wahrscheinlichkeit beim Schreiben des Codes sicherlich berücksichtigen und den Code einfügen, der es erlaubt, Objektparameter im Stapelverfahren zu ändern, wo immer dies möglich ist. Ich werde jedoch nicht die programmatische gleichzeitige Behandlung mehrerer Chart-Elemente feinabstimmen und testen. Ich werde auf diese Aufgabe zurückkommen, wenn es Anfragen von den Benutzern der Bibliothek gibt.
Verbesserung der Bibliothek der Klasse
Lassen Sie uns der Bibliothek neue Nachrichten hinzufügen. In \MQL5\Include\DoEasy\Data.mqh fügen wir die neue Nachrichtenindizes hinzu:
MSG_CHART_OBJ_TEMPLATE_SAVED, // Chart template saved MSG_CHART_OBJ_TEMPLATE_APPLIED, // Template applied to chart MSG_CHART_OBJ_INDICATOR_ADDED, // Indicator added MSG_CHART_OBJ_INDICATOR_REMOVED, // Indicator removed MSG_CHART_OBJ_INDICATOR_CHANGED, // Indicator changed MSG_CHART_OBJ_WINDOW_ADDED, // Subwindow added MSG_CHART_OBJ_WINDOW_REMOVED, // Subwindow removed //--- CChartObjCollection MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION, // Chart collection MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ, // Failed to create a new chart object MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART, // Failed to add a chart object to the collection MSG_CHART_COLLECTION_ERR_CHARTS_MAX, // Cannot open new chart. Number of open charts at maximum MSG_CHART_COLLECTION_CHART_OPENED, // Chart opened MSG_CHART_COLLECTION_CHART_CLOSED, // Chart closed }; //+------------------------------------------------------------------+
und Nachrichtentexte, die den neu hinzugefügten Indizes entsprechen:
{"Шаблон графика сохранён","Chart template saved"}, {"Шаблон применён к графику","Template applied to the chart"}, {"Добавлен индикатор","Added indicator"}, {"Удалён индикатор","Removed indicator"}, {"Изменён индикатор","Changed indicator"}, {"Добавлено подокно","Added subwindow"}, {"Удалено подокно","Removed subwindow"}, //--- CChartObjCollection {"Коллекция чартов","Chart collection"}, {"Не удалось создать новый объект-чарт","Failed to create new chart object"}, {"Не удалось добавить объект-чарт в коллекцию","Failed to add chart object to collection"}, {"Нельзя открыть новый график, так как количество открытых графиков уже максимальное","You cannot open a new chart, since the number of open charts is already maximum"}, {"Открыт график","Open chart"}, {"Закрыт график","Closed chart"}, }; //+---------------------------------------------------------------------+
Im aktuellen Artikel werden wir einige Chart-Ereignisse behandeln. Um diese zu verfolgen und ein bestimmtes Ereignis anzugeben, erstellen wir eine neue Enumeration der möglichen Chart-Ereignisse in \MQL5\Include\DoEasy\Defines.mqh:
//+------------------------------------------------------------------+ //| Data for working with charts | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of possible chart events | //+------------------------------------------------------------------+ enum ENUM_CHART_OBJ_EVENT { CHART_OBJ_EVENT_NO_EVENT = SIGNAL_MQL5_EVENTS_NEXT_CODE, // No event CHART_OBJ_EVENT_CHART_OPEN, // "New chart opening" event CHART_OBJ_EVENT_CHART_CLOSE, // "Chart closure" event CHART_OBJ_EVENT_CHART_WND_ADD, // "Adding a new window on the chart" event CHART_OBJ_EVENT_CHART_WND_DEL, // "Removing a window from the chart" event CHART_OBJ_EVENT_CHART_WND_IND_ADD, // "Adding a new indicator to the chart window" event CHART_OBJ_EVENT_CHART_WND_IND_DEL, // "Removing an indicator from the chart window" event CHART_OBJ_EVENT_CHART_WND_IND_CHANGE, // "Changing indicator parameters in the chart window" event }; #define CHART_OBJ_EVENTS_NEXT_CODE (CHART_OBJ_EVENT_CHART_WND_IND_CHANGE+1) // The code of the next event after the last chart event code //+------------------------------------------------------------------+
Beim Registrieren von Chart-Ereignissen, die in dieser Enumeration angegeben sind, wird ein benutzerdefiniertes Ereignis an den Programm-Chart gesendet. Dieses benutzerdefinierte Ereignis soll den Typ eines aufgetretenen Ereignisses unter Verwendung der entsprechenden Konstante aus der Enumeration enthalten. Anschließend analysiert das Programm den Ereigniscode und behandelt ihn entsprechend.
Bei der Implementierung der Ereignisbehandlung bin ich auf eine unerwartete Herausforderung gestoßen. Es stellte sich heraus, dass es schwierig ist, einen Index für ein bereits gelöschtes Chart-Subfenster und den darin befindlichen Indikator zu definieren. Um sie bequemer zu definieren, entschied ich mich, eine neue Eigenschaft für das Indikator-Objekt im Chart-Fenster zu implementieren — einen Index des Fensters, in dem es sich befindet.
Alle gelöschten Charts, Chart-Fenster und entsprechende Indikatoren (Objekte, die sie beschreiben) sollen in speziellen Listen gespeichert werden, die es uns ermöglichen, jederzeit ein zuvor gelöschtes Objekt zu erhalten. Dies ist das Objekt, von dem wir den Index eines Indikatorfensters erhalten können (wenn der Indikator gelöscht wurde).
Ergänzen wir die Enumeration der Integer-Eigenschaften des Chartobjekts um eine neue Eigenschaftskonstante für ein Indikatorobjekt im Chartfenster:
//+------------------------------------------------------------------+ //| Chart integer property | //+------------------------------------------------------------------+ enum ENUM_CHART_PROP_INTEGER { CHART_PROP_ID = 0, // Chart ID CHART_PROP_TIMEFRAME, // Chart timeframe CHART_PROP_SHOW, // Price chart drawing CHART_PROP_IS_OBJECT, // Chart object (OBJ_CHART) identification attribute CHART_PROP_BRING_TO_TOP, // Show chart above all others CHART_PROP_CONTEXT_MENU, // Enable/disable access to the context menu using the right click CHART_PROP_CROSSHAIR_TOOL, // Enable/disable access to the Crosshair tool using the middle click CHART_PROP_MOUSE_SCROLL, // Scroll the chart horizontally using the left mouse button CHART_PROP_EVENT_MOUSE_WHEEL, // Send messages about mouse wheel events (CHARTEVENT_MOUSE_WHEEL) to all MQL5 programs on a chart CHART_PROP_EVENT_MOUSE_MOVE, // Send messages about mouse button click and movement events (CHARTEVENT_MOUSE_MOVE) to all MQL5 programs on a chart CHART_PROP_EVENT_OBJECT_CREATE, // Send messages about the graphical object creation event (CHARTEVENT_OBJECT_CREATE) to all MQL5 programs on a chart CHART_PROP_EVENT_OBJECT_DELETE, // Send messages about the graphical object destruction event (CHARTEVENT_OBJECT_DELETE) to all MQL5 programs on a chart CHART_PROP_MODE, // Type of the chart (candlesticks, bars or line (ENUM_CHART_MODE)) CHART_PROP_FOREGROUND, // Price chart in the foreground CHART_PROP_SHIFT, // Mode of shift of the price chart from the right border CHART_PROP_AUTOSCROLL, // The mode of automatic shift to the right border of the chart CHART_PROP_KEYBOARD_CONTROL, // Allow managing the chart using a keyboard CHART_PROP_QUICK_NAVIGATION, // Allow the chart to intercept Space and Enter key strokes to activate the quick navigation bar CHART_PROP_SCALE, // Scale CHART_PROP_SCALEFIX, // Fixed scale mode CHART_PROP_SCALEFIX_11, // 1:1 scale mode CHART_PROP_SCALE_PT_PER_BAR, // The mode of specifying the scale in points per bar CHART_PROP_SHOW_TICKER, // Display a symbol ticker in the upper left corner CHART_PROP_SHOW_OHLC, // Display OHLC values in the upper left corner CHART_PROP_SHOW_BID_LINE, // Display Bid value as a horizontal line on the chart CHART_PROP_SHOW_ASK_LINE, // Display Ask value as a horizontal line on a chart CHART_PROP_SHOW_LAST_LINE, // Display Last value as a horizontal line on a chart CHART_PROP_SHOW_PERIOD_SEP, // Display vertical separators between adjacent periods CHART_PROP_SHOW_GRID, // Display a grid on the chart CHART_PROP_SHOW_VOLUMES, // Display volumes on a chart CHART_PROP_SHOW_OBJECT_DESCR, // Display text descriptions of objects CHART_PROP_VISIBLE_BARS, // Number of bars on a chart that are available for display CHART_PROP_WINDOWS_TOTAL, // The total number of chart windows including indicator subwindows CHART_PROP_WINDOW_HANDLE, // Chart window handle CHART_PROP_WINDOW_YDISTANCE, // Distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window CHART_PROP_FIRST_VISIBLE_BAR, // Number of the first visible bar on the chart CHART_PROP_WIDTH_IN_BARS, // Width of the chart in bars CHART_PROP_WIDTH_IN_PIXELS, // Width of the chart in pixels CHART_PROP_HEIGHT_IN_PIXELS, // Height of the chart in pixels CHART_PROP_COLOR_BACKGROUND, // Color of background of the chart CHART_PROP_COLOR_FOREGROUND, // Color of axes, scale and OHLC line CHART_PROP_COLOR_GRID, // Grid color CHART_PROP_COLOR_VOLUME, // Color of volumes and position opening levels CHART_PROP_COLOR_CHART_UP, // Color for the up bar, shadows and body borders of bull candlesticks CHART_PROP_COLOR_CHART_DOWN, // Color of down bar, its shadow and border of body of the bullish candlestick CHART_PROP_COLOR_CHART_LINE, // Color of the chart line and the Doji candlesticks CHART_PROP_COLOR_CANDLE_BULL, // Color of body of a bullish candlestick CHART_PROP_COLOR_CANDLE_BEAR, // Color of body of a bearish candlestick CHART_PROP_COLOR_BID, // Color of the Bid price line CHART_PROP_COLOR_ASK, // Color of the Ask price line CHART_PROP_COLOR_LAST, // Color of the last performed deal's price line (Last) CHART_PROP_COLOR_STOP_LEVEL, // Color of stop order levels (Stop Loss and Take Profit) CHART_PROP_SHOW_TRADE_LEVELS, // Display trade levels on the chart (levels of open positions, Stop Loss, Take Profit and pending orders) CHART_PROP_DRAG_TRADE_LEVELS, // Enable the ability to drag trading levels on a chart using mouse CHART_PROP_SHOW_DATE_SCALE, // Display the time scale on a chart CHART_PROP_SHOW_PRICE_SCALE, // Display a price scale on a chart CHART_PROP_SHOW_ONE_CLICK, // Display the quick trading panel on the chart CHART_PROP_IS_MAXIMIZED, // Chart window maximized CHART_PROP_IS_MINIMIZED, // Chart window minimized CHART_PROP_IS_DOCKED, // Chart window docked CHART_PROP_FLOAT_LEFT, // Left coordinate of the undocked chart window relative to the virtual screen CHART_PROP_FLOAT_TOP, // Upper coordinate of the undocked chart window relative to the virtual screen CHART_PROP_FLOAT_RIGHT, // Right coordinate of the undocked chart window relative to the virtual screen CHART_PROP_FLOAT_BOTTOM, // Bottom coordinate of the undocked chart window relative to the virtual screen //--- CWndInd CHART_PROP_WINDOW_IND_HANDLE, // Indicator handle in the chart window CHART_PROP_WINDOW_IND_INDEX, // Indicator index in the chart window CHART_PROP_WINDOW_NUM, // Chart window index }; #define CHART_PROP_INTEGER_TOTAL (67) // Total number of integer properties #define CHART_PROP_INTEGER_SKIP (0) // Number of integer DOM properties not used in sorting //+------------------------------------------------------------------+
Da sich die Anzahl der Integer-Eigenschaften erhöht hat, erhöhen wir auch deren Anzahl von 66 auf 67.
Hinzufügen der Sortierung nach dem Index des Chart-Fensters zur Enumeration der Sortierkriterien für Chartobjekte:
//+------------------------------------------------------------------+ //| Possible chart sorting criteria | //+------------------------------------------------------------------+ #define FIRST_CHART_DBL_PROP (CHART_PROP_INTEGER_TOTAL-CHART_PROP_INTEGER_SKIP) #define FIRST_CHART_STR_PROP (CHART_PROP_INTEGER_TOTAL-CHART_PROP_INTEGER_SKIP+CHART_PROP_DOUBLE_TOTAL-CHART_PROP_DOUBLE_SKIP) enum ENUM_SORT_CHART_MODE { //--- Sort by integer properties SORT_BY_CHART_ID = 0, // Sort by chart ID SORT_BY_CHART_TIMEFRAME, // Sort by chart timeframe SORT_BY_CHART_SHOW, // Sort by the price chart drawing attribute SORT_BY_CHART_IS_OBJECT, // Sort by chart object (OBJ_CHART) identification attribute SORT_BY_CHART_BRING_TO_TOP, // Sort by the flag of displaying a chart above all others SORT_BY_CHART_CONTEXT_MENU, // Sort by the flag of enabling/disabling access to the context menu using the right click SORT_BY_CHART_CROSSHAIR_TOO, // Sort by the flag of enabling/disabling access to the Crosshair tool using the middle click SORT_BY_CHART_MOUSE_SCROLL, // Sort by the flag of scrolling the chart horizontally using the left mouse button SORT_BY_CHART_EVENT_MOUSE_WHEEL, // Sort by the flag of sending messages about mouse wheel events to all MQL5 programs on a chart SORT_BY_CHART_EVENT_MOUSE_MOVE, // Sort by the flag of sending messages about mouse button click and movement events to all MQL5 programs on a chart SORT_BY_CHART_EVENT_OBJECT_CREATE, // Sort by the flag of sending messages about the graphical object creation event to all MQL5 programs on a chart SORT_BY_CHART_EVENT_OBJECT_DELETE, // Sort by the flag of sending messages about the graphical object destruction event to all MQL5 programs on a chart SORT_BY_CHART_MODE, // Sort by chart type SORT_BY_CHART_FOREGROUND, // Sort by the "Price chart in the foreground" flag SORT_BY_CHART_SHIFT, // Sort by the "Mode of shift of the price chart from the right border" flag SORT_BY_CHART_AUTOSCROLL, // Sort by the "The mode of automatic shift to the right border of the chart" flag SORT_BY_CHART_KEYBOARD_CONTROL, // Sort by the flag allowing the chart management using a keyboard SORT_BY_CHART_QUICK_NAVIGATION, // Sort by the flag allowing the chart to intercept Space and Enter key strokes to activate the quick navigation bar SORT_BY_CHART_SCALE, // Sort by scale SORT_BY_CHART_SCALEFIX, // Sort by the fixed scale flag SORT_BY_CHART_SCALEFIX_11, // Sort by the 1:1 scale flag SORT_BY_CHART_SCALE_PT_PER_BAR, // Sort by the flag of specifying the scale in points per bar SORT_BY_CHART_SHOW_TICKER, // Sort by the flag displaying a symbol ticker in the upper left corner SORT_BY_CHART_SHOW_OHLC, // Sort by the flag displaying OHLC values in the upper left corner SORT_BY_CHART_SHOW_BID_LINE, // Sort by the flag displaying Bid value as a horizontal line on the chart SORT_BY_CHART_SHOW_ASK_LINE, // Sort by the flag displaying Ask value as a horizontal line on the chart SORT_BY_CHART_SHOW_LAST_LINE, // Sort by the flag displaying Last value as a horizontal line on the chart SORT_BY_CHART_SHOW_PERIOD_SEP, // Sort by the flag displaying vertical separators between adjacent periods SORT_BY_CHART_SHOW_GRID, // Sort by the flag of displaying a grid on the chart SORT_BY_CHART_SHOW_VOLUMES, // Sort by the mode of displaying volumes on a chart SORT_BY_CHART_SHOW_OBJECT_DESCR, // Sort by the flag of displaying object text descriptions SORT_BY_CHART_VISIBLE_BARS, // Sort by the number of bars on a chart that are available for display SORT_BY_CHART_WINDOWS_TOTAL, // Sort by the total number of chart windows including indicator subwindows SORT_BY_CHART_WINDOW_HANDLE, // Sort by the chart handle SORT_BY_CHART_WINDOW_YDISTANCE, // Sort by the distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window SORT_BY_CHART_FIRST_VISIBLE_BAR, // Sort by the number of the first visible bar on the chart SORT_BY_CHART_WIDTH_IN_BARS, // Sort by the width of the chart in bars SORT_BY_CHART_WIDTH_IN_PIXELS, // Sort by the width of the chart in pixels SORT_BY_CHART_HEIGHT_IN_PIXELS, // Sort by the height of the chart in pixels SORT_BY_CHART_COLOR_BACKGROUND, // Sort by the color of the chart background SORT_BY_CHART_COLOR_FOREGROUND, // Sort by color of axes, scale and OHLC line SORT_BY_CHART_COLOR_GRID, // Sort by grid color SORT_BY_CHART_COLOR_VOLUME, // Sort by the color of volumes and position opening levels SORT_BY_CHART_COLOR_CHART_UP, // Sort by the color for the up bar, shadows and body borders of bull candlesticks SORT_BY_CHART_COLOR_CHART_DOWN, // Sort by the color of down bar, its shadow and border of body of the bullish candlestick SORT_BY_CHART_COLOR_CHART_LINE, // Sort by the color of the chart line and the Doji candlesticks SORT_BY_CHART_COLOR_CANDLE_BULL, // Sort by the color of a bullish candlestick body SORT_BY_CHART_COLOR_CANDLE_BEAR, // Sort by the color of a bearish candlestick body SORT_BY_CHART_COLOR_BID, // Sort by the color of the Bid price line SORT_BY_CHART_COLOR_ASK, // Sort by the color of the Ask price line SORT_BY_CHART_COLOR_LAST, // Sort by the color of the last performed deal's price line (Last) SORT_BY_CHART_COLOR_STOP_LEVEL, // Sort by the color of stop order levels (Stop Loss and Take Profit) SORT_BY_CHART_SHOW_TRADE_LEVELS, // Sort by the flag of displaying trading levels on the chart SORT_BY_CHART_DRAG_TRADE_LEVELS, // Sort by the flag enabling the ability to drag trading levels on a chart using mouse SORT_BY_CHART_SHOW_DATE_SCALE, // Sort by the flag of displaying the time scale on the chart SORT_BY_CHART_SHOW_PRICE_SCALE, // Sort by the flag of displaying the price scale on the chart SORT_BY_CHART_SHOW_ONE_CLICK, // Sort by the flag of displaying the quick trading panel on the chart SORT_BY_CHART_IS_MAXIMIZED, // Sort by the "Chart window maximized" flag SORT_BY_CHART_IS_MINIMIZED, // Sort by the "Chart window minimized" flag SORT_BY_CHART_IS_DOCKED, // Sort by the "Chart window docked" flag SORT_BY_CHART_FLOAT_LEFT, // Sort by the left coordinate of the undocked chart window relative to the virtual screen SORT_BY_CHART_FLOAT_TOP, // Sort by the upper coordinate of the undocked chart window relative to the virtual screen SORT_BY_CHART_FLOAT_RIGHT, // Sort by the right coordinate of the undocked chart window relative to the virtual screen SORT_BY_CHART_FLOAT_BOTTOM, // Sort by the bottom coordinate of the undocked chart window relative to the virtual screen SORT_BY_CHART_WINDOW_IND_HANDLE, // Sort by the indicator handle in the chart window SORT_BY_CHART_WINDOW_IND_INDEX, // Sort by the indicator index in the chart window SORT_BY_CHART_WINDOW_NUM, // Sort by chart window index //--- Sort by real properties SORT_BY_CHART_SHIFT_SIZE = FIRST_CHART_DBL_PROP, // Sort by the shift size of the zero bar from the right border in % SORT_BY_CHART_FIXED_POSITION, // Sort by the chart fixed position from the left border in % SORT_BY_CHART_FIXED_MAX, // Sort by the fixed chart maximum SORT_BY_CHART_FIXED_MIN, // Sort by the fixed chart minimum SORT_BY_CHART_POINTS_PER_BAR, // Sort by the scale value in points per bar SORT_BY_CHART_PRICE_MIN, // Sort by the chart minimum SORT_BY_CHART_PRICE_MAX, // Sort by the chart maximum //--- Sort by string properties SORT_BY_CHART_COMMENT = FIRST_CHART_STR_PROP, // Sort by a comment text on the chart SORT_BY_CHART_EXPERT_NAME, // Sort by a name of an EA launched on the chart SORT_BY_CHART_SCRIPT_NAME, // Sort by a name of a script launched on the chart SORT_BY_CHART_WINDOW_IND_NAME, // Sort by a name of an indicator launched in the chart window SORT_BY_CHART_SYMBOL, // Sort by chart symbol }; //+------------------------------------------------------------------+
Wie oben erwähnt, werde ich spezielle Listen verwenden, um Objektkopien der entfernten Indikatoren, Fenster und Charts zu speichern. Wir werden den Zugriff auf diese Listen in jedem Chartobjekt, seinen Fenstern und den zu den Fenstern gehörenden Indikatoren benötigen. Um zu vermeiden, dass die benutzerdefinierten Listen in jeder Objektklasse gespeichert werden, und um den Zugriff auf diese Listen von anderen Objekten aus zu organisieren, die Daten über ein bestimmtes gelöschtes Objekt benötigen, deklarieren wir alle diese Listen in der Klasse Kollektion des Chart-Objekts (die Klasse bietet Zugriff auf alle Chart-Objekte), während Zeiger auf die Listen an andere Objekte (Chart, Chart-Fenster, Indikator im Chart-Fenster) übergeben werden. Somit hat jedes Objekt, das in der Kollektion gespeichert ist, Zugriff auf alle Listen.
Dies führt zu einigen Einschränkungen bei der Verwendung dieser Listen innerhalb von Objekten der Kollektion, abgesehen von dem Objekt der Kollektion selbst. Wir können keine Objekte in den Listen löschen, die Listen zurücksetzen, während wir innerhalb aller Sammlungsobjekte arbeiten, außer der Sammlung selbst, und Zeiger auf die Listen in keiner Weise verändern. Wenn wir jedoch diese Eigenschaft der Verwendung des Zeigers auf die Liste im Auge behalten (und zwar im Nur-Lese-Modus), wird der Zugriff auf die Liste von verschiedenen Objekten aus vereinfacht, da die Übergabe des Zeigers auf das Listenobjekt ausreicht, um dessen Inhalt zu lesen.
Verbessern wir nun die Klassendatei des Indikatorobjekts im Chartfenster und die Objektdatei des Chartfensters (beide Klassen befinden sich in \MQL5\Include\DoEasy\Objects\Chart\ChartWnd.mqh).
Im privaten Klassenteil CWndInddeklarieren wir die Variable zum Speichern des Subfenster-Index, in dem sich der Indikator befindet, während Sie im öffentlichen Teil der Klasse die Methoden zum Setzen und zur Rückgabe aller Objekteigenschaften schreiben (bisher konnten wir nur eine einzige Eigenschaft setzen — den Indikator-Index in der Fensterliste):
//+------------------------------------------------------------------+ //| Chart window indicator object class | //+------------------------------------------------------------------+ class CWndInd : public CObject { private: long m_chart_id; // Chart ID string m_name; // Indicator short name int m_index; // indicator index in the list int m_window_num; // Indicator subwindow index int m_handle; // Indicator handle public: //--- Return itself CWndInd *GetObject(void) { return &this; } //--- Return (1) indicator name, (2) index in the list, (3) indicator handle and (4) subwindow index string Name(void) const { return this.m_name; } int Index(void) const { return this.m_index; } int Handle(void) const { return this.m_handle; } int WindowNum(void) const { return this.m_window_num; } //--- Set (1) subwindow name, (2) window index on the chart, (3) handle, (4) index void SetName(const string name) { this.m_name=name; } void SetIndex(const int index) { this.m_index=index; } void SetHandle(const int handle) { this.m_handle=handle; } void SetWindowNum(const int win_num) { this.m_window_num=win_num; } //--- Display the description of object properties in the journal (dash=true - hyphen before the description, false - description only) void Print(const bool dash=false) { ::Print((dash ? "- " : "")+this.Header()); } //--- Return the object short name string Header(void) const { return CMessage::Text(MSG_CHART_OBJ_INDICATOR)+" "+this.Name(); } //--- Compare CWndInd objects with each other by the specified property virtual int Compare(const CObject *node,const int mode=0) const; //--- Constructors CWndInd(void){;} CWndInd(const int handle,const string name,const int index,const int win_num) : m_handle(handle), m_name(name), m_index(index), m_window_num(win_num) {} }; //+------------------------------------------------------------------+
Fügen wir außerdem dem parametrischen Konstruktor den Index des Chart-Unterfensters hinzu, in dem sich der Indikator befindet, und weisen Sie den übergebenen Wert der entsprechenden Variablen zu.
Nun sollten wir bei der Erstellung eines solchen Objekts zusätzlich den Index des Unterfensters angeben, in dem sich der Indikator befindet, für den das Objekt erstellt wird. So sind wir in der Lage, das Fenster mit dem Indikator zu finden, nachdem wir den Indikator und das Fenster entfernt haben. Das wird es uns erleichtern, den Index eines bereits fehlenden Fensters zu finden, um zu verstehen, welches von ihnen den Indikator enthielt, der zusammen mit dem Fenster entfernt wurde, während sich ihre Objekte in den Listen der entfernten Chartobjekte befinden.
Lassen Sie uns auch im Chart-Fenster-Objekt Änderungen vornehmen. Deklarieren wir im privaten Abschnitt der Klasse die Zeiger auf die Listen der im Fenster geänderten und aus ihm entfernten Indikatoren, sowie die Variable zum Speichern eines Symbols des Charts, zu dem das Fenster gehört. Deklarieren wir die Methode, die das Flag zurückgibt, das das Vorhandensein des Indikators aus dem Fenster in der Liste der Chart-Objekte anzeigt, die Methode, die das Indikator-Objekt zurückgibt, das in der Liste vorhanden ist, aber nicht im Chart-Fenster vorhanden ist und die Methode zur Überprüfung der Änderungen der Parameter von Indikatoren, die im Fenster vorhanden sind:
//+------------------------------------------------------------------+ //| Chart window object class | //+------------------------------------------------------------------+ class CChartWnd : public CBaseObjExt { private: CArrayObj m_list_ind; // Indicator list CArrayObj *m_list_ind_del; // Pointer to the list of indicators removed from the indicator window CArrayObj *m_list_ind_param; // Pointer to the list of changed indicators int m_window_num; // Subwindow index int m_wnd_coord_x; // The X coordinate for the time on the chart in the window int m_wnd_coord_y; // The Y coordinate for the price on the chart in the window string m_symbol; // Symbol of a chart the window belongs to //--- Return the flag indicating the presence of an indicator (1) from the list in the window and (2) from the window in the list bool IsPresentInWindow(const CWndInd *ind); bool IsPresentInList(const string name); //--- Return the indicator object present in the list but not present on the chart CWndInd *GetMissingInd(void); //--- Remove indicators not present in the window from the list void IndicatorsDelete(void); //--- Add new indicators to the list void IndicatorsAdd(void); //--- Check the changes of the parameters of existing indicators void IndicatorsChangeCheck(void); public:
Jetzt wird die Klasse von der erweiterten Basisklasse aller Bibliotheksobjekte abgeleitet und bietet eine Ereignisfunktionalität, die für jedes dieser Objekte einfach durchgeführt werden kann.
Fügen wir im öffentlichen Abschnitt der Klasse, nämlich in der Methode, die das Flag zurückgibt, das anzeigt, dass ein Objekt eine bestimmte Eigenschaft unterstützt, eine weitere unterstützte Eigenschaft hinzu — das Chart-Symbol. Verschieben wir die Implementierung der Methode, die die String-Eigenschaftsbeschreibung zurückgibt, über den Klassenkörper hinaus (und besprechen sie weiter):
public: //--- Return itself CChartWnd *GetObject(void) { return &this; } //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_CHART_PROP_INTEGER property) { return(property==CHART_PROP_WINDOW_YDISTANCE || property==CHART_PROP_HEIGHT_IN_PIXELS ? true : false); } virtual bool SupportProperty(ENUM_CHART_PROP_DOUBLE property) { return false; } virtual bool SupportProperty(ENUM_CHART_PROP_STRING property) { return (property==CHART_PROP_WINDOW_IND_NAME || property==CHART_PROP_SYMBOL ? true : false); } //--- Get description of (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_CHART_PROP_INTEGER property); string GetPropertyDescription(ENUM_CHART_PROP_DOUBLE property) { return CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED); } string GetPropertyDescription(ENUM_CHART_PROP_STRING property);
Deklarieren wir im öffentlichen Abschnitt der Klasse die Methode zum Erzeugen und Senden eines Chart-Ereignisses an das Steuerprogramm Chart. Der parametrische Konstruktor der Klasse empfängt nun den Namen des Chart-Symbols und die Zeiger auf die Listen der entfernten und geänderten Indikatoren des Fensters. Außerdem deklarieren wir den Destruktor der Klasse:
//--- Return the object short name virtual string Header(void); //--- Create and send the chart event to the control program chart void SendEvent(ENUM_CHART_OBJ_EVENT event); //--- Compare CChartWnd objects by a specified property (to sort the list by an MQL5 signal object) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CChartWnd objects by all properties (to search for equal MQL5 signal objects) bool IsEqual(CChartWnd* compared_obj) const; //--- Constructors CChartWnd(void){;} CChartWnd(const long chart_id,const int wnd_num,const string symbol,CArrayObj *list_ind_del,CArrayObj *list_ind_param); //--- Destructor ~CChartWnd(void);
Als Nächstes fügen wir andere notwendige Methoden hinzu, deren Zweck in den Kommentaren des Codes beschrieben wird:
//--- Return (1) the subwindow index, (2) the number of indicators attached to the window and (3) the name of a symbol chart int WindowNum(void) const { return this.m_window_num; } int IndicatorsTotal(void) const { return this.m_list_ind.Total(); } string Symbol(void) const { return m_symbol;} //--- Set (1) the subwindow index and (2) the chart symbol void SetWindowNum(const int num) { this.m_window_num=num; } void SetSymbol(const string symbol) { this.m_symbol=symbol; } //--- Return (1) the indicator list, the window indicator object from the list by (2) index in the list and (3) by handle CArrayObj *GetIndicatorsList(void) { return &this.m_list_ind; } CWndInd *GetIndicatorByIndex(const int index); CWndInd *GetIndicatorByHandle(const int handle); //--- Return (1) the last one added to the window, (2) the last one removed from the window and (3) the changed indicator CWndInd *GetLastAddedIndicator(void) { return this.m_list_ind.At(this.m_list_ind.Total()-1); } CWndInd *GetLastDeletedIndicator(void) { return this.m_list_ind_del.At(this.m_list_ind_del.Total()-1); } CWndInd *GetLastChangedIndicator(void) { return this.m_list_ind_param.At(this.m_list_ind_param.Total()-1);}
Betrachten wir nun die Implementierung der neuen und verbesserten Methoden im Detail.
In der Initialisierungsliste des parametrischen Klassenkonstruktors weisen wir den in den Parametern übergebenen Wert m_Symbol zu, während wir im Hauptteil der Klasse die Werte von Zeigern (die an die Methode übergeben wurden) den Variablenzeigern der Listen zuweisen:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CChartWnd::CChartWnd(const long chart_id,const int wnd_num,const string symbol,CArrayObj *list_ind_del,CArrayObj *list_ind_param) : m_window_num(wnd_num), m_symbol(symbol), m_wnd_coord_x(0), m_wnd_coord_y(0) { this.m_list_ind_del=list_ind_del; this.m_list_ind_param=list_ind_param; CBaseObj::SetChartID(chart_id); this.IndicatorsListCreate(); } //+------------------------------------------------------------------+
Im vorherigen Artikel (nämlich in den Methoden, die Daten zu den an das Fenster angehängten Indikatoren liefern) wurden die Indikator-Handles aus der Indikatorliste genommen. Nach dem Lesen der Indikatordaten wurde das Handle sofort wieder freigegeben. Das Ergebnis war, dass jedes Mal ein neues Handle für denselben Indikator erstellt wurde (aufgrund meines falschen Verständnisses der Hilfeinformationen — das Indikator-Handle sollte nur dann freigegeben werden, wenn es wirklich nicht mehr benötigt wird, nämlich wenn die Programmoperation beendet ist, und nicht sofort nach dem Erhalt der Daten über das Handle, während der Indikator vom Programm weiter verwendet werden soll). Dies werde ich im aktuellen Artikel beheben — die Handles aller Indikatoren sollen erst im Destruktor der Klasse freigegeben werden.
Der Destruktor der Klasse:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CChartWnd::~CChartWnd(void) { int total=this.m_list_ind.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { CWndInd *ind=this.m_list_ind.At(i); if(ind==NULL) continue; ::IndicatorRelease(ind.Handle()); this.m_list_ind.Delete(i); } } //+------------------------------------------------------------------+
Hier in der Schleife durch die Liste der Fensterindikatorobjekte, das nächste Objekt holen, das Indikatorhandle freigeben in den Objekteigenschaften setzen und das Objekt selbst entfernen.
Die virtuelle Methode des Vergleichs zweier Objekte anhand einer angegebenen Eigenschaft empfängt den Vergleich anhand des Fensterindex und nach dem Chart-Symbol:
//+------------------------------------------------------------------+ //| Compare CChartWnd objects with each other by a specified property| //+------------------------------------------------------------------+ int CChartWnd::Compare(const CObject *node,const int mode=0) const { const CChartWnd *obj_compared=node; if(mode==CHART_PROP_WINDOW_YDISTANCE) return(this.YDistance()>obj_compared.YDistance() ? 1 : this.YDistance()<obj_compared.YDistance() ? -1 : 0); else if(mode==CHART_PROP_HEIGHT_IN_PIXELS) return(this.HeightInPixels()>obj_compared.HeightInPixels() ? 1 : this.HeightInPixels()<obj_compared.HeightInPixels() ? -1 : 0); else if(mode==CHART_PROP_WINDOW_NUM) return(this.WindowNum()>obj_compared.WindowNum() ? 1 : this.WindowNum()<obj_compared.WindowNum() ? -1 : 0); else if(mode==CHART_PROP_SYMBOL) return(this.Symbol()==obj_compared.Symbol() ? 0 : this.Symbol()>obj_compared.Symbol() ? 1 : -1); return -1; } //+------------------------------------------------------------------+
Dies ist notwendig, um Objekte in den Listen nach Chart-Symbol und der neu implementierten Objekteigenschaft zu sortieren und zu suchen, dem Fensterindex.
Die Implementierung der Methode, die die Beschreibung der Objekt-String-Eigenschaft zurückgibt, wurde außerhalb des Hauptteils der Klasse verschoben:
//+------------------------------------------------------------------+ //| Return description of object's string property | //+------------------------------------------------------------------+ string CChartWnd::GetPropertyDescription(ENUM_CHART_PROP_STRING property) { return ( property==CHART_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : "" ); } //+------------------------------------------------------------------+
Wie in allen ähnlichen Methoden aller Bibliotheksobjekte erstellen wir hier eine Textbeschreibung der an die Methode übergebenen Eigenschaft:
Wenn es sich um eine Eigenschaft des "Chart-Symbol" handelt, geben wir ihrer textliche Beschreibung zurück, für jede andere Eigenschaft eine leere Zeichenkette.
Wenn die Eigenschaft das Flag "Eigenschaft nicht unterstützt" hat, wird die Zeichenkette mit demselben Inhalt zurückgegeben.
In der Methode, die die Liste der an das Fenster angehängten Indikatoren erstellt, entfernen wir die Zeichenkette, in der das Handle des aktuell ausgewählten Indikators freigegeben wird (der Grund wurde oben besprochen):
//--- get and save the indicator handle by its short name int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); //--- Free the indicator handle ::IndicatorRelease(handle); //--- Create the new indicator object in the chart window
die Zeile, in der wir ein neues Indikatorobjekt im Chart-Fenster erstellen, erhält die Möglichkeit, die aktuelle Fensterindexklasse an den Konstruktor zu übergeben:
//+------------------------------------------------------------------+ //| Create the list of indicators attached to the window | //+------------------------------------------------------------------+ void CChartWnd::IndicatorsListCreate(void) { //--- Clear the indicator lists this.m_list_ind.Clear(); //--- Get the total number of indicators in the window int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num); //--- In the loop by the number of indicators, for(int i=0;i<total;i++) { //--- obtain and save the short indicator name, string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i); //--- get and save the indicator handle by its short name int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); //--- Create the new indicator object in the chart window CWndInd *ind=new CWndInd(handle,name,i,this.WindowNum()); if(ind==NULL) continue; //--- set the sorted list flag to the list this.m_list_ind.Sort(); //--- If failed to add the object to the list, remove it if(!this.m_list_ind.Add(ind)) delete ind; } } //+------------------------------------------------------------------+
Jetzt "weiß" jedes Indikatorobjekt im Chart-Fenster, in welchem Fenster es sich befindet.
Dasselbe geschieht mit der Methode zum Hinzufügen neuer Indikatoren zur Liste:
Wir entfernen die Zeilen
int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); //--- Free the indicator handle ::IndicatorRelease(handle); //--- Create the new indicator object in the chart window
und übergeben den Fensterindex beim Erstellen eines neuen Indikatorobjekts im Chart-Fenster:
//+------------------------------------------------------------------+ //| Add new indicators to the list | //+------------------------------------------------------------------+ void CChartWnd::IndicatorsAdd(void) { //--- Get the total number of indicators in the window int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num); //--- In the loop by the number of indicators, for(int i=0;i<total;i++) { //--- obtain and save the short indicator name, string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i); //--- get and save the indicator handle by its short name int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); //--- Create the new indicator object in the chart window CWndInd *ind=new CWndInd(handle,name,i,this.WindowNum()); if(ind==NULL) continue; //--- set the sorted list flag to the list this.m_list_ind.Sort(); //--- If the object is already in the list or an attempt to add it to the list failed, remove it if(this.m_list_ind.Search(ind)>WRONG_VALUE || !this.m_list_ind.Add(ind)) delete ind; } } //+------------------------------------------------------------------+
In der Methode, die das Flag für das Vorhandensein des Indikators aus der Liste im Fenster zurückgibt, wird auch die Zeichenkette entfernt:
int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); ::IndicatorRelease(handle);
Nun geben wir auch in dieser Methode den Indikator-Handle nicht frei:
//+--------------------------------------------------------------------------------------+ //| Return the flag indicating the presence of an indicator from the list in the window | //+--------------------------------------------------------------------------------------+ bool CChartWnd::IsPresentInWindow(const CWndInd *ind) { int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num); for(int i=0;i<total;i++) { string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i); int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); if(ind.Name()==name && ind.Handle()==handle) return true; } return false; } //+------------------------------------------------------------------+
Die Methode prüft Änderungen in den Parametern der vorhandenen Indikatoren:
//+------------------------------------------------------------------+ //| Check the changes of the parameters of existing indicators | //+------------------------------------------------------------------+ void CChartWnd::IndicatorsChangeCheck(void) { //--- Get the total number of indicators in the window int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num); //--- In the loop by all window indicators, for(int i=0;i<total;i++) { //--- get the indicator name and get its handle by a name string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i); int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name); //--- If the indicator with such a name is present in the object indicator list, move on to the next one if(this.IsPresentInList(name)) continue; //--- Get the indicator object present in the list but not present in the window CWndInd *ind=this.GetMissingInd(); if(ind==NULL) continue; //--- If the indicator and the detected object have the same index, this is the indicator with changed parameters if(ind.Index()==i) { //--- Create a new indicator object based on the detected indicator object, CWndInd *changed=new CWndInd(ind.Handle(),ind.Name(),ind.Index(),ind.WindowNum()); if(changed==NULL) continue; //--- set the sorted list flag to the list of changed indicators this.m_list_ind_param.Sort(); //--- If failed to add a newly created indicator object to the list of changed indicators, //--- remove the created object and move on to the next indicator in the window if(!this.m_list_ind_param.Add(changed)) { delete changed; continue; } //--- Set the new parameters for the detected "lost" indicator - short name and handle ind.SetName(name); ind.SetHandle(handle); //--- and call the method of sending a custom event to the control program chart this.SendEvent(CHART_OBJ_EVENT_CHART_WND_IND_CHANGE); } } } //+------------------------------------------------------------------+
Die gesamte Methodenlogik ist in den Codezeilen vollständig beschrieben. Indikatoren im Chart-Fenster werden durch ihre Kurznamen identifiziert. Wenn ein Indikator einen geänderten Parameter aufweist, sollte sein Kurzname geändert werden (dies ist bei korrekt erstellten benutzerdefinierten Indikatoren der Fall, bei Standardindikatoren wird diese Eigenschaft berücksichtigt). Daher basiert die Suche nach einem geänderten Indikator hier auf der Tatsache, dass der Index des geänderten Indikators im Fenster gleich bleibt, im Gegensatz zu seinem Kurznamen. Wenn wir also einen Indikator entdecken, der in der Objektliste des Fensters vorhanden ist, aber nicht im Chart-Fenster des Client-Terminals, müssen wir prüfen, ob die Indizes übereinstimmen — wenn der Indikator und das entdeckte Objekt den gleichen Index haben (beim Löschen des Indikators werden die Indizes der anderen Indikatoren im Fenster geändert), dann ist dies der gesuchte Indikator mit geänderten Parametern.
Da die aus dem Fenster entfernten Indikatoren in einer speziellen Liste gespeichert werden sollen (für ihre spätere Suche bei der Behandlung von Ereignissen), müssen wir die Methode zum Entfernen von nicht im Fenster vorhandenen Indikatoren aus der Liste verbessern:
//+------------------------------------------------------------------+ //| Remove indicators not present in the window from the list | //+------------------------------------------------------------------+ void CChartWnd::IndicatorsDelete(void) { //--- In the loop by the list of window indicator objects, int total=this.m_list_ind.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- get the next indicator object CWndInd *ind=this.m_list_ind.At(i); if(ind==NULL) continue; //--- If such an indicator is present in the chart window, move on to the next object in the list if(this.IsPresentInWindow(ind)) continue; //--- Create a copy of a removed indicator CWndInd *ind_del=new CWndInd(ind.Handle(),ind.Name(),ind.Index(),ind.WindowNum()); if(ind_del==NULL) continue; //--- If failed to place a created object to the list of indicators removed from the window, //--- remove it and go to the next object in the list if(!this.m_list_ind_del.Add(ind_del)) { delete ind_del; continue; } //--- Remove the indicator, which was deleted from the window, from the list this.m_list_ind.Delete(i); } } //+------------------------------------------------------------------+
Die Logik der Methode ist im Code detailliert beschrieben. Ich denke, sie bedarf keiner besonderen Erklärungen. Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren unten stellen.
Die Methode gibt das Flag des Vorhandenseins eines Indikators aus dem Fenster in der Liste zurück:
//+-----------------------------------------------------------------------------+ //| Return the flag of the presence of an indicator from the window in the list | //+-----------------------------------------------------------------------------+ bool CChartWnd::IsPresentInList(const string name) { CWndInd *ind=new CWndInd(); if(ind==NULL) return false; ind.SetName(name); this.m_list_ind.Sort(SORT_BY_CHART_WINDOW_IND_NAME); int index=this.m_list_ind.Search(ind); delete ind; return(index>WRONG_VALUE); } //+------------------------------------------------------------------+
Die Methode erlaubt es, über einen Kurznamen das Vorhandensein eines entsprechenden Indikatorobjekts in der Fensterobjektliste zu definieren.
Hier erzeugen wir ein temporäres Indikatorobjekt und vergeben einen Kurznamen, der an die Methode übergeben wird. Wir setzen das Flag zum Sortieren nach Indikatornamen in der Liste der Indikatorobjekte und holen den Indikatorindex mit einem solchen Namen in der Liste. Vergessen wir nicht, das temporäre Objekt zu löschen und das Flag zurückzugeben, das anzeigt, dass der Index eines gefundenen Indikators in der Liste größer als -1 ist (wenn der Indikator mit einem solchen Namen gefunden wird, ist sein Index größer als -1).
Die Methode gibt ein Indikatorobjekt zurück, das in der Liste, aber nicht im Chart-Fenster vorhanden ist:
//+------------------------------------------------------------------+ //| Return the indicator object present in the list | //| but not on the chart | //+------------------------------------------------------------------+ CWndInd *CChartWnd::GetMissingInd(void) { for(int i=0;i<this.m_list_ind.Total();i++) { CWndInd *ind=this.m_list_ind.At(i); if(!this.IsPresentInWindow(ind)) return ind; } return NULL; } //+------------------------------------------------------------------+
Hier, in der Schleife durch alle Indikator-Objekte in der Liste, erhalten wir das nächste Indikator-Objekt. Wenn es keinen solchen Indikator im Chart gibt, gib den Zeiger auf das erkannte Indikatorobjekt zurück. Andernfalls wird NULL zurückgegeben.
Die Methode gibt das Indikatorobjekt aus der Objektliste anhand des Indikatorindex in der Liste des Chart-Fensters zurück:
//+------------------------------------------------------------------+ //| Return the indicator object by the index in the window list | //+------------------------------------------------------------------+ CWndInd *CChartWnd::GetIndicatorByIndex(const int index) { CWndInd *ind=new CWndInd(); if(ind==NULL) return NULL; ind.SetIndex(index); this.m_list_ind.Sort(SORT_BY_CHART_WINDOW_IND_INDEX); int n=this.m_list_ind.Search(ind); delete ind; return this.m_list_ind.At(n); } //+------------------------------------------------------------------+
Hier erzeugen wir ein temporäres Indikatorobjekt und zuweisen den Index, der an die Methode übergeben wird. Wir setzen das Flag für die Sortierung nach Indikatorindex in einem Chart auf die Liste der Indikatorobjekte und holen den Indikatorindex in der Fensterobjektliste mit einem solchen Index in der Chart-Fensterliste. Wir müssen aber das temporäre Objekt löschen und den Zeiger auf das Objekt mit einem bestimmten Index in der Liste der Indikatorobjekte zurückgeben.
Wenn das Objekt nicht gefunden wird, gibt die Methode Search() -1 zurück, während bei einem solchen Indexwert NULL zurückgibt. Die Methode gibt also entweder einen Zeiger auf das gefundene Objekt in der Liste zurück, oder NULL, wenn das Indikatorobjekt mit dem angegebenen Index im Chart-Fenster nicht in der Liste vorhanden ist.
Die Methode gibt das Fensterindikatorobjekt aus der Liste per Handle zurück:
//+------------------------------------------------------------------+ //| Return the window indicator object from the list by handle | //+------------------------------------------------------------------+ CWndInd *CChartWnd::GetIndicatorByHandle(const int handle) { CWndInd *ind=new CWndInd(); if(ind==NULL) return NULL; ind.SetHandle(handle); this.m_list_ind.Sort(SORT_BY_CHART_WINDOW_IND_HANDLE); int index=this.m_list_ind.Search(ind); delete ind; return this.m_list_ind.At(index); } //+------------------------------------------------------------------+
Die Methode ist identisch mit der oben betrachteten, nur dass sie den Wert des Handles des benötigten Indikatorobjekts erhält. So wird die Liste nach der Eigenschaft "Indikator-Handle" sortiert, so dass die Suche in der Liste nach dieser Objekteigenschaft durchgeführt wird.
Verbessern wir die Methode zur Aktualisierung der Daten durch die an das Fenster angehängten Indikatoren für das Senden von benutzerdefinierten Ereignissen an den Chart des Steuerungsprogramms im Falle von Änderungen der Anzahl ihrer Parameter oder der Parameter selbst:
//+------------------------------------------------------------------+ //| Update data on attached indicators | //+------------------------------------------------------------------+ void CChartWnd::Refresh(void) { //--- Calculate the change of the indicator number in the "now and during the previous check" window int change=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num)-this.m_list_ind.Total(); //--- If there is no change in the number of indicators in the window, if(change==0) { //--- check the change of parameters of all indicators and exit this.IndicatorsChangeCheck(); return; } //--- If indicators are added if(change>0) { //--- Call the method of adding new indicators to the list this.IndicatorsAdd(); //--- In the loop by the number of indicators added to the window, for(int i=0;i<change;i++) { //--- get the new indicator in the list by the index calculated from the end of the list int index=this.m_list_ind.Total()-(1+i); //--- and if failed to obtain the object, move on to the next one CWndInd *ind=this.m_list_ind.At(index); if(ind==NULL) continue; //--- call the method of sending an event to the control program chart this.SendEvent(CHART_OBJ_EVENT_CHART_WND_IND_ADD); } } //--- If there are removed indicators if(change<0) { //--- Call the method of removing unnecessary indicators from the list this.IndicatorsDelete(); //--- In the loop by the number of indicators removed from the window, for(int i=0;i<-change;i++) { //--- get a new removed indicator in the list of removed indicators by index calculated from the end of the list int index=this.m_list_ind_del.Total()-(1+i); //--- and if failed to obtain the object, move on to the next one CWndInd *ind=this.m_list_ind_del.At(index); if(ind==NULL) continue; //--- call the method of sending an event to the control program chart this.SendEvent(CHART_OBJ_EVENT_CHART_WND_IND_DEL); } } } //+------------------------------------------------------------------+
Die Logik der Methode ist innerhalb des Codes beschrieben. Ich sollte anmerken, dass die Schleifen durch neu hinzugefügte Indikatorobjekte in der Indikatorliste (oder entfernte Indikatoren) in der aktuellen Implementierung der Ereignisbehandlung nicht benötigt werden. Jetzt ist es möglich, sofort die Methode zum Senden von Ereignissen aufzurufen. Aber die Schleife für die Suche nach Indikatorobjekten, die neu zu den Listen hinzugefügt wurden, kann notwendig sein, falls wir die Klasse für eine korrekte Verfolgung von Änderungen mehrerer Indikatoren auf einmal innerhalb eines einzelnen Timer-Ticks neu anordnen müssen. Es ist viel einfacher, das programmatisch zu implementieren, als manuell. Derzeit implementiere ich die manuelle Handhabung von Änderungen im Chart. Daher benötige ich diese Schleifen vorerst nicht, obwohl sie sich in der zukünftigen Entwicklung als nützlich erweisen könnten.
Die Methode, die ein Chart-Fenster-Ereignis erzeugt und an das Steuerprogramm Chart sendet:
//+------------------------------------------------------------------+ //| Create and send a chart window event | //| to the control program chart | //+------------------------------------------------------------------+ void CChartWnd::SendEvent(ENUM_CHART_OBJ_EVENT event) { //--- If an indicator is added if(event==CHART_OBJ_EVENT_CHART_WND_IND_ADD) { //--- Get the last indicator object added to the list CWndInd *ind=this.GetLastAddedIndicator(); if(ind==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_WND_IND_ADD event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart window index to dparam, //--- pass the short name of the added indicator to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name()); } //--- If the indicator is removed else if(event==CHART_OBJ_EVENT_CHART_WND_IND_DEL) { //--- Get the last indicator object added to the list of removed indicators CWndInd *ind=this.GetLastDeletedIndicator(); if(ind==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_WND_IND_DEL event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart window index to dparam, //--- pass the short name of a deleted indicator to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name()); } //--- If the indicator has changed else if(event==CHART_OBJ_EVENT_CHART_WND_IND_CHANGE) { //--- Get the last indicator object added to the list of changed indicators CWndInd *ind=this.GetLastChangedIndicator(); if(ind==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_WND_IND_CHANGE event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart window index to dparam, //--- pass the short name of a changed indicator to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name()); } } //+------------------------------------------------------------------+
Die gesamte Methodenlogik ist in den Codezeilen vollständig beschrieben. Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren unten stellen.
Verbessern wir nun noch die Chart-Objektklasse in \MQL5\Include\DoEasy\Objects\Chart\ChartObj.mqh.
Ähnlich wie bei der Klasse des Chartfenster-Objekts leiten wir diese Klasse von der Klasse des erweiterten Objekts aller Bibliotheksobjekte ab, während wir im privaten Abschnitt der Klasse die Zeiger auf entfernte Chartfenster, entfernte und geänderte Indikatoren sowie die Methode zum Neuerstellen von Chartfenstern deklarieren:
//+------------------------------------------------------------------+ //| Chart object class | //+------------------------------------------------------------------+ class CChartObj : public CBaseObjExt { private: CArrayObj m_list_wnd; // List of chart window objects CArrayObj *m_list_wnd_del; // Pointer to the list of chart window objects CArrayObj *m_list_ind_del; // Pointer to the list of indicators removed from the indicator window CArrayObj *m_list_ind_param; // Pointer to the list of changed indicators long m_long_prop[CHART_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[CHART_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[CHART_PROP_STRING_TOTAL]; // String properties int m_digits; // Symbol's Digits() datetime m_wnd_time_x; // Time for X coordinate on the windowed chart double m_wnd_price_y; // Price for Y coordinate on the windowed chart //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_CHART_PROP_DOUBLE property) const { return(int)property-CHART_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_CHART_PROP_STRING property) const { return(int)property-CHART_PROP_INTEGER_TOTAL-CHART_PROP_DOUBLE_TOTAL; } //--- The methods of setting parameter flags bool SetShowFlag(const string source,const bool flag,const bool redraw=false); bool SetBringToTopFlag(const string source,const bool flag,const bool redraw=false); bool SetContextMenuFlag(const string source,const bool flag,const bool redraw=false); bool SetCrosshairToolFlag(const string source,const bool flag,const bool redraw=false); bool SetMouseScrollFlag(const string source,const bool flag,const bool redraw=false); bool SetEventMouseWhellFlag(const string source,const bool flag,const bool redraw=false); bool SetEventMouseMoveFlag(const string source,const bool flag,const bool redraw=false); bool SetEventObjectCreateFlag(const string source,const bool flag,const bool redraw=false); bool SetEventObjectDeleteFlag(const string source,const bool flag,const bool redraw=false); bool SetForegroundFlag(const string source,const bool flag,const bool redraw=false); bool SetShiftFlag(const string source,const bool flag,const bool redraw=false); bool SetAutoscrollFlag(const string source,const bool flag,const bool redraw=false); bool SetKeyboardControlFlag(const string source,const bool flag,const bool redraw=false); bool SetQuickNavigationFlag(const string source,const bool flag,const bool redraw=false); bool SetScaleFixFlag(const string source,const bool flag,const bool redraw=false); bool SetScaleFix11Flag(const string source,const bool flag,const bool redraw=false); bool SetScalePTPerBarFlag(const string source,const bool flag,const bool redraw=false); bool SetShowTickerFlag(const string source,const bool flag,const bool redraw=false); bool SetShowOHLCFlag(const string source,const bool flag,const bool redraw=false); bool SetShowBidLineFlag(const string source,const bool flag,const bool redraw=false); bool SetShowAskLineFlag(const string source,const bool flag,const bool redraw=false); bool SetShowLastLineFlag(const string source,const bool flag,const bool redraw=false); bool SetShowPeriodSeparatorsFlag(const string source,const bool flag,const bool redraw=false); bool SetShowGridFlag(const string source,const bool flag,const bool redraw=false); bool SetShowObjectDescriptionsFlag(const string source,const bool flag,const bool redraw=false); bool SetShowTradeLevelsFlag(const string source,const bool flag,const bool redraw=false); bool SetDragTradeLevelsFlag(const string source,const bool flag,const bool redraw=false); bool SetShowDateScaleFlag(const string source,const bool flag,const bool redraw=false); bool SetShowPriceScaleFlag(const string source,const bool flag,const bool redraw=false); bool SetShowOneClickPanelFlag(const string source,const bool flag,const bool redraw=false); bool SetDockedFlag(const string source,const bool flag,const bool redraw=false); //--- The methods of setting property values bool SetMode(const string source,const ENUM_CHART_MODE mode,const bool redraw=false); bool SetScale(const string source,const int scale,const bool redraw=false); bool SetModeVolume(const string source,const ENUM_CHART_VOLUME_MODE mode,const bool redraw=false); void SetVisibleBars(void); void SetWindowsTotal(void); void SetFirstVisibleBars(void); void SetWidthInBars(void); void SetWidthInPixels(void); void SetMaximizedFlag(void); void SetMinimizedFlag(void); void SetExpertName(void); void SetScriptName(void); //--- (1) Create, (2) check and re-create the chart window list void CreateWindowsList(void); void RecreateWindowsList(const int change); //--- Add an extension to the screenshot file if it is missing string FileNameWithExtention(const string filename); public:
Im öffentlichen Bereich der Klasse werden mit write und declare neue Methoden für die Arbeit mit Klassenereignissen definiert.
Übergeben wir die Zeiger auf neue Listen an den parametrischen Konstruktor:
public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_CHART_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_CHART_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_CHART_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_CHART_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_CHART_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_CHART_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return (1) itself, (2) the window object list and (3) the list of removed window objects CChartObj *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list_wnd; } //--- Return the last (1) added (removed) chart window CChartWnd *GetLastAddedWindow(void) { return this.m_list_wnd.At(this.m_list_wnd.Total()-1); } CChartWnd *GetLastDeletedWindow(void) { return this.m_list_wnd_del.At(this.m_list_wnd_del.Total()-1); } //--- Return (1) the last one added to the window, (2) the last one removed from the window and (3) the changed indicator, CWndInd *GetLastAddedIndicator(const int win_num); CWndInd *GetLastDeletedIndicator(void) { return this.m_list_ind_del.At(this.m_list_ind_del.Total()-1); } CWndInd *GetLastChangedIndicator(void) { return this.m_list_ind_param.At(this.m_list_ind_param.Total()-1);} //--- Return the indicator by index from the specified chart window CWndInd *GetIndicator(const int win_num,const int ind_index); //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_CHART_PROP_INTEGER property) { return (property!=CHART_PROP_WINDOW_YDISTANCE ? true : false); } virtual bool SupportProperty(ENUM_CHART_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CHART_PROP_STRING property) { return true; } //--- Get description of (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_CHART_PROP_INTEGER property); string GetPropertyDescription(ENUM_CHART_PROP_DOUBLE property); string GetPropertyDescription(ENUM_CHART_PROP_STRING property); //--- Display the description of object properties in the journal (full_prop=true - all properties, false - supported ones only) void Print(const bool full_prop=false); //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false); //--- Return the object short name virtual string Header(void); //--- Create and send the chart event to the control program chart void SendEvent(ENUM_CHART_OBJ_EVENT event); //--- Compare CChartObj objects by a specified property (to sort the list by a specified chart object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CChartObj objects by all properties (to search for equal chart objects) bool IsEqual(CChartObj* compared_obj) const; //--- Update the chart object and its list of indicator windows void Refresh(void); //--- Constructors CChartObj(){;} CChartObj(const long chart_id,CArrayObj *list_wnd_del,CArrayObj *list_ind_del,CArrayObj *list_ind_param); //+------------------------------------------------------------------+
Betrachten wir die neuen und verbesserten Klassenmethoden.
Im parametrischen Konstruktor, lassen wir die Variablen, die die Zeiger auf Listenobjekte speichern, die Zeiger auf sie erhalten, die an die Methode übergeben werden:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CChartObj::CChartObj(const long chart_id,CArrayObj *list_wnd_del,CArrayObj *list_ind_del,CArrayObj *list_ind_param) : m_wnd_time_x(0),m_wnd_price_y(0) { this.m_list_wnd_del=list_wnd_del; this.m_list_ind_del=list_ind_del; this.m_list_ind_param=list_ind_param; //--- Set chart ID to the base object
At the end of the constructor listing, set the flag of sorting by chart window index to the list of removed chart object windows:
this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); this.m_list_wnd_del.Sort(SORT_BY_CHART_WINDOW_NUM); this.CreateWindowsList(); } //+------------------------------------------------------------------+
Die Methode, die den zuletzt zum Fenster hinzugefügten Indikator zurückgibt:
//+------------------------------------------------------------------+ //| Return the last indicator added to the window | //+------------------------------------------------------------------+ CWndInd *CChartObj::GetLastAddedIndicator(const int win_num) { CChartWnd *wnd=this.GetWindowByNum(win_num); return(wnd!=NULL ? wnd.GetLastAddedIndicator() : NULL); } //+------------------------------------------------------------------+
Die Methode erhält den Fensterindex des Charts, aus dem wir den zuletzt hinzugefügten Indikator holen wollen. Verwenden wir die Methode GetWindowByNum() um das gewünschte Chart-Fenster zu erhalten. Im Gegenzug holen wir uns den zuletzt hinzugefügten Indikator aus diesem Chart-Fenster. Wenn es nicht gelingt, das Chart-Fenster-Objekt zu erhalten, wird NULL zurückgegeben. Vergessen wir nicht, dass die Methode GetLastAddedIndicator() Chartfenster-Objekt auch NULL zurückgeben kann.
Die Methode gibt den Indikator nach Index aus dem angegebenen Chart-Fenster zurück:
//+------------------------------------------------------------------+ //| Return the indicator by index from the specified chart window | //+------------------------------------------------------------------+ CWndInd *CChartObj::GetIndicator(const int win_num,const int ind_index) { CChartWnd *wnd=this.GetWindowByNum(win_num); return(wnd!=NULL ? wnd.GetIndicatorByIndex(ind_index) : NULL); } //+------------------------------------------------------------------+
Die Methode erhält den Index des Chart-Fensters, aus dem wir den zuletzt hinzugefügten Indikator holen wollen, und den Index des Indikators in der Fensterliste.
Verwenden wir die Methode GetWindowByNum() um das gewünschte Chart-Fenster zu erhalten und um den Indikator über seinen Index im Chart-Fenster zu erhalten. Wenn es nicht gelingt, das Chart-Fenster-Objekt zu erhalten, geben wir NULL zurück. Beachten Sie, dass die Methode GetIndicatorByIndex() Chartfenster-Objekt auch NULL zurückgeben kann.
In der Methode, die das Chart-Objekt und die Liste seiner Fenster aktualisiert, ersetzen wir die Methode CreateWindowsList() durch die neue Methode RecreateWindowsList():
//+------------------------------------------------------------------+ //| Update the chart object and its window list | //+------------------------------------------------------------------+ void CChartObj::Refresh(void) { for(int i=0;i<this.m_list_wnd.Total();i++) { CChartWnd *wnd=this.m_list_wnd.At(i); if(wnd==NULL) continue; wnd.Refresh(); } int change=(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOWS_TOTAL)-this.WindowsTotal(); if(change==0) return; this.RecreateWindowsList(change); } //+------------------------------------------------------------------+
Die Methode zum Erstellen der Liste der Chart-Fenster CreateWindowsList() ist nur für den Aufbau von Fenstern beim Start des Programms zu verwenden. Die Methode zum Neuaufbau von geänderten Fenstern (weiter unten betrachtet) ist beim Aktualisieren des Chart-Objekts zu verwenden.
Wenn wir nun ein neues Chart-Fensterobjekt erzeugen, müssen wir ihm den Namen des Chart-Symbols und die Zeiger auf die Listen übergeben. Also, lassen wir uns die Übergabe an den Konstruktor der Chart-Fenster-Objekt-Klasse implementieren, wenn wir ein neues Chart-Fenster-Objekt erstellen in der Methode zur Erstellung der Chart-Fenster-Liste:
//+------------------------------------------------------------------+ //| Create the list of chart windows | //+------------------------------------------------------------------+ void CChartObj::CreateWindowsList(void) { //--- Clear the chart window list this.m_list_wnd.Clear(); //--- Get the total number of chart windows from the environment int total=(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOWS_TOTAL); //--- In the loop by the total number of windows for(int i=0;i<total;i++) { //--- Create a new chart window object CChartWnd *wnd=new CChartWnd(this.m_chart_id,i,this.Symbol(),this.m_list_ind_del,this.m_list_ind_param); if(wnd==NULL) continue; //--- If the window index exceeds 0 (not the main chart window) and it still has no indicator, //--- remove the newly created chart window object and go to the next loop iteration if(wnd.WindowNum()!=0 && wnd.IndicatorsTotal()==0) { delete wnd; continue; } //--- If the object was not added to the list, remove that object this.m_list_wnd.Sort(); if(!this.m_list_wnd.Add(wnd)) delete wnd; } //--- If the number of objects in the list corresponds to the number of windows on the chart, //--- write that value to the chart object property //--- If the number of objects in the list does not correspond to the number of windows on the chart, //--- write the number of objects in the list to the chart object property. int value=int(this.m_list_wnd.Total()==total ? total : this.m_list_wnd.Total()); this.SetProperty(CHART_PROP_WINDOWS_TOTAL,value); } //+------------------------------------------------------------------+
Die Methode zur Überprüfung der Änderungen von Chart-Fenstern und zur Neuerstellung ihrer Liste:
//+------------------------------------------------------------------+ //| Check and re-create the chart window list | //+------------------------------------------------------------------+ void CChartObj::RecreateWindowsList(const int change) { //--- If the window is removed if(change<0) { //--- If the chart has only one window, this means we have only the main chart with no subwindows, //--- while the change in the number of chart windows indicates the removal of a symbol chart in the terminal. //--- This situation is handled in the collection class of chart objects - leave the method if(this.WindowsTotal()==1) return; //--- Get the last removed indicator from the list of removed indicators CWndInd *ind=this.m_list_ind_del.At(this.m_list_ind_del.Total()-1); //--- If managed to get the indicator, if(ind!=NULL) { //--- create a new chart window object CChartWnd *wnd=new CChartWnd(); if(wnd!=NULL) { //--- Set the subwindow index from the last removed indicator object, //--- ID and the chart object symbol name for a new object wnd.SetWindowNum(ind.WindowNum()); wnd.SetChartID(this.ID()); wnd.SetSymbol(this.Symbol()); //--- If failed to add the created object to the list of removed chart window objects, remove it if(!this.m_list_wnd_del.Add(wnd)) delete wnd; } } //--- Call the method of sending an event to the control program chart and re-create the chart window list this.SendEvent(CHART_OBJ_EVENT_CHART_WND_DEL); this.CreateWindowsList(); return; } //--- If there are no changes, leave else if(change==0) return; //--- If a window is added //--- Get the total number of chart windows from the environment int total=(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOWS_TOTAL); //--- In the loop by the total number of windows for(int i=0;i<total;i++) { //--- Create a new chart window object CChartWnd *wnd=new CChartWnd(this.m_chart_id,i,this.Symbol(),this.m_list_ind_del,this.m_list_ind_param); if(wnd==NULL) continue; this.m_list_wnd.Sort(SORT_BY_CHART_WINDOW_NUM); //--- If the window index exceeds 0 (not the main chart window) and it still has no indicator, //--- or such a window is already present in the list or the window object is not added to the list //--- remove the newly created chart window object and go to the next loop iteration if((wnd.WindowNum()!=0 && wnd.IndicatorsTotal()==0) || this.m_list_wnd.Search(wnd)>WRONG_VALUE || !this.m_list_wnd.Add(wnd)) { delete wnd; continue; } //--- If added the window, call the method of sending an event to the control program chart this.SendEvent(CHART_OBJ_EVENT_CHART_WND_ADD); } //--- If the number of objects in the list corresponds to the number of windows on the chart, //--- write that value to the chart object property //--- If the number of objects in the list does not correspond to the number of windows on the chart, //--- write the number of objects in the list to the chart object property. int value=int(this.m_list_wnd.Total()==total ? total : this.m_list_wnd.Total()); this.SetProperty(CHART_PROP_WINDOWS_TOTAL,value); } //+------------------------------------------------------------------+
Die Methode erzeugt und sendet ein Chart-Ereignis an die Karte des Steuerprogramms:
//+------------------------------------------------------------------+ //| Create and send a chart event | //| to the control program chart | //+------------------------------------------------------------------+ void CChartObj::SendEvent(ENUM_CHART_OBJ_EVENT event) { //--- If a window is added if(event==CHART_OBJ_EVENT_CHART_WND_ADD) { //--- Get the last chart window object added to the list CChartWnd *wnd=this.GetLastAddedWindow(); if(wnd==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_WND_ADD event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart window index to dparam, //--- pass the chart symbol to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol()); } //--- If the window is removed else if(event==CHART_OBJ_EVENT_CHART_WND_DEL) { //--- Get the last chart window object added to the list of removed windows CChartWnd *wnd=this.GetLastDeletedWindow(); if(wnd==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_WND_DEL event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart window index to dparam, //--- pass the chart symbol to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol()); } } //+------------------------------------------------------------------+
Die gesamte Logik der letzten beiden Methoden ist vollständig in den entsprechenden Zeilen beschrieben und erfordert keine weiteren Erklärungen.
Wenn Sie Fragen zu den Methoden haben, können Sie diese gerne im Kommentarteil stellen.
Verbessern wir die Klasse Chartobjekte Kollektion in \MQL5\Include\DoEasy\Collections\ChartObjCollection.mqh.
Im privaten Abschnitt der Klasse deklarieren wir die Listenobjekte, deren Zeiger in den Chart-Fenstern an die Chart-Fenster- und Indikatorobjekte übergeben wurden:
//+------------------------------------------------------------------+ //| MQL5 signal object collection | //+------------------------------------------------------------------+ class CChartObjCollection : public CBaseObj { private: CListObj m_list; // List of chart objects CListObj m_list_del; // List of deleted chart objects CArrayObj m_list_wnd_del; // List of deleted chart window objects CArrayObj m_list_ind_del; // List of indicators removed from the indicator window CArrayObj m_list_ind_param; // List of changed indicators int m_charts_total_prev; // Previous number of charts in the terminal //--- Return the number of charts in the terminal int ChartsTotal(void) const; //--- Return the flag indicating the existence of (1) a chart object and (2) a chart bool IsPresentChartObj(const long chart_id); bool IsPresentChart(const long chart_id); //--- Create a new chart object and add it to the list bool CreateNewChartObj(const long chart_id,const string source); //--- Find the missing chart object, create it and add it to the collection list bool FindAndCreateMissingChartObj(void); //--- Find a chart object not present in the terminal and remove it from the list void FindAndDeleteExcessChartObj(void); public:
Anstelle der Zeiger auf die Listen deklarieren wir hier die CArrayObj-Objekte, in denen wir alle gelöschten Chart-Objekte speichern werden.
Im öffentlichen Teil der Klasse, deklarieren wir neue Methoden, die für die Arbeit mit Chartobjekten benötigt werden:
public: //--- Return (1) itself, (2) chart object collection list, (3) the list of deleted chart objects, //--- the list (4) of deleted window objects, (5) deleted and (6) changed indicators CChartObjCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } CArrayObj *GetListDeletedCharts(void) { return &this.m_list_del; } CArrayObj *GetListDeletedWindows(void) { return &this.m_list_wnd_del; } CArrayObj *GetListDeletedIndicators(void) { return &this.m_list_ind_del; } CArrayObj *GetListChangedIndicators(void) { return &this.m_list_ind_param; } //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_CHART_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CHART_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CHART_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode); } //--- Return the number of chart objects in the list int DataTotal(void) const { return this.m_list.Total(); } //--- Display (1) the complete and (2) short collection description in the journal void Print(void); void PrintShort(void); //--- Constructor CChartObjCollection(); //--- Return the list of chart objects by (1) symbol and (2) timeframe CArrayObj *GetChartsList(const string symbol) { return this.GetList(CHART_PROP_SYMBOL,symbol,EQUAL); } CArrayObj *GetChartsList(const ENUM_TIMEFRAMES timeframe) { return this.GetList(CHART_PROP_TIMEFRAME,timeframe,EQUAL);} //--- Return the pointer to the chart object (1) by ID and (2) by an index in the list CChartObj *GetChart(const long id); CChartObj *GetChart(const int index) { return this.m_list.At(index); } //--- Return (1) the last added chart and (2) the last removed chart CChartObj *GetLastAddedChart(void) { return this.m_list.At(this.m_list.Total()-1); } CChartObj *GetLastDeletedChart(void) { return this.m_list_del.At(this.m_list_del.Total()-1); } //--- Return (1) the last added window on the chart by chart ID and (2) the last removed chart window CChartWnd *GetLastAddedChartWindow(const long chart_id); CChartWnd *GetLastDeletedChartWindow(void) { return this.m_list_wnd_del.At(this.m_list_wnd_del.Total()-1);} //--- Return (1) the last one added to the specified window of the specified chart, (2) the last one removed from the window and (3) the changed indicator CWndInd *GetLastAddedIndicator(const long chart_id,const int win_num); CWndInd *GetLastDeletedIndicator(void) { return this.m_list_ind_del.At(this.m_list_ind_del.Total()-1); } CWndInd *GetLastChangedIndicator(void) { return this.m_list_ind_param.At(this.m_list_ind_param.Total()-1);} //--- Return the indicator by index from the specified window of the specified chart CWndInd *GetIndicator(const long chart_id,const int win_num,const int ind_index); //--- Return the chart ID with the program long GetMainChartID(void) const { return CBaseObj::GetMainChartID(); } //--- Create the collection list of chart objects bool CreateCollection(void); //--- Update (1) the chart object collection list and (2) the specified chart object void Refresh(void); void Refresh(const long chart_id); //--- (1) Open a new chart with the specified symbol and period, (2) close the specified chart bool Open(const string symbol,const ENUM_TIMEFRAMES timeframe); bool Close(const long chart_id); //--- Create and send the chart event to the control program chart void SendEvent(ENUM_CHART_OBJ_EVENT event); }; //+------------------------------------------------------------------+
Betrachten wir die Implementierung der neuen Methoden und die Verbesserung der vorhandenen Methoden.
Im Klassenkonstruktor, löschen wir die neuen Listen und setzen die sortierten Listenflags entsprechend:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CChartObjCollection::CChartObjCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list_ind_del.Clear(); this.m_list_ind_del.Sort(); this.m_list_ind_param.Clear(); this.m_list_ind_param.Sort(); this.m_list_wnd_del.Clear(); this.m_list_wnd_del.Sort(); this.m_list_del.Clear(); this.m_list_del.Sort(); this.m_list.Type(COLLECTION_CHARTS_ID); this.m_charts_total_prev=this.ChartsTotal(); } //+------------------------------------------------------------------+
In der Methode, die die Liste der Chartobjekte der Kollektion deklariert, fügen wir den Aufruf der Methode zum Senden von Ereignissen an das Steuerprogramm Chart hinzu:
//+------------------------------------------------------------------+ //| Update the collection list of chart objects | //+------------------------------------------------------------------+ void CChartObjCollection::Refresh(void) { //--- In the loop by the number of chart objects in the list, for(int i=0;i<this.m_list.Total();i++) { //--- get the next chart object and CChartObj *chart=this.m_list.At(i); if(chart==NULL) continue; //--- update it chart.Refresh(); } //--- Get the number of open charts in the terminal and int charts_total=this.ChartsTotal(); //--- calculate the difference between the number of open charts in the terminal //--- and chart objects in the collection list int change=charts_total-this.m_list.Total(); //--- If there are no changes, leave if(change==0) return; //--- If a chart is added in the terminal if(change>0) { //--- Find the missing chart object, create and add it to the collection list this.FindAndCreateMissingChartObj(); //--- Get the current chart and return to it since //--- adding a new chart switches the focus to it CChartObj *chart=this.GetChart(GetMainChartID()); if(chart!=NULL) chart.SetBringToTopON(true); for(int i=0;i<change;i++) { chart=m_list.At(m_list.Total()-(1+i)); if(chart==NULL) continue; this.SendEvent(CHART_OBJ_EVENT_CHART_OPEN); } } //--- If a chart is removed in the terminal else if(change<0) { //--- Find an extra chart object in the collection list and remove it from the list this.FindAndDeleteExcessChartObj(); for(int i=0;i<-change;i++) { CChartObj *chart=this.m_list_del.At(this.m_list_del.Total()-(1+i)); if(chart==NULL) continue; this.SendEvent(CHART_OBJ_EVENT_CHART_CLOSE); } } } //+------------------------------------------------------------------+
Die Codeblöcke, in denen die Methode SendEvent() aufgerufen wird, sind identisch mit denen, die zuvor für die Objektklasse des Chart-Fensters betrachtet wurden, und werden auch mit Blick auf ihre mögliche zukünftige Verbesserung implementiert. Die Schleifen sind nicht erforderlich, um manuelle Änderungen in Terminal-Charts zu behandeln — wir können die Methode zum Senden von Ereignissen sofort aufrufen.
In der Methode, die ein neues Chart-Objekt erzeugt und zur Liste hinzufügt, ist es jetzt möglich, die Zeiger auf neue Listen beim Erzeugen eines neuen Chart-Objekts zu übergeben. Fügen wir diese Änderungen hinzu:
//+------------------------------------------------------------------+ //| Create a new chart object and add it to the list | //+------------------------------------------------------------------+ bool CChartObjCollection::CreateNewChartObj(const long chart_id,const string source) { ::ResetLastError(); CChartObj *chart_obj=new CChartObj(chart_id,this.GetListDeletedWindows(),this.GetListDeletedIndicators(),this.GetListChangedIndicators()); if(chart_obj==NULL) { CMessage::ToLog(source,MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ,true); return false; } this.m_list.Sort(SORT_BY_CHART_ID); if(!this.m_list.InsertSort(chart_obj)) { CMessage::ToLog(source,MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART,true); delete chart_obj; return false; } return true; } //+------------------------------------------------------------------+
Die Methode gibt das zuletzt zum Chart hinzugefügte Fenster nach Chart-ID zurück:
//+------------------------------------------------------------------+ //| Return the last added window to the chart by ID | //+------------------------------------------------------------------+ CChartWnd* CChartObjCollection::GetLastAddedChartWindow(const long chart_id) { CChartObj *chart=this.GetChart(chart_id); if(chart==NULL) return NULL; CArrayObj *list=chart.GetList(); return(list!=NULL ? list.At(list.Total()-1) : NULL); } //+------------------------------------------------------------------+
Die Methode erhält die ID des Charts, dessen zuletzt hinzugefügtes Fenster wir erhalten möchten.
Holen wir uns das Chart-Objekt über die an die Methode übergebene ID und wir erhalten die Liste seiner Fenster.
Das zuletzt hinzugefügte Fenster befindet sich immer am Ende der Liste — wir geben das Objekt ganz am Ende der Liste der Chart-Fenster oder NULL zurück, wenn dies nicht gelingt.
Die Methode gibt den letzten Indikator zurück, der dem angegebenen Fenster eines angegebenen Charts hinzugefügt wurde:
//+------------------------------------------------------------------+ //| Return the last indicator added | //| to the specified window of the specified chart | //+------------------------------------------------------------------+ CWndInd* CChartObjCollection::GetLastAddedIndicator(const long chart_id,const int win_num) { CChartObj *chart=this.GetChart(chart_id); return(chart!=NULL ? chart.GetLastAddedIndicator(win_num) : NULL); } //+------------------------------------------------------------------+
Die Methode erhält die ID des Charts und die Nummer des Teilfensters, dessen zuletzt hinzugefügten Indikator wir erhalten möchten.
Wir ermitteln das Chart-Objekt anhand der an die Methode übergebenen ID und verwenden die Methode GetLastAddedIndicator() des Chart-Objekts, um den Zeiger auf den letzten Indikator zurückzugeben, der dem angegebenen Chart-Fenster hinzugefügt wurde. Wenn dies fehlschlägt, wird NULL zurückgegeben.
Die Methode gibt den Indikator nach Index aus dem angegebenen Fenster des angegebenen Charts zurück:
//+------------------------------------------------------------------+ //| Return the indicator by index | //| from the specified window of the specified chart | //+------------------------------------------------------------------+ CWndInd* CChartObjCollection::GetIndicator(const long chart_id,const int win_num,const int ind_index) { CChartObj *chart=this.GetChart(chart_id); return(chart!=NULL ? chart.GetIndicator(win_num,ind_index) : NULL); } //+------------------------------------------------------------------+
Die Methode erhält die ID des Charts, den Index des Subfensters und den Index des Indikators, den wir erhalten möchten.
Wir holen uns das Chart-Objekt anhand der an die Methode übergebenen ID und verwenden die Chart-Objekt-Methode GetIndicator() um den Zeiger auf den Indikator aus dem angegebenen Chart-Fenster zurückzugeben. Wenn dies fehlschlägt, wird NULL zurückgegeben.
In der Methode, die ein nicht im Terminal vorhandenes Chart-Objekt findet und aus der Liste entfernt, fügen wir den Code-Block ein, der das erkannte Chart-Objekt in die Liste der entfernten Charts setzt:
//+-----------------------------------------------------------------------------+ //|Find a chart object not present in the terminal and remove it from the list | //+-----------------------------------------------------------------------------+ void CChartObjCollection::FindAndDeleteExcessChartObj(void) { for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--) { CChartObj *chart=this.m_list.At(i); if(chart==NULL) continue; if(!this.IsPresentChart(chart.ID())) { chart=this.m_list.Detach(i); if(chart!=NULL) { if(!this.m_list_del.Add(chart)) this.m_list.Delete(i); } } } } //+------------------------------------------------------------------+
Hier gilt: Wenn das aus der Liste erhaltene Chart-Objekt nicht im Client-Terminal vorhanden ist, verwenden wir die Methode der Standardbibliothek Detach(), um das Objekt aus der Liste zu entfernen und zur Liste der entfernten Charts hinzuzufügen. Wenn es nicht gelingt, das entfernte Objekt in die neue Liste aufzunehmen, löschen wir es, um ein Speicherleck zu vermeiden.
Die Methode erzeugt und sendet ein Chart-Ereignis an die Karte des Steuerprogramms:
//+------------------------------------------------------------------+ //| Create and send a chart event | //| to the control program chart | //+------------------------------------------------------------------+ void CChartObjCollection::SendEvent(ENUM_CHART_OBJ_EVENT event) { //--- If a chart is added if(event==CHART_OBJ_EVENT_CHART_OPEN) { //--- Get the last chart object added to the list CChartObj *chart=this.GetLastAddedChart(); if(chart==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_OPEN event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart timeframe to dparam, //--- pass the chart symbol to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol()); } //--- If a chart is removed else if(event==CHART_OBJ_EVENT_CHART_CLOSE) { //--- Get the last chart object added to the list of removed charts CChartObj *chart=this.GetLastDeletedChart(); if(chart==NULL) return; //--- Send the CHART_OBJ_EVENT_CHART_CLOSE event to the control program chart //--- pass the chart ID to lparam, //--- pass the chart timeframe to dparam, //--- pass the chart symbol to sparam ::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol()); } } //+------------------------------------------------------------------+
Die Logik der Methode ist in ihrem Code detailliert beschrieben. Es bedarf keiner weiteren Erklärungen.
Kontrollieren von Chart-Ereignissen
Ich habe die Funktionalität zum Verfolgen einiger Chart-Ereignisse erstellt. Nun müssen wir dem Hauptprogramm den Zugriff darauf ermöglichen. Um dies zu erreichen, werde ich die Hauptklasse der Bibliothek CEngine verwenden. Sie soll die neuen Methoden enthalten, die den Zugriff auf die Methoden der Chart-Objekt-Kollektion ermöglichen, die ich im aktuellen Artikel implementiert habe. Da wir bereits die im vorherigen Artikel beschriebene Funktionalität zum Aktualisieren der Liste der Chart-Objekte haben, ist alles für die Verfolgung von Ereignissen im Steuerprogramm bereit. Wir müssen nur noch die Behandlung eingehender Ereignisse (nämlich der Ereignisse, die von der Kollektionsklasse der Chart-Objekte kommen) aus der Bibliothek im Test-EA implementieren.
Die Bibliothek ist noch nicht in der Lage, Änderungen in allen Chart-Eigenschaften, ihren Fenstern und Indikatoren zu verfolgen. Da aber alle Objekte bereits Nachkommen der erweiterten Klasse des Basisobjekts aller Bibliotheksobjekte sind (die ihre Nachkommen automatisch mit Ereignisfunktionalität ausstattet), müssen wir nur noch die Methoden zur Verwaltung der Objekteigenschaften hinzufügen. Das werde ich mir für den nächsten Artikel aufheben. Verbinden wir nun die neue Funktionalität mit der "Außenwelt" und testen alles, was ich im aktuellen Artikel implementiert habe.
Fügen wir in der Datei \MQL5\Include\DoEasy\Engine.mqh der Hauptobjektklasse der Bibliothek die Methoden für den Zugriff auf die neuen Methoden der Chart-Objekt Kollektion hinzu:
//--- Return the list of chart objects by (1) symbol and (2) timeframe CArrayObj *GetListCharts(const string symbol) { return this.m_charts.GetChartsList(symbol); } CArrayObj *GetListCharts(const ENUM_TIMEFRAMES timeframe) { return this.m_charts.GetChartsList(timeframe); } //--- Return the list of removed (1) chart objects, (2) chart windows, (3) indicators in the chart window and (4) changed indicators in the chart window CArrayObj *GetListChartsClosed(void) { return this.m_charts.GetListDeletedCharts(); } CArrayObj *GetListChartWindowsDeleted(void) { return this.m_charts.GetListDeletedWindows(); } CArrayObj *GetListChartWindowsIndicatorsDeleted(void) { return this.m_charts.GetListDeletedIndicators(); } CArrayObj *GetListChartWindowsIndicatorsChanged(void) { return this.m_charts.GetListChangedIndicators(); } //--- Return (1) the specified chart object and (2) the chart object with the program CChartObj *ChartGetChartObj(const long chart_id) { return this.m_charts.GetChart(chart_id); } CChartObj *ChartGetMainChart(void) { return this.m_charts.GetChart(this.m_charts.GetMainChartID());} //--- Reutrn the chart object of the last (1) open and (2) closed chart CChartObj *ChartGetLastOpenedChart(void) { return this.m_charts.GetLastAddedChart(); } CChartObj *ChartGetLastClosedChart(void) { return this.m_charts.GetLastDeletedChart(); } //--- Return the object (1) of the last added window of the specified chart and (2) the last removed chart window CChartWnd *ChartGetLastAddedChartWindow(const long chart_id) { return this.m_charts.GetLastAddedChartWindow(chart_id);} CChartWnd *ChartGetLastDeletedChartWindow(void) { return this.m_charts.GetLastDeletedChartWindow(); } //--- Return (1) the last one added to the specified window of the specified chart, (2) the last one removed from the window and (3) the changed indicator CWndInd *ChartGetLastAddedIndicator(const long id,const int win) { return m_charts.GetLastAddedIndicator(id,win); } CWndInd *ChartGetLastDeletedIndicator(void) { return this.m_charts.GetLastDeletedIndicator(); } CWndInd *ChartGetLastChangedIndicator(void) { return this.m_charts.GetLastChangedIndicator(); } //--- Return the indicator by index from the specified window of the specified chart CWndInd *ChartGetIndicator(const long chart_id,const int win_num,const int ind_index) { return m_charts.GetIndicator(chart_id,win_num,ind_index); } //--- Return the number of charts in the collection list int ChartsTotal(void) { return this.m_charts.DataTotal(); }
Alle neu hinzugefügten Methoden geben das Ergebnis des Aufrufs der entsprechenden Methoden der Kollektion von Chart-Objekten zurück.
Test
Um den Test durchzuführen, verwende ich den EA aus dem vorigen Artikel und speichere ihn in \MQL5\Experts\TestDoEasy\Teil71\ als TestDoEasyPart71.mq5.
Alles, was wir tun müssen, ist das Hinzufügen von neuen Ereigniscodes zur Ereignisbehandlung durch OnDoEasyEvent().
Es macht keinen Sinn, den gesamten Funktionscode zu betrachten. Er ist sehr umfangreich und erfordert eine Aufteilung in separate Handler für Ereignisse verschiedener Bibliotheksobjekte. Damit werde ich mich erst viel später beschäftigen.
Betrachten wir nun den Codeblock, der der EA-Funktion OnDoEasyEvent() hinzugefügt werden soll:
//--- Handling timeseries events else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE) { //--- "New bar" event if(idx==SERIES_EVENTS_NEW_BAR) { Print(TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam)); } } //--- Handle chart events else if(idx>CHART_OBJ_EVENT_NO_EVENT && idx<CHART_OBJ_EVENTS_NEXT_CODE) { //--- "New chart opening" event if(idx==CHART_OBJ_EVENT_CHART_OPEN) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol()); CChartObj *chart=engine.ChartGetLastOpenedChart(); if(chart!=NULL) { string symbol=sparam; long chart_id=lparam; ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam; string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id; Print(DFUN,CMessage::Text(MSG_CHART_COLLECTION_CHART_OPENED),": ",header); } } //--- "Chart closure" event if(idx==CHART_OBJ_EVENT_CHART_CLOSE) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol()); CChartObj *chart=engine.ChartGetLastClosedChart(); if(chart!=NULL) { string symbol=sparam; long chart_id=lparam; ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam; string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id; Print(DFUN,CMessage::Text(MSG_CHART_COLLECTION_CHART_CLOSED),": ",header); } } //--- "Adding a new window on the chart" event if(idx==CHART_OBJ_EVENT_CHART_WND_ADD) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol()); ENUM_TIMEFRAMES timeframe=WRONG_VALUE; string ind_name=""; string symbol=sparam; long chart_id=lparam; int win_num=(int)dparam; string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id+": "; CChartObj *chart=engine.ChartGetLastOpenedChart(); if(chart!=NULL) { timeframe=chart.Timeframe(); CChartWnd *wnd=engine.ChartGetLastAddedChartWindow(chart.ID()); if(wnd!=NULL) { CWndInd *ind=wnd.GetLastAddedIndicator(); if(ind!=NULL) ind_name=ind.Name(); } } Print(DFUN,header,CMessage::Text(MSG_CHART_OBJ_WINDOW_ADDED)," ",(string)win_num," ",ind_name); } //--- "Removing a window from the chart" event if(idx==CHART_OBJ_EVENT_CHART_WND_DEL) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol()); CChartWnd *wnd=engine.ChartGetLastDeletedChartWindow(); ENUM_TIMEFRAMES timeframe=WRONG_VALUE; string symbol=sparam; long chart_id=lparam; int win_num=(int)dparam; string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id+": "; Print(DFUN,header,CMessage::Text(MSG_CHART_OBJ_WINDOW_REMOVED)," ",(string)win_num); } //--- "Adding a new indicator to the chart window" event if(idx==CHART_OBJ_EVENT_CHART_WND_IND_ADD) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name()); ENUM_TIMEFRAMES timeframe=WRONG_VALUE; string ind_name=sparam; string symbol=NULL; long chart_id=lparam; int win_num=(int)dparam; string header=NULL; CWndInd *ind=engine.ChartGetLastAddedIndicator(chart_id,win_num); if(ind!=NULL) { CChartObj *chart=engine.ChartGetChartObj(chart_id); if(chart!=NULL) { symbol=chart.Symbol(); timeframe=chart.Timeframe(); CChartWnd *wnd=chart.GetWindowByNum(win_num); if(wnd!=NULL) header=wnd.Header(); } } Print(DFUN,symbol," ",TimeframeDescription(timeframe),", ID ",chart_id,", ",header,": ",CMessage::Text(MSG_CHART_OBJ_INDICATOR_ADDED)," ",ind_name); } //--- "Removing an indicator from the chart window" event if(idx==CHART_OBJ_EVENT_CHART_WND_IND_DEL) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name()); ENUM_TIMEFRAMES timeframe=WRONG_VALUE; string ind_name=sparam; string symbol=NULL; long chart_id=lparam; int win_num=(int)dparam; string header=NULL; CWndInd *ind=engine.ChartGetLastDeletedIndicator(); if(ind!=NULL) { CChartObj *chart=engine.ChartGetChartObj(chart_id); if(chart!=NULL) { symbol=chart.Symbol(); timeframe=chart.Timeframe(); CChartWnd *wnd=chart.GetWindowByNum(win_num); if(wnd!=NULL) header=wnd.Header(); } } Print(DFUN,symbol," ",TimeframeDescription(timeframe),", ID ",chart_id,", ",header,": ",CMessage::Text(MSG_CHART_OBJ_INDICATOR_REMOVED)," ",ind_name); } //--- "Changing indicator parameters in the chart window" event if(idx==CHART_OBJ_EVENT_CHART_WND_IND_CHANGE) { //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name()); ENUM_TIMEFRAMES timeframe=WRONG_VALUE; string ind_name=sparam; string symbol=NULL; long chart_id=lparam; int win_num=(int)dparam; string header=NULL; CWndInd *ind=NULL; CWndInd *ind_changed=engine.ChartGetLastChangedIndicator(); if(ind_changed!=NULL) { ind=engine.ChartGetIndicator(chart_id,win_num,ind_changed.Index()); if(ind!=NULL) { CChartObj *chart=engine.ChartGetChartObj(chart_id); if(chart!=NULL) { symbol=chart.Symbol(); timeframe=chart.Timeframe(); CChartWnd *wnd=chart.GetWindowByNum(win_num); if(wnd!=NULL) header=wnd.Header(); } } } Print(DFUN,symbol," ",TimeframeDescription(timeframe),", ID ",chart_id,", ",header,": ",CMessage::Text(MSG_CHART_OBJ_INDICATOR_CHANGED)," ",ind_name," >>> ",ind.Name()); } } //--- Handling trading events
Jedes der Ereignisse, die von der Kollektion der Chartobjekte ankommen, zeigt ein Beispiel für das Senden dieses Ereignisses von den Bibliotheksklassen in den Kommentaren.
Dieses Beispiel zeigt deutlich, welche Art von Daten wir in jedem der drei Parameter (lparam, dparam und sparam) erhalten. Diese Daten werden für die Suche nach den benötigten Objekten in der Bibliothek verwendet. Sie dienen auch als Grundlage für die Erstellung einer gemeinsamen Meldung, die im Journal angezeigt werden soll. Eine weitere Behandlung von Ereignissen aus der Kollektion der Chart-Objekte werde ich im Test-EA nicht implementieren. Dieses Beispiel ist völlig ausreichend, um zu verstehen, wie man eingehende Ereignisse für den eigenen Bedarf behandeln kann.
Kompilieren Sie den EA und starten Sie ihn auf dem Chart.
Öffnen Sie einen neuen Symbol-Chart, dann sehen Sie die folgende Meldung im Journal vom OnDoEasyEvent():
OnDoEasyEvent: Open chart: AUDNZD H4, ID 131733844391938634
Starten Sie einen beliebigen Oszillator mit eigenen Fenster auf dem geöffneten Chart — Sie erhalten die folgende Journalmeldung von OnDoEasyEvent():
OnDoEasyEvent: AUDNZD H1, ID 131733844391938634: Added subwindow 1 Momentum(14)
Fügen Sie jetzt zum geöffneten Chart einen Indikator hinzu, der im Hauptfenster gezeichnet wird — Sie erhalten die folgende Journalmeldung von OnDoEasyEvent():
OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Main chart window: Added indicator AMA(14,2,30)
Ändern Sie die Oszillator-Parameter — Sie erhalten die folgende Journalmeldung von OnDoEasyEvent():
OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Chart subwindow 1: Changed indicator Momentum(14) >>> Momentum(20)
Ändern Sie die Parameter des Indikators im Hauptfenster — Sie erhalten die folgende Journalmeldung von OnDoEasyEvent():
OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Main chart window: Changed indicator AMA(14,2,30) >>> AMA(20,2,30)
Entfernen Sie das Oszillatorfenster — Sie erhalten die beiden, folgenden Journalmeldungen von OnDoEasyEvent():
OnDoEasyEvent: AUDNZD H4, ID 131733844391938634: Removed indicator Momentum(20) OnDoEasyEvent: AUDNZD H1, ID 131733844391938634: Removed subwindow 1
Entfernen Sie nun den Indikator aus dem Hauptfenster — Sie erhalten die folgende Journalmeldung von OnDoEasyEvent():
OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Main chart window: Removed indicator AMA(20,2,30)
Schließen Sie das zuvor geöffnete Chart-Fenster — Sie erhalten die folgende Journalmeldung von OnDoEasyEvent():
OnDoEasyEvent: Closed chart: AUDNZD H4, ID 131733844391938634
Wie wir sehen können, werden alle Ereignisse korrekt behandelt und an das Hauptprogramm gesendet.
Was kommt als Nächstes?
Im nächsten Artikel werde ich die automatische Verfolgung von Änderungen und die Verwaltung der Eigenschaften aller Chartobjekte implementieren.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit der Test-EA-Datei für MQL5 zum Testen und Herunterladen angehängt.
Ihre Fragen und Vorschläge schreiben Sie bitte in den Kommentarteil.
*Frühere Artikel dieser Serie:
Andere Klassen in der Bibliothek DoEasy (Teil 67): Objektklasse der Charts
Andere Klassen in der Bibliothek DoEasy (Teil 68): Die Chartfenster-Objektklasse und die Indikator-Objektklassen im Chartfenster
Andere Klassen in der Bibliothek DoEasy (Teil 69): Kollektionsklasse der Chart-Objekte
Andere Klassen in der Bibliothek DoEasy (Teil 70): Erweiterte Funktionalität und automatisches Aktualisieren der Kollektion der Chartobjekte
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/9360





- 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.