Otras clases en la biblioteca DoEasy (Parte 68): Clase de objeto de ventana de gráfico y clases de objetos de indicador en la ventana del gráfico
Contenido
- Concepto
- Clase de ventana de gráfico con objetos de indicador en la ventana
- Simulación
- ¿Qué es lo próximo?
Concepto
En el último artículo, comenzamos a desarrollar la clase de objeto de gráfico y creamos su primera versión. El objeto describe un gráfico del terminal con todos sus parámetros. Asimismo, nos permite gestionar sus propiedades, a saber, obtener y establecer los parámetros del tamaño de la ventana y la visualización de los elementos del gráfico.
No obstante, en un gráfico podemos tener varias ventanas en las que también se colocan indicadores. Estas ventanas tienen sus propios tamaños y, por el momento, nuestro objeto gráfico podrá simplemente retornar los parámetros de la subventana indicada que se encuentre en ella, asi como controlar su altura. En una misma subventana, podemos colocar un número diferente de indicadores (como en la ventana principal del gráfico), y debemos tener esto en cuenta para que al acceder al objeto del gráfico, podamos solicitar la ventana necesaria ubicada en el gráfico, y desde el objeto de la ventana resultante, solicitar una lista con sus indicadores y obtener el identificador del deseado, pudiendo así continuar el trabajo con él.
Hoy, vamos a crear dos objetos: un objeto de indicador en la ventana del gráfico que describirá algunos parámetros del indicador para su identificación, y un objeto de ventana del gráfico que almacenará sus dimensiones y una lista con los indicadores (objetos de indicador en la ventana del gráfico) adjuntos al mismo. El objeto de gráfico que comenzamos a crear en el último artículo tendrá una lista de objetos de ventana adjunta (incluida la ventana principal del gráfico).
En el futuro, esta jerarquía facilitará sustancialmente nuestro trabajo con muchos gráficos y sus subventanas con listas de indicadores en ellos. Por desgracia, es demasiado pronto para crear la colección de clases de objetos de gráfico que anunciamos en el último artículo; necesitamos completar todas las modificaciones principales del objeto de gráfico, cosa que haremos hoy.
Clase de ventana de gráfico con objetos de indicador en la ventana
En pimer lugar, completaremos la biblioteca con todos los textos necesarios de los mensajes.
En el archivo \MQL5\Include\DoEasy\Data.mqh escribimos los índices de los nuevos mensajes:
MSG_CHART_OBJ_CHART_WINDOW, // Main chart window MSG_CHART_OBJ_CHART_SUBWINDOW, // Chart subwindow MSG_CHART_OBJ_CHART_SUBWINDOWS_NUM, // Subwindows MSG_CHART_OBJ_INDICATORS_MW_NAME_LIST, // Indicators in the main chart window MSG_CHART_OBJ_INDICATORS_SW_NAME_LIST, // Indicators in the chart window MSG_CHART_OBJ_INDICATOR, // Indicator MSG_CHART_OBJ_INDICATORS_TOTAL, // Indicators MSG_CHART_OBJ_WINDOW_N, // Window MSG_CHART_OBJ_INDICATORS_NONE, // No indicators }; //+------------------------------------------------------------------+
y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:
{"Главное окно графика","Main chart window"}, {"Подокно графика","Chart subwindow"}, {"Подокон","Subwindows"}, {"Индикаторы в главном окне графика","Indicators in the main chart window"}, {"Индикаторы в окне графика","Indicators in the chart window"}, {"Индикатор","Indicator"}, {"Индикаторов","Indicators total"}, {"Окно","Window"}, {"Отсутствуют","No indicators"}, }; //+---------------------------------------------------------------------+
Al crear la enumeración de las propiedades de tipo entero del objeto de gráfico, hemos omitido a propósito tres propiedades que son propias no solo del objeto de la ventana principal, sino también de todas las subventanas del gráfico:
- Visibilidad de las subventanas,
- Distancia en píxeles a lo largo del eje vertical Y entre el marco superior de la subventana del indicador y el marco superior de la ventana principal del gráfico.
- Altura del gráfico en píxeles.
Precisamente estas propiedades serán las propiedades principales del objeto de la ventana del gráfico (excepto la visibilidad de la subventana, que reconoceremos a partir del objeto de gráfico). La lista de ventanas de gráficos estará presente en cada objeto de gráfico, y cada objeto tendrá sus propios valores de estas propiedades. Al mismo tiempo, cada objeto de ventana contendrá una lista de indicadores adjuntos a esta ventana, y necesitaremos especificar las constantes adicionales en la enumeración de las propiedades de tipo entero y string del objeto de gráfico.
En el archivo \MQL5\Include\DoEasy\Defines.mqh, comentamos las propiedades que habíamos definido anteriormente, pero que no habíamos comentado, a saber, las propiedades del objeto de ventana del gráfico. Luego, añadimos las nuevas para el objeto de indicador en la ventana del gráfico:
//+------------------------------------------------------------------+ //| 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_IS_VISIBLE, // Subwindow visibility 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 }; #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 //+------------------------------------------------------------------+ //| Chart real properties | //+------------------------------------------------------------------+ enum ENUM_CHART_PROP_DOUBLE { CHART_PROP_SHIFT_SIZE = CHART_PROP_INTEGER_TOTAL, // Shift size of the zero bar from the right border in % CHART_PROP_FIXED_POSITION, // Chart fixed position from the left border in % CHART_PROP_FIXED_MAX, // Chart fixed maximum CHART_PROP_FIXED_MIN, // Chart fixed minimum CHART_PROP_POINTS_PER_BAR, // Scale in points per bar CHART_PROP_PRICE_MIN, // Chart minimum CHART_PROP_PRICE_MAX, // Chart maximum }; #define CHART_PROP_DOUBLE_TOTAL (7) // Total number of real properties #define CHART_PROP_DOUBLE_SKIP (0) // Number of real properties not used in sorting //+------------------------------------------------------------------+ //| Chart string properties | //+------------------------------------------------------------------+ enum ENUM_CHART_PROP_STRING { CHART_PROP_COMMENT = (CHART_PROP_INTEGER_TOTAL+CHART_PROP_DOUBLE_TOTAL), // Chart comment text CHART_PROP_EXPERT_NAME, // Name of an EA launched on the chart CHART_PROP_SCRIPT_NAME, // Name of a script launched on the chart CHART_PROP_INDICATOR_NAME, // Name of an indicator launched on the chart CHART_PROP_SYMBOL, // Chart symbol }; #define CHART_PROP_STRING_TOTAL (5) // Total number of string properties //+------------------------------------------------------------------+
Por consiguiente, modificaremos los valores del número de propiedades a cuyas listas hemos añadido las nuevas constantes: hemos aumentado el número de propiedades de tipo entero de 62 a 67, y el número de propiedades de tipo string de 4 a 5.
Añadimos a la enumeración de posibles criterios de clasificación de los objetos de gráfico nuevos criterios que se correspondan con las propiedades nuevamente añadidas:
//+------------------------------------------------------------------+ //| 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_SHOW = 0, // 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_IS_VISIBLE, // Sort by the subwindow visibility flag 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 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_INDICATOR_NAME, // Sort by a name of an indicator launched on the chart SORT_BY_CHART_SYMBOL, // Sort by chart symbol }; //+------------------------------------------------------------------+
En la primera versión del objeto de gráfico, la lista de criterios era incorrecta porque las propiedades comentadas de tipo entero del gráfico se añadieron a los criterios de clasificación, y no había clasificación según el nombre del símbolo. Hemos solucionado esto en el presente artículo.
Ahora, necesitamos crear dos clases: la clase del objeto de indicador en la ventana del gráfico y la clase del objeto de ventana de gráfico. Vamos a añadirlos en un solo archivo.
En la carpeta de la biblioteca \MQL5\Include\DoEasy\Objects\Chart\, creamos el nuevo archivo ChartWnd.mqh de las clases CWndInd (indicador en la ventana del gráfico) y CChartWnd (ventana del gráfico).
La clase CWndInd debe heredarse de la clase básica de la biblioteca estándar CObject, y la clase CChartWnd, del objeto básico de todos los objetos de la biblioteca CBaseObj.
Podemos adjuntar muchos indicadores diferentes a la ventana del gráfico. Por consiguiente, el objeto de la ventana del gráfico debe conocer su existencia para que siempre podamos obtener el identificador del indicador necesario desde la ventana del gráfico y luego trabajar con él. Para identificar los indicadores, no se necesitan muchos parámetros distintos; bastará con conocer el identificador del indicador, su nombre breve y el índice de la ventana del gráfico a la que se adjunta el indicador. Por ello, la clase de objeto de indicador en la ventana del gráfico será la más sencilla posible y se heredará del objeto básico de la biblioteca estándar, solo para que podamos añadir todos estos objetos a la lista de punteros a los objetos CArrayObj incluidos en las ventanas de los gráficos de los objetos.
Vamos a escribir el código de la nueva clase en el archivo ChartWnd.mqh recién creado:
//+------------------------------------------------------------------+ //| ChartWnd.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| 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; // Window index on the chart int m_handle; // Indicator handle public: //--- Return itself CWndInd *GetObject(void) { return &this; } //--- Return (1) indicator name, (2) window index and (3) indicator handle string Name(void) const { return this.m_name; } int Index(void) const { return this.m_index; } int Handle(void) const { return this.m_handle; } //--- 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) : m_handle(handle),m_name(name),m_index(index) {} }; //+------------------------------------------------------------------+ //| Compare CWndInd objects with each other by the specified property| //+------------------------------------------------------------------+ int CWndInd::Compare(const CObject *node,const int mode=0) const { const CWndInd *obj_compared=node; if(mode==CHART_PROP_WINDOW_IND_HANDLE) return(this.Handle()>obj_compared.Handle() ? 1 : this.Handle()<obj_compared.Handle() ? -1 : 0); else if(mode==CHART_PROP_WINDOW_IND_INDEX) return(this.Index()>obj_compared.Index() ? 1 : this.Index()<obj_compared.Index() ? -1 : 0); return(this.Name()==obj_compared.Name() ? 0 : this.Name()<obj_compared.Name() ? -1 : 1); } //+------------------------------------------------------------------+
Esta es la clase completa del objeto de indicador en la ventana del gráfico.
En la sección privada se ubican las variables de miembros de clase para el almacenamiento:
- el identificador del gráfico en el que se encuentra la ventana con este indicador,
- el nombre breve del indicador (en las ventanas de los gráficos, los indicadores son identificados por el terminal según sus nombres breves),
- el índice de la ventana del gráfico en el que se encuentra el indicador (índice 0 - ventana principal del gráfico, índice 1 y superiores - subventanas del gráfico),
- el manejador de este indicador.
Estos datos bastarán para guardar una lista con todos los indicadores adjuntos a la ventana en el objeto de ventana del gráfico. La lista en sí contendrá exactamente estos objetos, que nos ayudarán a encontrar el indicador necesario y retornar su identificador para seguir trabajando con él.
Los métodos públicos que retornan los valores de las variables anteriormente enumeradas no requieren una explicación especial. Vamos a analizar algunos otros métodos de clase.
Método que retorna el nombre breve de un objeto de indicador:
//--- Return the object short name string Header(void) const { return CMessage::Text(MSG_CHART_OBJ_INDICATOR)+" "+this.Name(); }
Simplemente se retorna el encabezado "Indicador " + el nombre del indicador.
Método que muestra en el diario la descripción de las propiedades del objeto de indicador:
//--- 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()); }
Como podemos tener varios indicadores en la ventana del gráfico, se mostrarán como una lista debajo del encabezado. Por consiguiente, para visualizar mejor la lista en el diario de registro, utilizaremos un guión delante del nombre del indicador. El parámetro de entrada del método indicará la necesidad de mostrar el guión.
Tenemos dos constructores: uno predeterminado y otro paramétrico. El constructor predeterminado puede resultar útil para crear un objeto de indicador "vacío" en la ventana, mientras que el constructor paramétrico se usará como constructor principal de la clase al crear una lista de indicadores en la clase del objeto de ventana del gráfico.
Al constructor paramétrico se le transmiten el identificador del indicador, su nombre breve y el índice de la subventana donde se encuentra este indicador.
//--- Constructors CWndInd(void); CWndInd(const int handle,const string name,const int index) : m_handle(handle),m_name(name),m_index(index) {}
Todos los valores de parámetro transmitidos al método se asignan inmediatamente a las variables de clase en su lista de inicialización.
Método para comparar los objetos de indicador de la ventana del gráfico entre sí según la propiedad especificada:
//+------------------------------------------------------------------+ //| Compare CWndInd objects with each other by the specified property| //+------------------------------------------------------------------+ int CWndInd::Compare(const CObject *node,const int mode=0) const { const CWndInd *obj_compared=node; if(mode==CHART_PROP_WINDOW_IND_HANDLE) return(this.Handle()>obj_compared.Handle() ? 1 : this.Handle()<obj_compared.Handle() ? -1 : 0); else if(mode==CHART_PROP_WINDOW_IND_INDEX) return(this.Index()>obj_compared.Index() ? 1 : this.Index()<obj_compared.Index() ? -1 : 0); return(this.Name()==obj_compared.Name() ? 0 : this.Name()<obj_compared.Name() ? -1 : 1); } //+------------------------------------------------------------------+
Para este objeto, no disponemos de nuestras propias enumeraciones de las propiedades; todas sus propiedades se encuentran en las enumeraciones de las propiedades del objeto de gráfico. Por ello, cualquiera de las propiedades del objeto de gráfico se puede transmitir al método, pero la comparación según las propiedades transmitidas se realizará solo si en el parámetro mode se transmite la propiedad "manejador del indicador" o "índice de la ventana del gráfico" En cualquier otro caso, la comparación se realizará según el nombre breve del indicador.
El método de comparación es estándar para todos los objetos de la biblioteca: si el valor del parámetro del objeto actual es superior al valor del objeto que se está comparando, retornaremos 1; si el valor del parámetro del objeto actual es inferior al valor del objeto que se está comparando, retornaremos -1; de lo contrario retornaremos 0.
Vamos a crear la clase del objeto de ventana del gráfico.
En el mismo archivo en el que hemos escrito la clase del objeto de indicador en la ventana del gráfico (ChartWnd.mqh), continuamos escribiendo el código e introducimos la clase del objeto de ventana del gráfico. La clase debe heredarse del objeto básico de todos los objetos de la biblioteca:
//+------------------------------------------------------------------+ //| Chart window object class | //+------------------------------------------------------------------+ class CChartWnd : public CBaseObj { }
En la sección privada de la clase se encontrarán la lista de punteros a los objetos de indicador en esta ventana, el número de esta subventana descrita por el objeto, y los métodos auxiliares para organizar el funcionamiento de la clase:
//+------------------------------------------------------------------+ //| Chart window object class | //+------------------------------------------------------------------+ class CChartWnd : public CBaseObj { private: CArrayObj m_list_ind; // Indicator list int m_window_num; // Subwindow index //--- Return the flag indicating the presence of an indicator from the list in the window bool IsPresentInWindow(const CWndInd *ind); //--- Remove indicators not present in the window from the list void IndicatorsDelete(void); //--- Add new indicators to the list void IndicatorsAdd(void); //--- Set a subwindow index void SetWindowNum(const int num) { this.m_window_num=num; } public:
En la sección pública de la clase, se ubican los métodos estándar para los objetos de la biblioteca (salvo los métodos para establecer los valores de las propiedades, porque para este objeto no existen las listas propias de enumeración de las propiedades, y nosotros solo vamos a retornar los valores necesarios descritos por algunas propiedades del objeto de gráfico pertenecientes a la ventana del gráfico). Ya hemos analizado más de una vez estos métodos en artículos anteriores.
Asimismo, en la clase se encuentran los métodos para establecer y retornar los valores de las propiedades de la ventana y trabajar con la clase. Más abajo, podemos verlos con detalle.
//+------------------------------------------------------------------+ //| Chart window object class | //+------------------------------------------------------------------+ class CChartWnd : public CBaseObj { private: CArrayObj m_list_ind; // Indicator list int m_window_num; // Subwindow index //--- Return the flag indicating the presence of an indicator from the list in the window bool IsPresentInWindow(const CWndInd *ind); //--- Remove indicators not present in the window from the list void IndicatorsDelete(void); //--- Add new indicators to the list void IndicatorsAdd(void); //--- Set a subwindow index void SetWindowNum(const int num) { this.m_window_num=num; } 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_INDICATOR_NAME ? 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) { return CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED); } //--- 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); //--- 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); //--- Return the distance in pixels between the window borders int YDistance(void) const { return (int)::ChartGetInteger(this.m_chart_id,CHART_WINDOW_YDISTANCE,this.m_window_num);} //--- (1) Return and (2) set the window height in pixels int HeightInPixels(void) const { return (int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_window_num);} bool SetHeightInPixels(const int value,const bool redraw=false); //--- Return (1) the subwindow index and (2) the number of indicators attached to the window int WindowNum(void) const { return this.m_window_num;} int IndicatorsTotal(void) const { return this.m_list_ind.Total(); } //--- Return (1) the indicator list and (2) the window indicator object from the list by index CArrayObj *GetIndicatorsList(void) { return &this.m_list_ind; } CWndInd *GetIndicator(const int index) { return this.m_list_ind.At(index); } //--- Display the description of indicators attached to the chart window in the journal void PrintIndicators(const bool dash=false); //--- Display the description of the window parameters in the journal void PrintParameters(const bool dash=false); //--- Create the list of indicators attached to the window void IndicatorsListCreate(void); //--- Update data on attached indicators void Refresh(void); }; //+------------------------------------------------------------------+
Método que retorna la distancia en píxeles entre los bordes de las ventanas:
//--- Return the distance in pixels between the window borders int YDistance(void) const { return (int)::ChartGetInteger(this.m_chart_id,CHART_WINDOW_YDISTANCE,this.m_window_num);}
Como la propiedad del gráfico CHART_WINDOW_YDISTANCE es solo de lectura, aquí no hay ningún método para establecer este valor. El método simplemente retorna el valor de esta propiedad para esta subventana del gráfico en concreto.
Método que retorna la altura de la ventana en píxeles:
int HeightInPixels(void) const { return (int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_window_num);}
Funciona de forma idéntica al anteriormente analizado, y retorna el valor de esta propiedad para el número de ventana indicado en la variable m_window_num.
Método que establece la altura de la ventana en píxeles (se declara en el cuerpo de la clase, y se implementa fuera del cuerpo de la clase):
//+------------------------------------------------------------------+ //| Set the window height in pixels | //+------------------------------------------------------------------+ bool CChartWnd::SetHeightInPixels(const int value,const bool redraw=false) { ::ResetLastError(); if(!::ChartSetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_window_num,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } if(redraw) ::ChartRedraw(this.m_chart_id); return true; } //+------------------------------------------------------------------+
El método resulta similar a los métodos que establecen los valores de las propiedades del objeto de gráfico que analizamos en el artículo anterior. Transmitimos al método el valor necesario para la configuración. Después, intentamos configurarlo en la ventana utilizando la función ChartSetInteger(), y si el evento de cambio del gráfico no se encontraba en la cola, informamos sobre ello y retornamos false. Si el evento se ha puesto con éxito en cola, retornamos true, redibujando previamente el gráfico al activarse la bandera redraw. El redibujado forzoso del gráfico es necesario para no tener que esperar a que ningún evento del gráfico (la llegada de una cotización, el cambio de tamaño, los clics del ratón, etc.) muestre los cambios, volviendo a dibujar el gráfico de inmediato para ver el resultado.
Método que compara los objetos de la ventana del gráfico entre sí según la propiedad especificada:
//+------------------------------------------------------------------+ //| 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); return -1; } //+------------------------------------------------------------------+
Aquí, al igual que sucede en el método de comparación en la clase del indicador de la ventana del gráfico que hemos analizado anteriormente, comparamos solo algunas de las propiedades indicadas en las enumeraciones de las propiedades del objeto de gráfico:
- Distancia en píxeles a lo largo del eje vertical Y entre el marco superior de la subventana del indicador y el marco superior de la ventana principal del gráfico.
- La altura del gráfico en píxeles,
- En cualquier otro caso, retornamos -1
Método que compara los objetos de la ventana del gráfico entre sí según todas las propiedades:
//+------------------------------------------------------------------+ //| Compare the CChartWnd objects by all properties | //+------------------------------------------------------------------+ bool CChartWnd::IsEqual(CChartWnd *compared_obj) const { return(this.YDistance()!=compared_obj.YDistance() || this.HeightInPixels()!=compared_obj.HeightInPixels() ? false : true); } //+------------------------------------------------------------------+
Aquí, si aunque sea una de las propiedades de los dos objetos comparados no resulta igual, retornamos false: los objetos no son idénticos. De lo contrario, retornamos true: los objetos son idénticos.
Método que retorna la descripción de la propiedad de tipo entero:
//+------------------------------------------------------------------+ //| Return description of object's integer property | //+------------------------------------------------------------------+ string CChartWnd::GetPropertyDescription(ENUM_CHART_PROP_INTEGER property) { return ( property==CHART_PROP_WINDOW_YDISTANCE ? CMessage::Text(MSG_CHART_OBJ_WINDOW_YDISTANCE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.YDistance() ) : property==CHART_PROP_HEIGHT_IN_PIXELS ? CMessage::Text(MSG_CHART_OBJ_HEIGHT_IN_PIXELS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.HeightInPixels() ) : "" ); } //+------------------------------------------------------------------+
Dependiendo de cuál de las dos propiedades enteras del objeto de ventana del gráfico sea transmitida al método, se crea y se retorna una línea con su descripción.
Método que muestra en el diario la propiedad del objeto:
//+------------------------------------------------------------------+ //| Display object properties in the journal | //+------------------------------------------------------------------+ void CChartWnd::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=CHART_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_INTEGER prop=(ENUM_CHART_PROP_INTEGER)i; if(prop!=CHART_PROP_WINDOW_YDISTANCE && prop!=CHART_PROP_HEIGHT_IN_PIXELS) continue; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=CHART_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { //ENUM_CHART_PROP_DOUBLE prop=(ENUM_CHART_PROP_DOUBLE)i; //if(!full_prop && !this.SupportProperty(prop)) continue; //::Print(this.GetPropertyDescription(prop)); } beg=end; end+=CHART_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_STRING prop=(ENUM_CHART_PROP_STRING)i; if(prop==CHART_PROP_INDICATOR_NAME) { this.PrintIndicators(); continue; } if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } //+------------------------------------------------------------------+
En tres ciclos para todas las propiedades del objeto gráfico, obtenemos la siguiente propiedad y mostramos su descripción en el diario. Como el objeto de la ventana del gráfico no dispone de sus propias listas de enumeración para sus propiedades, necesitaremos imprimir solo aquellas propiedades del objeto de gráfico que sean inherentes al objeto de la ventana del gráfico, es decir, las de tipo entero y string. Este objeto no tiene propiedades de tipo real, por lo que no necesitamos mostrarlas. Podríamos haberlo hecho de forma distinta, estableciendo simplemente un marco rígido para el comienzo y el final de cada ciclo... Pero esta no es una decisión muy correcta desde el punto de vista de las modificaciones posteriores de las propiedades del objeto de gráfico; tendremos que regresar a este objeto y editar los valores del principio y el final de cada ciclo. Por consiguiente, simplemente dejaremos vacío el ciclo de propiedades reales (tal vez de forma temporal, hasta que las propiedades reales del objeto de la ventana del gráfico sean necesarias para posibles modificaciones posteriores). Por lo tanto, para realizar cualquier cambio en el número de propiedades, el comienzo y el final de cada ciclo siempre deberán ser correctos.
Método que retorna el nombre breve del objeto de ventana del gráfico:
//+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CChartWnd::Header(void) { return(this.m_window_num==0 ? CMessage::Text(MSG_CHART_OBJ_CHART_WINDOW) : (string)this.WindowNum()+" "+CMessage::Text(MSG_CHART_OBJ_CHART_SUBWINDOW)); } //+------------------------------------------------------------------+
El método verifica el índice de la subventana, y si se trata de la ventana principal del gráfico (índice 0), se retornará la línea "ventana principal del gráfico"; si se trata de una subventana del gráfico principal, se retornará el número de subventana + la línea "subventana del gráfico".
Método que muestra en el diario la descripción breve de un objeto de ventana del gráfico:
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CChartWnd::PrintShort(const bool dash=false) { ::Print((dash ? "- " : ""),this.Header()," ID: ",(string)this.GetChartID(),", ",CMessage::Text(MSG_CHART_OBJ_INDICATORS_TOTAL),": ",this.IndicatorsTotal()); } //+------------------------------------------------------------------+
Aquí, creamos una línea con el nombre breve del objeto, el identificador del gráfico y el número de indicadores fijados a esta ventana. Si transmitimos al método la bandera que indica la necesidad de mostrar un guión antes de la descripción del objeto (dash), se mostrará un guión antes de la línea creada.
Método que muestra en el diario la descripción de todos los indicadores fijados a esta ventana:
//+------------------------------------------------------------------+ //| Display the description of indicators attached to the window | //+------------------------------------------------------------------+ void CChartWnd::PrintIndicators(const bool dash=false) { string header= ( this.WindowNum()==0 ? CMessage::Text(MSG_CHART_OBJ_INDICATORS_MW_NAME_LIST) : CMessage::Text(MSG_CHART_OBJ_INDICATORS_SW_NAME_LIST)+" "+(string)this.WindowNum() ); ::Print(header,":"); int total=this.IndicatorsTotal(); if(total==0) ::Print("- ",CMessage::Text(MSG_CHART_OBJ_INDICATORS_NONE)); else for(int i=0;i<total;i++) { CWndInd *ind=this.m_list_ind.At(i); if(ind==NULL) continue; ind.Print(dash); } } //+------------------------------------------------------------------+
Primero, creamos y mostramos en el diario el encabezado, dependiendo de qué ventana se trate.
Si se trata de la ventana principal del gráfico, el texto del encabezado será "indicadores en la ventana principal del gráfico",
De lo contrario, el texto del encabezado será "indicadores en la ventana del gráfico" + número de esta ventana.
Luego, miramos el número de indicadores fijados a esta ventana , y si no hay, mostramos la línea "no hay" .
De lo contrario, obtendremos en un ciclo por la lista de todos los indicadores el siguiente objeto de indicador en la ventana del gráfico y lo imprimiremos.
Método que muestra en el diario la descripción de los parámetros de la ventana:
//+------------------------------------------------------------------+ //| Display the description of the window parameters in the journal | //+------------------------------------------------------------------+ void CChartWnd::PrintParameters(const bool dash=false) { string header= ( this.WindowNum()==0 ? CMessage::Text(MSG_CHART_OBJ_CHART_WINDOW) : CMessage::Text(MSG_CHART_OBJ_CHART_SUBWINDOW)+" "+(string)this.WindowNum() ); ::Print((dash ? " " : ""),header,":"); if(this.WindowNum()>0) ::Print((dash ? " - " : ""),GetPropertyDescription(CHART_PROP_WINDOW_YDISTANCE)); ::Print((dash ? " - " : ""),GetPropertyDescription(CHART_PROP_HEIGHT_IN_PIXELS)); } //+------------------------------------------------------------------+
Primero, creamos y mostramos en el diario el encabezado, dependiendo de qué ventana se trate.
si se trata de la ventana principal del gráfico, el texto del encabezado será "ventana principal del gráfico",
de lo contrario, el texto del encabezado será "subventana del gráfico" + el número de esta ventana.
Luego, si se trata de una subventana del gráfico (su número es mayor que cero), mostraremos el valor de la distancia en píxeles a lo largo del eje vertical Y entre el marco superior de la subventana del indicador y el marco superior de la ventana principal del gráfico (para la ventana principal, este valor es siempre 0, por lo que no lo mostraremos), y después mostraremos en el diario la segunda propiedad del objeto: la altura del gráfico en píxeles.
Método que crea la lista de indicadores fijados a la ventana:
//+------------------------------------------------------------------+ //| Create the list of indicators attached to the window | //+------------------------------------------------------------------+ void CChartWnd::IndicatorsListCreate(void) { //--- Clear the list of indicators 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); //--- Free the indicator handle ::IndicatorRelease(handle); //--- Create the new indicator object in the chart window CWndInd *ind=new CWndInd(handle,name,i); 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; } } //+------------------------------------------------------------------+
El método se comenta con detalle en el código. Notemos que, al obtener la lista de indicadores en la ventana, obtenemos el manejador del indicador según su nombre breve usando ChartIndicatorGet(), lo cual nos impone algunas "responsabilidades". El terminal realiza un seguimiento del uso de cada indicador, y con cada nueva obtención del manejador, aumentará el contador interno de uso de este indicador. Si no liberamos en nuestro programa el manejador del indicador que ya no necesitamos, nos resultará imposible captar el manejador "perdido" más tarde. Por consiguiente, inmediatamente después de obtener todos los datos del indicador necesario, liberamos el manejador, disminuyendo así el contador interno del uso del indicador.
Método que añade los nuevos indicadores a la lista:
//+------------------------------------------------------------------+ //| 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); //--- Release the indicator handle ::IndicatorRelease(handle); //--- Create the new indicator object in the chart window CWndInd *ind=new CWndInd(handle,name,i); 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; } } //+------------------------------------------------------------------+
La lógica del método es idéntica a la analizada anteriormente. También se comenta en el código. La única diferencia es que la lista no se borra inicialmente en el método. Al añadir un indicador a la lista, se comprueba en primer lugar la presencia de dicho indicador en la lista, y si ya está presente, el objeto de este indicador será eliminado.
Para poder sincronizar nuestra lista de indicadores en la ventana con su número real (después de todo, podemos añadir los indicadores a la ventana y eliminarlos de la ventana del gráfico), debemos comparar su número en la ventana con el número en la lista. Implementaremos esto en los siguientes artículos. No obstante, vamos a crear aquí el método que retorna la presencia de un indicador de la lista en la ventana del terminal:
//+----------------------------------------------------------------------------------------+ //| 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); ::IndicatorRelease(handle); if(ind.Name()==name && ind.Handle()==handle) return true; } return false; } //+------------------------------------------------------------------+
Transmitmos al método el puntero al objeto de indicador en la ventana del gráfico cuya presencia real debemos comprobar. A continuación, en un ciclo por el número total de indicadores en la ventana del gráfico obtenemos el nombre del siguiente indicador, obtenemos su manejador y lo liberamos de inmediato. Si el nombre breve y el manejador del indicador actual coinciden con el nombre y el manejador del objeto que estamos comprobando, retornaremos true: este indicador aún se encuentra en la ventana del gráfico. Al finalizar el ciclo, retornamos false: no se han encontrado coincidencias, lo cual significa que no existe tal indicador en la ventana del gráfico.
Si la lista presenta indicadores que no están presentes en el gráfico, deberemos eliminarlos de la lista.
Método que elimina de la lista los indicadores ya existentes en la ventana:
//+------------------------------------------------------------------+ //| Remove indicators not present in the window from the list | //+------------------------------------------------------------------+ void CChartWnd::IndicatorsDelete(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(!this.IsPresentInWindow(ind)) this.m_list_ind.Delete(i); } } //+------------------------------------------------------------------+
Aquí, en un ciclo por la lista de objetos de indicador, obtenemos el siguiente objeto de indicador según el índice del ciclo y comprobamos su presencia en la ventana real del gráfico. Si está ausente, eliminaremos el puntero al mismo de la lista usando el método Delete().
Más adelante, al implementar la clase de colección de gráficos, deberemos monitorear el estado del indicador en las subventanas de todos los objetos del gráfico. Si existen desajustes entre el número real de indicadores en las subventanas de los gráficos y sus descripciones en las listas de los objetos de ventana del gráfico, deberemos eliminar los indicadores innecesarios de la lista y añadir nuevos si estos estuvieran disponibles.
Para ello, crearemos un método que combinará los datos de los indicadores fijados a las ventanas de los gráficos:
//+------------------------------------------------------------------+ //| Update data on attached indicators | //+------------------------------------------------------------------+ void CChartWnd::Refresh(void) { this.IndicatorsDelete(); this.IndicatorsAdd(); } //+------------------------------------------------------------------+
Aquí, primero buscamos y eliminamos de la lista los indicadores ausentes en la ventana del gráfico real que aún existan en la lista, y después buscamos y añadimos los nuevos indicadores que estén en la ventana, pero que todavía no se encuentren en la lista con la ayuda de los métodos analizados anteriormente.
Por último, vamos a analizar el constructor paramétrico de la clase:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CChartWnd::CChartWnd(const long chart_id,const int wnd_num) : m_window_num(wnd_num) { CBaseObj::SetChartID(chart_id); this.IndicatorsListCreate(); } //+------------------------------------------------------------------+
Aquí, en primer lugar, establecemos el valor del identificador del gráfico en el objeto padre CBaseObj, y después creamos la lista de indicadores adjuntos a esta ventana del gráfico. Establecemos el número de subventana del gráfico en la lista de inicialización del constructor.
Con esto, podemos dar por finalizada la creación de la clase de objeto de indicador en la ventana del gráfico y la clase de objeto de ventana del gráfico.
Podrá ver el listado completo de ambas clases en un único archivo ubicado en los archivos adjuntos al artículo.
Como ahora tenemos un objeto que describe la ventana del gráfico y sus subventanas, respectivamente, es hora de mejorar la clase del objeto de gráfico CCHARTOBJ CChartObj, que se encuentar en el archivo \MQL5\Include\DoEasy\Objects\Chart\ChartObj.mqh. Ahora, tendremos una lista con todas las subventanas ubicadas en su ventana principal. Para obtener las propiedades de la ventana, deberemos utilizar el puntero al objeto de ventana del gráfico necesario, creado anteriormente. Partiendo del objeto de ventana obtenido, podremos obtener la lista de todos los indicadores fijados a él, y ya de esos indicadores, podremos recibir el manejador del indicador necesario para trabajar con él.
En primer lugar, añadimos al archivo del objeto de gráfico el archivo de los objetos de la ventana y de los indicadores en la ventana y declaramos el objeto de lista en el que guardaremos los punteros a todas las ventanas del objeto de gráfico:
//+------------------------------------------------------------------+ //| ChartObj.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Objects\BaseObj.mqh" #include "ChartWnd.mqh" //+------------------------------------------------------------------+ //| Chart object class | //+------------------------------------------------------------------+ class CChartObj : public CBaseObj { private: CArrayObj m_list_wnd; // List of chart window objects 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()
En la seccón privada de la clase, en la lista de los métodos para establecer los valores de las propiedades, añadimos el método que establece el valor de visibilidad de la ventana del gráfico:
//--- 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 SetVisible(void); void SetFirstVisibleBars(void); void SetWidthInBars(void); void SetWidthInPixels(void); void SetMaximizedFlag(void); void SetMinimizedFlag(void); void SetExpertName(void); void SetScriptName(void); public:
Como la "distancia en píxeles a lo largo del eje vertical Y entre el marco superior de la subventana del indicador y el marco superior de la ventana principal del gráfico" no tiene sentido para la ventana principal (siempre está en 0), entonces, en el método que retorna la bandera de soporte por parte del objeto de una propiedad de tipo entero, cambiaremos el retorno del valor en caso de que sea la propiedad CHART_PROP_WINDOW_YDISTANCE:
//--- 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; }
Si la propiedad transmitida al método no es igual a CHART_PROP_WINDOW_YDISTANCE, retornaremos true, de lo contrario, retornaremos false.
En la lista de métodos para el acceso simplificado a las propiedades del objeto, añadimos el método que retorna la visibilidad de la ventana:
//--- Return the total number of chart windows including indicator subwindows int WindowsTotal(void) const { return (int)this.GetProperty(CHART_PROP_WINDOWS_TOTAL); } //--- Return the window visibility bool Visible(void) const { return (bool)this.GetProperty(CHART_PROP_WINDOW_IS_VISIBLE); } //--- Return the chart window handle int Handle(void) const { return (int)this.GetProperty(CHART_PROP_WINDOW_HANDLE); }
Aquí, el método retornará esta propiedad solo para la ventana principal del gráfico.
Los métodos que retornan la distancia en píxeles a lo largo del eje vertical Y entre el marco superior de la subventana del indicador y el marco superior de la ventana principal del gráfico, así como el método que retorna y configura la altura de un gráfico específico en píxeles han sufrido cambios. Ahora, para configurar y retornar estas propiedades, deberemos encontrar el objeto de la ventana necesaria, y ya en sus propiedades añadir u obtener estos valores. Analizaremos la implementación de los métodos un poco más tarde.
//--- Return the name of a script launched on the chart string ScriptName(void) const { return this.GetProperty(CHART_PROP_SCRIPT_NAME); } //--- Return the distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window int WindowYDistance(const int sub_window) const; //--- (1) Return and (2) set the height of the specified chart in pixels int WindowHeightInPixels(const int sub_window) const; bool SetWindowHeightInPixels(const int height,const int sub_window,const bool redraw=false); //--- Return the specified subwindow visibility
En la parte inferior del cuerpo de la clase, declaramos los métodos adicionales para obtener el objeto de ventana necesario y mostrar en el diario las propiedades de todas las subventanas del gráfico y los datos de todos los indicadores fijados a la ventana del gráfico especificada:
//--- Emulate a tick (chart updates - similar to the terminal Refresh command) void EmulateTick(void) { ::ChartSetSymbolPeriod(this.ID(),this.Symbol(),this.Timeframe());} //--- Return the chart window specified by index CChartWnd *GetWindowByIndex(const int index) const { return this.m_list_wnd.At(index); } //--- Return the window object by its subwindow index CChartWnd *GetWindowByNum(const int win_num) const; //--- Display data of all indicators of all chart windows in the journal void PrintWndIndicators(void); //--- Display the properties of all chart windows in the journal void PrintWndParameters(void); }; //+------------------------------------------------------------------+
En el constructor paramétrico de la clase, establecemos el valor del identificador del gráfico en el objeto padre de la clase, indicamos las tres propiedades de la ventana principal del objeto de gráfico (podríamos no hacer esto, ya que el objeto de la ventana principal ahora se encuentra en la lista de todas las ventanas del gráfico; pero, como el objeto tiene estas propiedades, las rellenaremos con los datos correctos), y al final del método, añadimos a la lista de todas las ventanas del gráfico todos los objetos de ventana que pertenecen a este gráfico:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CChartObj::CChartObj(const long chart_id) { //--- Set chart ID to the base object CBaseObj::SetChartID(chart_id); //--- Set integer properties this.SetProperty(CHART_PROP_ID,chart_id); // Chart ID this.SetProperty(CHART_PROP_TIMEFRAME,::ChartPeriod(this.ID())); // Chart timeframe this.SetProperty(CHART_PROP_SHOW,::ChartGetInteger(this.ID(),CHART_SHOW)); // Price chart drawing attribute this.SetProperty(CHART_PROP_IS_OBJECT,::ChartGetInteger(this.ID(),CHART_IS_OBJECT)); // Chart object identification attribute this.SetProperty(CHART_PROP_BRING_TO_TOP,false); // Show chart above all others this.SetProperty(CHART_PROP_CONTEXT_MENU,::ChartGetInteger(this.ID(),CHART_CONTEXT_MENU)); // Access to the context menu using the right click this.SetProperty(CHART_PROP_CROSSHAIR_TOOL,::ChartGetInteger(this.ID(),CHART_CROSSHAIR_TOOL)); // Access the Crosshair tool by pressing the middle mouse button this.SetProperty(CHART_PROP_MOUSE_SCROLL,::ChartGetInteger(this.ID(),CHART_MOUSE_SCROLL)); // Scroll the chart horizontally using the left mouse button this.SetProperty(CHART_PROP_EVENT_MOUSE_WHEEL,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_WHEEL)); // Send messages about mouse wheel events to all MQL5 programs on a chart this.SetProperty(CHART_PROP_EVENT_MOUSE_MOVE,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_MOVE)); // Send messages about mouse button click and movement events to all MQL5 programs on a chart this.SetProperty(CHART_PROP_EVENT_OBJECT_CREATE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_CREATE)); // Send messages about the graphical object creation event to all MQL5 programs on a chart this.SetProperty(CHART_PROP_EVENT_OBJECT_DELETE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_DELETE)); // Send messages about the graphical object destruction event to all MQL5 programs on a chart this.SetProperty(CHART_PROP_MODE,::ChartGetInteger(this.ID(),CHART_MODE)); // Type of the chart (candlesticks, bars or line) this.SetProperty(CHART_PROP_FOREGROUND,::ChartGetInteger(this.ID(),CHART_FOREGROUND)); // Price chart in the foreground this.SetProperty(CHART_PROP_SHIFT,::ChartGetInteger(this.ID(),CHART_SHIFT)); // Mode of shift of the price chart from the right border this.SetProperty(CHART_PROP_AUTOSCROLL,::ChartGetInteger(this.ID(),CHART_AUTOSCROLL)); // The mode of automatic shift to the right border of the chart this.SetProperty(CHART_PROP_KEYBOARD_CONTROL,::ChartGetInteger(this.ID(),CHART_KEYBOARD_CONTROL)); // Allow managing the chart using a keyboard this.SetProperty(CHART_PROP_QUICK_NAVIGATION,::ChartGetInteger(this.ID(),CHART_QUICK_NAVIGATION)); // Allow the chart to intercept Space and Enter key strokes to activate the quick navigation bar this.SetProperty(CHART_PROP_SCALE,::ChartGetInteger(this.ID(),CHART_SCALE)); // Scale this.SetProperty(CHART_PROP_SCALEFIX,::ChartGetInteger(this.ID(),CHART_SCALEFIX)); // Fixed scale mode this.SetProperty(CHART_PROP_SCALEFIX_11,::ChartGetInteger(this.ID(),CHART_SCALEFIX_11)); // 1:1 scale mode this.SetProperty(CHART_PROP_SCALE_PT_PER_BAR,::ChartGetInteger(this.ID(),CHART_SCALE_PT_PER_BAR)); // Mode for specifying the scale in points per bar this.SetProperty(CHART_PROP_SHOW_TICKER,::ChartGetInteger(this.ID(),CHART_SHOW_TICKER)); // Display a symbol ticker in the upper left corner this.SetProperty(CHART_PROP_SHOW_OHLC,::ChartGetInteger(this.ID(),CHART_SHOW_OHLC)); // Display OHLC values in the upper left corner this.SetProperty(CHART_PROP_SHOW_BID_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_BID_LINE)); // Display Bid value as a horizontal line on the chart this.SetProperty(CHART_PROP_SHOW_ASK_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_ASK_LINE)); // Display Ask value as a horizontal line on the chart this.SetProperty(CHART_PROP_SHOW_LAST_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_LAST_LINE)); // Display Last value as a horizontal line on the chart this.SetProperty(CHART_PROP_SHOW_PERIOD_SEP,::ChartGetInteger(this.ID(),CHART_SHOW_PERIOD_SEP)); // Display vertical separators between adjacent periods this.SetProperty(CHART_PROP_SHOW_GRID,::ChartGetInteger(this.ID(),CHART_SHOW_GRID)); // Display the chart grid this.SetProperty(CHART_PROP_SHOW_VOLUMES,::ChartGetInteger(this.ID(),CHART_SHOW_VOLUMES)); // Display volumes on the chart this.SetProperty(CHART_PROP_SHOW_OBJECT_DESCR,::ChartGetInteger(this.ID(),CHART_SHOW_OBJECT_DESCR)); // Display text descriptions of the objects this.SetProperty(CHART_PROP_VISIBLE_BARS,::ChartGetInteger(this.ID(),CHART_VISIBLE_BARS)); // Number of bars on a chart that are available for display this.SetProperty(CHART_PROP_WINDOWS_TOTAL,::ChartGetInteger(this.ID(),CHART_WINDOWS_TOTAL)); // The total number of chart windows including indicator subwindows this.SetProperty(CHART_PROP_WINDOW_IS_VISIBLE,::ChartGetInteger(this.ID(),CHART_WINDOW_IS_VISIBLE,0));// Window visibility this.SetProperty(CHART_PROP_WINDOW_HANDLE,::ChartGetInteger(this.ID(),CHART_WINDOW_HANDLE)); // Chart window handle this.SetProperty(CHART_PROP_WINDOW_YDISTANCE,::ChartGetInteger(this.ID(),CHART_WINDOW_YDISTANCE,0)); // Distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window this.SetProperty(CHART_PROP_FIRST_VISIBLE_BAR,::ChartGetInteger(this.ID(),CHART_FIRST_VISIBLE_BAR)); // Number of the first visible bar on the chart this.SetProperty(CHART_PROP_WIDTH_IN_BARS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_BARS)); // Chart width in bars this.SetProperty(CHART_PROP_WIDTH_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_PIXELS)); // Chart width in pixels this.SetProperty(CHART_PROP_HEIGHT_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_HEIGHT_IN_PIXELS,0)); // Chart height in pixels this.SetProperty(CHART_PROP_COLOR_BACKGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_BACKGROUND)); // Chart background color this.SetProperty(CHART_PROP_COLOR_FOREGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_FOREGROUND)); // Color of axes, scale and OHLC line this.SetProperty(CHART_PROP_COLOR_GRID,::ChartGetInteger(this.ID(),CHART_COLOR_GRID)); // Grid color this.SetProperty(CHART_PROP_COLOR_VOLUME,::ChartGetInteger(this.ID(),CHART_COLOR_VOLUME)); // Color of volumes and position opening levels this.SetProperty(CHART_PROP_COLOR_CHART_UP,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_UP)); // Color for the up bar, shadows and body borders of bullish candlesticks this.SetProperty(CHART_PROP_COLOR_CHART_DOWN,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_DOWN)); // Color for the down bar, shadows and body borders of bearish candlesticks this.SetProperty(CHART_PROP_COLOR_CHART_LINE,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_LINE)); // Color of the chart line and the Doji candlesticks this.SetProperty(CHART_PROP_COLOR_CANDLE_BULL,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BULL)); // Color of the bullish candle body this.SetProperty(CHART_PROP_COLOR_CANDLE_BEAR,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BEAR)); // Color of the bearish candle body this.SetProperty(CHART_PROP_COLOR_BID,::ChartGetInteger(this.ID(),CHART_COLOR_BID)); // Bid price line color this.SetProperty(CHART_PROP_COLOR_ASK,::ChartGetInteger(this.ID(),CHART_COLOR_ASK)); // Ask price line color this.SetProperty(CHART_PROP_COLOR_LAST,::ChartGetInteger(this.ID(),CHART_COLOR_LAST)); // Color of the last performed deal's price line (Last) this.SetProperty(CHART_PROP_COLOR_STOP_LEVEL,::ChartGetInteger(this.ID(),CHART_COLOR_STOP_LEVEL)); // Color of stop order levels (Stop Loss and Take Profit) this.SetProperty(CHART_PROP_SHOW_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_SHOW_TRADE_LEVELS)); // Display trade levels on the chart (levels of open positions, Stop Loss, Take Profit and pending orders) this.SetProperty(CHART_PROP_DRAG_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_DRAG_TRADE_LEVELS)); // Enable the ability to drag trading levels on a chart using mouse this.SetProperty(CHART_PROP_SHOW_DATE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_DATE_SCALE)); // Display the time scale on the chart this.SetProperty(CHART_PROP_SHOW_PRICE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_PRICE_SCALE)); // Display the price scale on the chart this.SetProperty(CHART_PROP_SHOW_ONE_CLICK,::ChartGetInteger(this.ID(),CHART_SHOW_ONE_CLICK)); // Display the quick trading panel on the chart this.SetProperty(CHART_PROP_IS_MAXIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MAXIMIZED)); // Chart window maximized this.SetProperty(CHART_PROP_IS_MINIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MINIMIZED)); // Chart window minimized this.SetProperty(CHART_PROP_IS_DOCKED,::ChartGetInteger(this.ID(),CHART_IS_DOCKED)); // Chart window docked this.SetProperty(CHART_PROP_FLOAT_LEFT,::ChartGetInteger(this.ID(),CHART_FLOAT_LEFT)); // Left coordinate of the undocked chart window relative to the virtual screen this.SetProperty(CHART_PROP_FLOAT_TOP,::ChartGetInteger(this.ID(),CHART_FLOAT_TOP)); // Upper coordinate of the undocked chart window relative to the virtual screen this.SetProperty(CHART_PROP_FLOAT_RIGHT,::ChartGetInteger(this.ID(),CHART_FLOAT_RIGHT)); // Right coordinate of the undocked chart window relative to the virtual screen this.SetProperty(CHART_PROP_FLOAT_BOTTOM,::ChartGetInteger(this.ID(),CHART_FLOAT_BOTTOM)); // Bottom coordinate of the undocked chart window relative to the virtual screen //--- Set real properties this.SetProperty(CHART_PROP_SHIFT_SIZE,::ChartGetDouble(this.ID(),CHART_SHIFT_SIZE)); // Shift size of the zero bar from the right border in % this.SetProperty(CHART_PROP_FIXED_POSITION,::ChartGetDouble(this.ID(),CHART_FIXED_POSITION)); // Chart fixed position from the left border in % this.SetProperty(CHART_PROP_FIXED_MAX,::ChartGetDouble(this.ID(),CHART_FIXED_MAX)); // Fixed chart maximum this.SetProperty(CHART_PROP_FIXED_MIN,::ChartGetDouble(this.ID(),CHART_FIXED_MIN)); // Fixed chart minimum this.SetProperty(CHART_PROP_POINTS_PER_BAR,::ChartGetDouble(this.ID(),CHART_POINTS_PER_BAR)); // Scale in points per bar this.SetProperty(CHART_PROP_PRICE_MIN,::ChartGetDouble(this.ID(),CHART_PRICE_MIN)); // Chart minimum this.SetProperty(CHART_PROP_PRICE_MAX,::ChartGetDouble(this.ID(),CHART_PRICE_MAX)); // Chart maximum //--- Set string properties this.SetProperty(CHART_PROP_COMMENT,::ChartGetString(this.ID(),CHART_COMMENT)); // Comment text on the chart this.SetProperty(CHART_PROP_EXPERT_NAME,::ChartGetString(this.ID(),CHART_EXPERT_NAME)); // name of an EA launched on the chart this.SetProperty(CHART_PROP_SCRIPT_NAME,::ChartGetString(this.ID(),CHART_SCRIPT_NAME)); // name of a script launched on the chart this.SetProperty(CHART_PROP_SYMBOL,::ChartSymbol(this.ID())); // Chart symbol this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); int total=this.WindowsTotal(); for(int i=0;i<total;i++) { CChartWnd *wnd=new CChartWnd(m_chart_id,i); if(wnd==NULL) continue; m_list_wnd.Sort(); if(!m_list_wnd.Add(wnd)) delete wnd; } } //+------------------------------------------------------------------+
Para crear las listas de las ventanas del gráfico, obtenemos el número total de todas las ventanas del gráfico, y en un ciclo por todas las ventanas, creamos un nuevo objeto de ventana del gráfico y lo añadimos a la lista.
En el método para comparar dos objetos de gráfico que creamos en el artículo anterior, cometimos un error de lógica: cada gráfico tiene su identificador único del gráfico y su propio manejador de ventana. Por eso, la comparación de estas propiedades en dos gráficos distintos siempre retornará false. Sin embargo, el objetivo del método es comprobar si dos gráficos distintos son idénticos, mientras que la comparación de sus identificadores y manejadores siempre mostrará que no son idénticos.
Vamos a corregir el error, omitiendo estas dos propiedades al realizar la comparación:
//+------------------------------------------------------------------+ //| Compare the CChartObj objects by all properties | //+------------------------------------------------------------------+ bool CChartObj::IsEqual(CChartObj *compared_obj) const { int beg=0, end=CHART_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_INTEGER prop=(ENUM_CHART_PROP_INTEGER)i; if(prop==CHART_PROP_ID || prop==CHART_PROP_WINDOW_HANDLE) continue; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=CHART_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_DOUBLE prop=(ENUM_CHART_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=CHART_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_STRING prop=(ENUM_CHART_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
En el método que retorna la descripción de una propiedad de tipo entero del objeto, añadimos el retorno de la descripción de las tres propiedades añadidas:
//+------------------------------------------------------------------+ //| Return description of object's integer property | //+------------------------------------------------------------------+ string CChartObj::GetPropertyDescription(ENUM_CHART_PROP_INTEGER property) { return ( property==CHART_PROP_ID ? CMessage::Text(MSG_CHART_OBJ_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BAR_PERIOD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(property)) ) : property==CHART_PROP_SHOW ? CMessage::Text(MSG_CHART_OBJ_SHOW)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_IS_OBJECT ? CMessage::Text(MSG_CHART_OBJ_IS_OBJECT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_BRING_TO_TOP ? CMessage::Text(MSG_CHART_OBJ_BRING_TO_TOP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_CONTEXT_MENU ? CMessage::Text(MSG_CHART_OBJ_CONTEXT_MENU)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_CROSSHAIR_TOOL ? CMessage::Text(MSG_CHART_OBJ_CROSSHAIR_TOOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_MOUSE_SCROLL ? CMessage::Text(MSG_CHART_OBJ_MOUSE_SCROLL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_EVENT_MOUSE_WHEEL ? CMessage::Text(MSG_CHART_OBJ_EVENT_MOUSE_WHEEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_EVENT_MOUSE_MOVE ? CMessage::Text(MSG_CHART_OBJ_EVENT_MOUSE_MOVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_EVENT_OBJECT_CREATE ? CMessage::Text(MSG_CHART_OBJ_EVENT_OBJECT_CREATE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_EVENT_OBJECT_DELETE ? CMessage::Text(MSG_CHART_OBJ_EVENT_OBJECT_DELETE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_MODE ? CMessage::Text(MSG_CHART_OBJ_MODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ChartModeDescription((ENUM_CHART_MODE)this.GetProperty(property)) ) : property==CHART_PROP_FOREGROUND ? CMessage::Text(MSG_CHART_OBJ_FOREGROUND)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHIFT ? CMessage::Text(MSG_CHART_OBJ_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_AUTOSCROLL ? CMessage::Text(MSG_CHART_OBJ_AUTOSCROLL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_KEYBOARD_CONTROL ? CMessage::Text(MSG_CHART_OBJ_KEYBOARD_CONTROL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_QUICK_NAVIGATION ? CMessage::Text(MSG_CHART_OBJ_QUICK_NAVIGATION)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SCALE ? CMessage::Text(MSG_CHART_OBJ_SCALE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_SCALEFIX ? CMessage::Text(MSG_CHART_OBJ_SCALEFIX)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SCALEFIX_11 ? CMessage::Text(MSG_CHART_OBJ_SCALEFIX_11)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SCALE_PT_PER_BAR ? CMessage::Text(MSG_CHART_OBJ_SCALE_PT_PER_BAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_TICKER ? CMessage::Text(MSG_CHART_OBJ_SHOW_TICKER)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_OHLC ? CMessage::Text(MSG_CHART_OBJ_SHOW_OHLC)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_BID_LINE ? CMessage::Text(MSG_CHART_OBJ_SHOW_BID_LINE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_ASK_LINE ? CMessage::Text(MSG_CHART_OBJ_SHOW_ASK_LINE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_LAST_LINE ? CMessage::Text(MSG_CHART_OBJ_SHOW_LAST_LINE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_PERIOD_SEP ? CMessage::Text(MSG_CHART_OBJ_SHOW_PERIOD_SEP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_GRID ? CMessage::Text(MSG_CHART_OBJ_SHOW_GRID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_VOLUMES ? CMessage::Text(MSG_CHART_OBJ_SHOW_VOLUMES)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ChartModeVolumeDescription((ENUM_CHART_VOLUME_MODE)this.GetProperty(property)) ) : property==CHART_PROP_SHOW_OBJECT_DESCR ? CMessage::Text(MSG_CHART_OBJ_SHOW_OBJECT_DESCR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_VISIBLE_BARS ? CMessage::Text(MSG_CHART_OBJ_VISIBLE_BARS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_WINDOWS_TOTAL ? CMessage::Text(MSG_CHART_OBJ_WINDOWS_TOTAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_WINDOW_IS_VISIBLE ? CMessage::Text(MSG_CHART_OBJ_WINDOW_IS_VISIBLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_WINDOW_HANDLE ? CMessage::Text(MSG_CHART_OBJ_WINDOW_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_WINDOW_YDISTANCE ? CMessage::Text(MSG_CHART_OBJ_WINDOW_YDISTANCE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_FIRST_VISIBLE_BAR ? CMessage::Text(MSG_CHART_OBJ_FIRST_VISIBLE_BAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_WIDTH_IN_BARS ? CMessage::Text(MSG_CHART_OBJ_WIDTH_IN_BARS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_WIDTH_IN_PIXELS ? CMessage::Text(MSG_CHART_OBJ_WIDTH_IN_PIXELS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_HEIGHT_IN_PIXELS ? CMessage::Text(MSG_CHART_OBJ_HEIGHT_IN_PIXELS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_COLOR_BACKGROUND ? CMessage::Text(MSG_CHART_OBJ_COLOR_BACKGROUND)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_FOREGROUND ? CMessage::Text(MSG_CHART_OBJ_COLOR_FOREGROUND)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_GRID ? CMessage::Text(MSG_CHART_OBJ_COLOR_GRID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_VOLUME ? CMessage::Text(MSG_CHART_OBJ_COLOR_VOLUME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_CHART_UP ? CMessage::Text(MSG_CHART_OBJ_COLOR_CHART_UP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_CHART_DOWN ? CMessage::Text(MSG_CHART_OBJ_COLOR_CHART_DOWN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_CHART_LINE ? CMessage::Text(MSG_CHART_OBJ_COLOR_CHART_LINE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_CANDLE_BULL ? CMessage::Text(MSG_CHART_OBJ_COLOR_CANDLE_BULL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_CANDLE_BEAR ? CMessage::Text(MSG_CHART_OBJ_COLOR_CANDLE_BEAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_BID ? CMessage::Text(MSG_CHART_OBJ_COLOR_BID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_ASK ? CMessage::Text(MSG_CHART_OBJ_COLOR_ASK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_LAST ? CMessage::Text(MSG_CHART_OBJ_COLOR_LAST)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_COLOR_STOP_LEVEL ? CMessage::Text(MSG_CHART_OBJ_COLOR_STOP_LEVEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CHART_PROP_SHOW_TRADE_LEVELS ? CMessage::Text(MSG_CHART_OBJ_SHOW_TRADE_LEVELS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_DRAG_TRADE_LEVELS ? CMessage::Text(MSG_CHART_OBJ_DRAG_TRADE_LEVELS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_DATE_SCALE ? CMessage::Text(MSG_CHART_OBJ_SHOW_DATE_SCALE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_PRICE_SCALE ? CMessage::Text(MSG_CHART_OBJ_SHOW_PRICE_SCALE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_SHOW_ONE_CLICK ? CMessage::Text(MSG_CHART_OBJ_SHOW_ONE_CLICK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_IS_MAXIMIZED ? CMessage::Text(MSG_CHART_OBJ_IS_MAXIMIZED)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_IS_MINIMIZED ? CMessage::Text(MSG_CHART_OBJ_IS_MINIMIZED)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_IS_DOCKED ? CMessage::Text(MSG_CHART_OBJ_IS_DOCKED)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_FLOAT_LEFT ? CMessage::Text(MSG_CHART_OBJ_FLOAT_LEFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_FLOAT_TOP ? CMessage::Text(MSG_CHART_OBJ_FLOAT_TOP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_FLOAT_RIGHT ? CMessage::Text(MSG_CHART_OBJ_FLOAT_RIGHT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_FLOAT_BOTTOM ? CMessage::Text(MSG_CHART_OBJ_FLOAT_BOTTOM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
Los bloques del código añadido son idénticos a todos los demás: dependiendo de la propiedad transmitida al método, se creará y retornará una línea con la descripción de esta propiedad.
Vamos a mejorar el método que muestra en el diario todas las propiedades del objeto:
//+------------------------------------------------------------------+ //| Display object properties in the journal | //+------------------------------------------------------------------+ void CChartObj::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=CHART_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_INTEGER prop=(ENUM_CHART_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; if(prop==CHART_PROP_WINDOW_IND_HANDLE || prop==CHART_PROP_WINDOW_IND_INDEX) continue; if(prop==CHART_PROP_HEIGHT_IN_PIXELS) { this.PrintWndParameters(); continue; } ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=CHART_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_DOUBLE prop=(ENUM_CHART_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=CHART_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_CHART_PROP_STRING prop=(ENUM_CHART_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; if(prop==CHART_PROP_INDICATOR_NAME) { this.PrintWndIndicators(); continue; } ::Print(this.GetPropertyDescription(prop)); } ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } //+------------------------------------------------------------------+
Si encontramos en el ciclo las propiedades "manejador del indicador" e "índice del indicador en la ventana", estas propiedades se omitirán: no estarán relacionadas con el objeto de gráfico.
Si encontramos la propiedad "altura del gráfico en píxeles", llamaremos al método que muestra la descripción de esta propiedad para todas las ventanas de este gráfico.
Exactamente de la misma forma, si encontramos la propiedad "nombre del indicador en la ventana", se llamará al método que muestra la descripción de todos los indicadodres fijados a todas las ventanas del gráfico. Veremos estos métodos más tarde.
El método que muestra en el diario la descripción breve del objeto también ha sido mejorado.
Ahora, muestra adicionalmente el número de subventanas de la ventana del gráfico, si las hay, o bien comunica que no hay ninguna:
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CChartObj::PrintShort(const bool dash=false) { ::Print ( (dash ? "- " : ""),this.Header()," ID: ",(string)this.ID(),", HWND: ",(string)this.Handle(), ", ",CMessage::Text(MSG_CHART_OBJ_CHART_SUBWINDOWS_NUM),": ",(this.WindowsTotal()>1 ? string(this.WindowsTotal()-1) : CMessage::Text(MSG_LIB_TEXT_NO)) ); } //+------------------------------------------------------------------+
Método que muestra en el diario los datos de todos los indicadores de todas las ventanas del gráfico:
//+-------------------------------------------------------------------+ //| Display data of all indicators of all chart windows in the journal| //+-------------------------------------------------------------------+ void CChartObj::PrintWndIndicators(void) { for(int i=0;i<this.WindowsTotal();i++) { CChartWnd *wnd=m_list_wnd.At(i); if(wnd==NULL) continue; wnd.PrintIndicators(true); } } //+------------------------------------------------------------------+
Aquí, en un ciclo por todos los objetos de ventana del gráfico, obtenemos el siguiente objeto de ventana del gráfico e imprimimos en el diario la descripción de todos los indicadores fijados a esta ventana. Transmitimos al método true para mostrar un guión antes de la descripción del indicador.
Método que muestra en el diario las propiedades de todas las ventanas del gráfico:
//+------------------------------------------------------------------+ //| Display the properties of all chart windows in the journal | //+------------------------------------------------------------------+ void CChartObj::PrintWndParameters(void) { for(int i=0;i<this.WindowsTotal();i++) { CChartWnd *wnd=m_list_wnd.At(i); if(wnd==NULL) continue; wnd.PrintParameters(true); } } //+------------------------------------------------------------------+
Aquí, en un ciclo por todos los objetos de ventana del gráfico, obtenemos el siguiente objeto de ventana del gráfico e imprimimos en el diario la descripción de sus parámetros.. Transmitimos al método true para mostrar un guión antes de la descripción de la ventana.
Método que retorna un objeto de ventana según su número de subventana:
//+------------------------------------------------------------------+ //| Return the window object by its subwindow index | //+------------------------------------------------------------------+ CChartWnd *CChartObj::GetWindowByNum(const int win_num) const { int total=m_list_wnd.Total(); for(int i=0;i<total;i++) { CChartWnd *wnd=m_list_wnd.At(i); if(wnd==NULL) continue; if(wnd.WindowNum()==win_num) return wnd; } return NULL; } //+------------------------------------------------------------------+
Aquí, en un ciclo por el número total de objetos de ventana del gráfico, obtenemos el siguiente objeto de ventana y si su número coincide con el transmitido al método, retornamos el puntero al objeto encontrado en la lista. Al finalizar el ciclo, retornamos NULL: el objeto no ha sido encontrado.
Método que establece la propiedad "visibilidad de la subventana" para un objeto de gráfico:
//+------------------------------------------------------------------+ //| Set the "Subwindow visibility" property | //+------------------------------------------------------------------+ void CChartObj::SetVisible(void) { this.SetProperty(CHART_PROP_WINDOW_IS_VISIBLE,::ChartGetInteger(this.ID(),CHART_WINDOW_IS_VISIBLE,0)); } //+------------------------------------------------------------------+
Aquí, solo tenemos que escribir en la propiedad indicada la propiedad read/only correspondiente de la ventana principal del gráfico.
Método que retorna la distancia en píxeles a lo largo del eje vertical Y entre el marco superior de la subventana del indicador y el marco superior de la ventana principal del gráfico:
//+-----------------------------------------------------------------------------------+ //| Return the distance in pixels by Y axis between | //| the upper frame of the indicator subwindow and the upper frame of the main window | //+-----------------------------------------------------------------------------------+ int CChartObj::WindowYDistance(const int sub_window) const { CChartWnd *wnd=GetWindowByNum(sub_window); return(wnd!=NULL ? wnd.YDistance() : WRONG_VALUE); } //+------------------------------------------------------------------+
Aquí, obtenemos el objeto de ventana del gráfico según su número de subventana y retornamos el valor de la propiedad del objeto obtenido.
Si el objeto no se ha obtenido, retornaremos -1.
Método que retorna la altura del gráfico indicado en píxeles:
//+------------------------------------------------------------------+ //| Return the height of the specified chart in pixels | //+------------------------------------------------------------------+ int CChartObj::WindowHeightInPixels(const int sub_window) const { CChartWnd *wnd=GetWindowByNum(sub_window); return(wnd!=NULL ? wnd.HeightInPixels() : WRONG_VALUE); } //+------------------------------------------------------------------+
Aquí, obtenemos el objeto de ventana del gráfico según su número de subventana y retornamos el valor de la propiedad del objeto obtenido.
Si el objeto no se ha obtenido, retornaremos -1
Método que establece la altura del gráfico indicado en píxeles:
//+------------------------------------------------------------------+ //| Set the height of the specified chart in pixels | //+------------------------------------------------------------------+ bool CChartObj::SetWindowHeightInPixels(const int height,const int sub_window,const bool redraw=false) { CChartWnd *wnd=GetWindowByNum(sub_window); return(wnd!=NULL ? wnd.SetHeightInPixels(height,redraw) : false); } //+------------------------------------------------------------------+
Aquí, obtenemos el objeto de ventana del gráfico según su número de subventana y retornamos el valor de la configuración de la propiedad correspondiente del objeto obtenido.
Si el objeto no se ha obtenido, retornaremos false.
Vamos a eliminar el método antiguo que escribimos en el artículo anterior:
//+------------------------------------------------------------------+
//| Set the height of the specified chart in pixels |
//+------------------------------------------------------------------+
bool CChartObj::SetWindowHeightInPixels(const int height,const int sub_window,const bool redraw=false)
{
::ResetLastError();
if(!::ChartSetInteger(this.ID(),CHART_HEIGHT_IN_PIXELS,sub_window,height))
{
CMessage::ToLog(DFUN,::GetLastError(),true);
return false;
}
if(redraw)
::ChartRedraw(this.ID());
return true;
}
//+------------------------------------------------------------------+
Con esto, hemos finalizado la mejora de la clase del objeto de gráfico.
Simulación
Para comprobar el rendimiento de los objetos creados, simplemente abriremos tres gráficos. En la ventana principal del gráfico con el asesor, añadimos un indicador de fractales + la ventana de un indicador, por ejemplo, DeMarker, en el que ubicaremos otro más, por ejemplo, AMA, calculado con los datos de DeMarker.
En el segundo gráfico, colocaremos una ventana estocástica, mientras que la tercera ventana será desacoplable (Alt + D):
En el diario, mostraremos las descripciones breves de los tres objetos del gráfico y la descripción completa del gráfico actual en el que se encuentra el asesor.
Para la prueba, tomaremos el asesor del artículo anterior y
lo guardaremos en la carpeta \MQL5\Experts\TestDoEasy\Part68\ con el nuevo nombre TestDoEasyPart68.mq5.
El asesor permanecerá prácticamente sin cambios. Todo lo que necesitamos hacer es completar el código del manejador OnTick() con esta lógica:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Handle the NewTick event in the library engine.OnTick(rates_data); //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the timer PressButtonsControl(); // Button pressing control engine.EventsHandling(); // Working with events } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing pending orders } //--- If it is the first launch static bool done=false; if(!done) { //--- Create the list object for storing chart objects CArrayObj *list=new CArrayObj(); if(list==NULL) return; //--- Declare the variables and get the first chart ID long currChart,prevChart=ChartFirst(); int i=0; //--- Create the chart object and add it to the list CChartObj *chart_first=new CChartObj(prevChart); list.Add(chart_first); //--- In the loop by the total number of terminal charts (not more than 100) while(i<CHARTS_MAX) { //--- based on the previous one, get the new chart currChart=ChartNext(prevChart); //--- When reaching the end of the chart list, complete the loop if(currChart<0) break; //--- Create the chart object based on the current chart ID in the loop and add it to the list CChartObj *chart=new CChartObj(currChart); list.Add(chart); //--- remember the current chart ID for ChartNext() and increase the loop counter prevChart=currChart; i++; } Print(""); //--- From the filled list in the loop, receive the next chart object and display its short description int total=list.Total(); for(int j=0;j<total;j++) { CChartObj *chart_obj=list.At(j); if(chart_obj!=NULL) chart_obj.PrintShort(); } Print(""); //--- Display the full description of the current chart: in the loop by all objects of the created list for(int j=0;j<total;j++) { //--- get the next chart object and CChartObj *chart_obj=list.At(j); //--- if its symbol matches the current chart symbol, display its full description in the journal if(chart_obj!=NULL && chart_obj.Symbol()==Symbol()) chart_obj.Print(); } //--- Destroy the list of chart objects delete list; done=true; } //--- } //+------------------------------------------------------------------+
Compilamos el asesor y lo iniciamos en el gráfico, creando previamente en el terminal el entorno necesario descrito al principio de este apartado.
En el diario se mostrarán las descripciones breves de los tres gráficos abiertos:
Main chart window EURUSD H4 ID: 131733844391938630, HWND: 5179646, Subwindows: 1 Main chart window AUDUSD H4 ID: 131733844391938634, HWND: 3672036, Subwindows: 1 Main chart window GBPUSD H4 ID: 131733844391938633, HWND: 3473910, Subwindows: No
y la descripción completa del actual. El resultado del funcionamiento de las clases creadas hoy se representa en el diario como líneas ordenadas de las propiedades de los objetos de ventana y los indicadores fijados a ellas:
============= The beginning of the parameter list (Main chart window EURUSD H4) ============= Chart ID: 131733844391938630 Timeframe: H4 Drawing attributes of a price chart: Yes Object "Chart": No Chart on top of other charts: No Accessing the context menu by pressing the right mouse button: Yes Accessing the "Crosshair tool" by pressing the middle mouse button: Yes Scrolling the chart horizontally using the left mouse button: Yes Sending messages about mouse wheel events to all mql5 programs on a chart: No Send notifications of mouse move and mouse click events to all mql5 programs on a chart: No Send a notification of an event of new object creation to all mql5-programs on a chart: No Send a notification of an event of object deletion to all mql5-programs on a chart: No Chart type: Display as Japanese candlesticks Price chart in the foreground: No Price chart indent from the right border: Yes Automatic moving to the right border of the chart: Yes Managing the chart using a keyboard: Yes Allowed to intercept Space and Enter key presses on the chart to activate the quick navigation bar: Yes Scale: 2 Fixed scale mode: No Scale 1:1 mode: No Scale to be specified in points per bar: No Display a symbol ticker in the upper left corner: Yes Display OHLC values in the upper left corner: Yes Display Bid values as a horizontal line in a chart: Yes Display Ask values as a horizontal line in a chart: Yes Display Last values as a horizontal line in a chart: No Display vertical separators between adjacent periods: No Display grid in the chart: No Display volume in the chart: Trade volumes Display textual descriptions of objects: Yes The number of bars on the chart that can be displayed: 137 The total number of chart windows, including indicator subwindows: 2 Visibility of subwindow: Yes Chart window handle: 5179646 Number of the first visible bar in the chart: 136 Chart width in bars: 168 Chart width in pixels: 670 Main chart window: - Chart height in pixels: 301 Chart subwindow 1: - The distance between the upper frame of the indicator subwindow and the upper frame of the main chart window: 303 - Chart height in pixels: 13 Chart background color: clrWhite Color of axes, scales and OHLC line: clrBlack Grid color: clrSilver Color of volumes and position opening levels: clrGreen Color for the up bar, shadows and body borders of bull candlesticks: clrBlack Color for the down bar, shadows and body borders of bear candlesticks: clrBlack Line chart color and color of "Doji" Japanese candlesticks: clrBlack Body color of a bull candlestick: clrWhite Body color of a bear candlestick: clrBlack Bid price level color: clrLightSkyBlue Ask price level color: clrCoral Line color of the last executed deal price (Last): clrSilver Color of stop order levels (Stop Loss and Take Profit): clrOrangeRed Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders): Yes Permission to drag trading levels on a chart with a mouse: Yes Showing the time scale on a chart: Yes Showing the price scale on a chart: Yes Showing the "One click trading" panel on a chart: No Chart window is maximized: Yes Chart window is minimized: No The chart window is docked: Yes The left coordinate of the undocked chart window relative to the virtual screen: 0 The top coordinate of the undocked chart window relative to the virtual screen: 0 The right coordinate of the undocked chart window relative to the virtual screen: 0 The bottom coordinate of the undocked chart window relative to the virtual screen: 0 ------ The size of the zero bar indent from the right border in percents: 18.93 Chart fixed position from the left border in percent value: 0.00 Fixed chart maximum: 1.22620 Fixed chart minimum : 1.17940 Scale in points per bar: 1.00 Chart minimum: 1.17940 Chart maximum: 1.22620 ------ Text of a comment in a chart: "" The name of the Expert Advisor running on the chart: "TestDoEasyPart68" The name of the script running on the chart: "" Indicators in the main chart window: - Indicator Fractals Indicators in the chart window 1: - Indicator DeM(14) - Indicator AMA(14,2,30) Symbol: "EURUSD" ============= End of the parameter list (Main chart window EURUSD H4) =============
¿Qué es lo próximo?
En el próximo artículo, comenzaremos a desarrollar la clase de colección de objetos de gráfico.
Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y el archivo del asesor de prueba para MQL5. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Querríamos señalar que no resulta recomendable usar los objetos de gráfico en desarrollos propios en su estado actual, debido a los cambios adicionales que sufrirán más adelante.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.
*Artículos de esta serie:
Trabajando con los precios en la biblioteca DoEasy (Parte 62): Actualización de las series de tick en tiempo real, preparando para trabajar con la Profundidad del mercado
Trabajando con los precios en la biblioteca DoEasy (Parte 63): Profundidad del mercado, clase de orden abstracta de la Profundidad del mercado
Trabajando con los precios en la biblioteca DoEasy (Parte 64): Profundidad del mercado, clases del objeto de instantánea y del objeto de serie de instantáneas del DOM
Trabajando con los precios y Señales en la biblioteca DoEasy (Parte 65): Colección de la profundidad de mercado y clase para trabajar con las Señales MQL5.com
Otras clases en la biblioteca DoEasy (Parte 66): Clases de Colección de Señales MQL5.com
Otras clases en la biblioteca DoEasy (Parte 67): Clase de objeto de gráfico
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/9236
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso