![Gráficos en la biblioteca DoEasy (Parte 82): Refactorización de los objetos de la biblioteca y colección de objetos gráficos](https://c.mql5.com/i/articles/overlay.png)
Gráficos en la biblioteca DoEasy (Parte 82): Refactorización de los objetos de la biblioteca y colección de objetos gráficos
Contenido
- Concepto
- Mejorando las clases de la biblioteca
- Clase de colección de objetos gráficos
- Simulación
- ¿Qué es lo próximo?
Concepto
En el último artículo, comenzamos a integrar gráficos en los objetos de biblioteca. Cada uno de los objetos de la biblioteca poseerá su propia instancia del objeto de gestión de objetos gráficos, lo cual le permitirá construir los objetos gráficos que le pertenezcan (tanto estándar, como basados en CCanvas).
Para integrar gráficos en los objetos de la biblioteca, ya en el último artículo, comenzamos a mejorar el objeto de barra, construyendo en él una clase de gestión de gráficos. Después de realizar la depuración, añadiremos el mecanismo creado y depurado para trabajar con los gráficos en otros objetos.
Y aquí, nos encontramos con la necesidad de mejorar todos los objetos de la biblioteca. En este sentido, necesitaremos asegurarnos de que cada objeto tenga su propio identificador único, Type(), mediante el cual podremos determinar qué tipo de objeto es. El asunto es que cada objeto de biblioteca debe poder crear objetos gráficos, y el objeto gráfico debe saber "quién" lo ha creado. Después de crear un objeto gráfico con cualquier objeto de la biblioteca, el objeto gráfico deberá saber quién lo ha creado y tener el puntero a su objeto padre. A su vez, el objeto padre deberá conocer los objetos gráficos que ha generado, y también tener los punteros a ellos.
Al mismo tiempo, al crear un objeto gráfico, deberemos introducirlo en una lista de colección de objetos gráficos única. Para todos los objetos gráficos, necesitaremos una nueva propiedad: la pertenencia del objeto. Utilizando esta, podremos determinar cómo se ha creado un objeto gráfico, ya sea mediante un programa o manualmente en el terminal. En este caso, además, aquellos objetos que hemos creado utilizando la biblioteca del programa se añadirán a la lista inmediatamente tras su creación. No obstante, los gráficos creados por el terminal (varios objetos gráficos añadidos manualmente al gráfico) deberán ser monitoreados por la clase de colección de objetos gráficos, y añadidos o eliminados de la lista. Además, deberemos crear para ellos objetos de programa separados de la clase de objeto gráfico, de forma que nuestro programa también pueda gestionarlos.
Para hacer esto, necesitaremos implementar en la clase de colección de objetos gráficos el seguimiento del estado de todas las ventanas de gráficos abiertas en el terminal: la aparición de los objetos gráficos estándar en ellas, la creación de objetos de la biblioteca sobre su base y su introducción en la lista de colección. Lo mismo sucederá con la eliminación de los objetos gráficos estándar.
De esta forma, nuestra biblioteca podrá asumir finalmente el control de todos los objetos gráficos estándar presentes en los gráficos abiertos, para luego trabajar con ellos como si fueran suyos, pero sin olvidar que han sido creados manualmente.
No todo lo expuesto se podrá implementar hoy: hemos descrito la base para varios artículos que tenemos por delante.
No obstante, hoy mejoraremos todos los objetos de la biblioteca. En esa línea, les asignaremos nuestros propios tipos y trabajaremos con la clase de colección de objetos gráficos, organizando el seguimiento de la creación de nuevos objetos gráficos y la eliminación de objetos existentes en los gráficos abiertos en el terminal.
Mejorando las clases de la biblioteca
Vamos a comenzar introduciendo las nuevas macrosustituciones y enumeraciones en el archivo \MQL5\Include\DoEasy\Defines.mqh. Necesitamos añadir los tipos de todos los objetos de la biblioteca, para que estos valores se puedan escribir en la propiedad "tipo" del objeto inmediatamente después de su creación.
Pero primero, vamos a añadir las macrosustituciones para indicar los parámetros del temporizador de la colección de objetos gráficos al final de la lista de parámetros del temporizador para las colecciones ya existentes:
//--- Parameters of the chart collection timer #define COLLECTION_CHARTS_PAUSE (500) // Chart collection timer pause in milliseconds #define COLLECTION_CHARTS_COUNTER_STEP (16) // Chart timer counter increment #define COLLECTION_CHARTS_COUNTER_ID (9) // Chart timer counter ID //--- Parameters of the graphical objects collection timer #define COLLECTION_GRAPH_OBJ_PAUSE (250) // Graphical objects collection timer pause in milliseconds #define COLLECTION_GRAPH_OBJ_COUNTER_STEP (16) // Graphical objects timer counter increment #define COLLECTION_GRAPH_OBJ_COUNTER_ID (10) // Graphical objects timer counter ID //--- Collection list IDs
Ya tenemos escritos en este archivo los identificadores de las listas de colección.
Lo lógico es continuar esta lista, pero usando los identificadores como tipos de objeto. Sin embargo, como vamos a continuar añadiendo nuevas colecciones de objetos, y la lista de sus identificadores se ampliará, necesitaremos agregar una etiqueta determinada desde cuyo valor partirán los valores de los tipos de objeto, usando ya para ellos el valor de esta etiqueta como inicial:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID #define COLLECTION_BUFFERS_ID (0x7780) // Indicator buffer collection list ID #define COLLECTION_INDICATORS_ID (0x7781) // Indicator collection list ID #define COLLECTION_INDICATORS_DATA_ID (0x7782) // Indicator data collection list ID #define COLLECTION_TICKSERIES_ID (0x7783) // Tick series collection list ID #define COLLECTION_MBOOKSERIES_ID (0x7784) // DOM series collection list ID #define COLLECTION_MQL5_SIGNALS_ID (0x7785) // MQL5 signals collection list ID #define COLLECTION_CHARTS_ID (0x7786) // Chart collection list ID #define COLLECTION_CHART_WND_ID (0x7787) // Chart window list ID #define COLLECTION_GRAPH_OBJ_ID (0x7788) // Graphical object collection list ID #define COLLECTION_ID_LIST_END (COLLECTION_GRAPH_OBJ_ID) // End of collection ID list //--- Pending request type IDs
Es decir, el valor de esta etiqueta + 1 será para nosotros el valor para la primera constante de enumeración de los tipos de objeto de la biblioteca, que añadiremos ahora:
//--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define NULL_COLOR (0x00FFFFFF) // Zero for the canvas with the alpha channel #define OUTER_AREA_SIZE (16) // Size of one side of the outer area around the workspace //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of library object types | //+------------------------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { //--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+1, // "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, // "Graphical element" object type OBJECT_DE_TYPE_GFORM, // Form object type OBJECT_DE_TYPE_GSHADOW, // Shadow object type //--- Animation OBJECT_DE_TYPE_GFRAME, // "Single animation frame" object type OBJECT_DE_TYPE_GFRAME_TEXT, // "Single text animation frame" object type OBJECT_DE_TYPE_GFRAME_QUAD, // "Single rectangular animation frame" object type OBJECT_DE_TYPE_GFRAME_GEOMETRY, // "Single geometric animation frame" object type OBJECT_DE_TYPE_GANIMATIONS, // "Animations" object type //--- Managing graphical objects OBJECT_DE_TYPE_GELEMENT_CONTROL, // "Managing graphical objects" object type //--- Standard graphical objects OBJECT_DE_TYPE_GSTD_VLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE, // "Vertical line" object type OBJECT_DE_TYPE_GSTD_HLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_HLINE, // "Horizontal line" object type OBJECT_DE_TYPE_GSTD_TREND = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TREND, // "Trend line" object type OBJECT_DE_TYPE_GSTD_TRENDBYANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRENDBYANGLE, // "Trend line by angle" object type OBJECT_DE_TYPE_GSTD_CYCLES = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CYCLES, // "Cyclic lines" object type OBJECT_DE_TYPE_GSTD_ARROWED_LINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROWED_LINE, // "Arrowed line" object type OBJECT_DE_TYPE_GSTD_CHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHANNEL, // "Equidistant channel" object type OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_STDDEVCHANNEL, // "Standard deviation channel" object type OBJECT_DE_TYPE_GSTD_REGRESSION = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_REGRESSION, // "Linear regression channel" object type OBJECT_DE_TYPE_GSTD_PITCHFORK = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_PITCHFORK, // "Andrews' pitchfork" object type OBJECT_DE_TYPE_GSTD_GANNLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNLINE, // "Gann line" object type OBJECT_DE_TYPE_GSTD_GANNFAN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNFAN, // "Gann fan" object type OBJECT_DE_TYPE_GSTD_GANNGRID = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNGRID, // "Gann grid" object type OBJECT_DE_TYPE_GSTD_FIBO = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBO, // "Fibo levels" object type OBJECT_DE_TYPE_GSTD_FIBOTIMES = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOTIMES, // "Fibo time zones" object type OBJECT_DE_TYPE_GSTD_FIBOFAN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOFAN, // "Fibo fan" object type OBJECT_DE_TYPE_GSTD_FIBOARC = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOARC, // "Fibo arcs" object type OBJECT_DE_TYPE_GSTD_FIBOCHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOCHANNEL, // "Fibo channel" object type OBJECT_DE_TYPE_GSTD_EXPANSION = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EXPANSION, // "Fibo expansion" object type OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5 = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE5, // "Elliott 5 waves" object type OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3 = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE3, // "Elliott 3 waves" object type OBJECT_DE_TYPE_GSTD_RECTANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE, // "Rectangle" object type OBJECT_DE_TYPE_GSTD_TRIANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRIANGLE, // "Triangle" object type OBJECT_DE_TYPE_GSTD_ELLIPSE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIPSE, // "Ellipse" object type OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_UP, // "Thumb up" object type OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_DOWN, // "Thumb down" object type OBJECT_DE_TYPE_GSTD_ARROW_UP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_UP, // "Arrow up" object type OBJECT_DE_TYPE_GSTD_ARROW_DOWN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_DOWN, // "Arrow down" object type OBJECT_DE_TYPE_GSTD_ARROW_STOP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_STOP, // "Stop sign" object type OBJECT_DE_TYPE_GSTD_ARROW_CHECK = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_CHECK, // "Check mark" object type OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_LEFT_PRICE, // "Left price label" object type OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_RIGHT_PRICE, // "Right price label" object type OBJECT_DE_TYPE_GSTD_ARROW_BUY = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_BUY, // "Buy sign" object type OBJECT_DE_TYPE_GSTD_ARROW_SELL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_SELL, // "Sell sign" object type OBJECT_DE_TYPE_GSTD_ARROW = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW, // "Arrow" object type OBJECT_DE_TYPE_GSTD_TEXT = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TEXT, // "Text" object type OBJECT_DE_TYPE_GSTD_LABEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_LABEL, // "Text label" object type OBJECT_DE_TYPE_GSTD_BUTTON = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BUTTON, // "Button" object type OBJECT_DE_TYPE_GSTD_CHART = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHART, // "Chart" object type OBJECT_DE_TYPE_GSTD_BITMAP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP, // "Bitmap" object type OBJECT_DE_TYPE_GSTD_BITMAP_LABEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP_LABEL, // "Bitmap label" object type OBJECT_DE_TYPE_GSTD_EDIT = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EDIT, // "Input field" object type OBJECT_DE_TYPE_GSTD_EVENT = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EVENT, // "Event object which corresponds to an event in Economic Calendar" object type OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE_LABEL, // "Rectangle Label object used to create and design the custom graphical interface" object type //--- Objects OBJECT_DE_TYPE_BASE = OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Base object for all library objects OBJECT_DE_TYPE_BASE_EXT, // Extended base object for all library objects OBJECT_DE_TYPE_ACCOUNT, // "Account" object type OBJECT_DE_TYPE_BOOK_ORDER, // "Book order" object type OBJECT_DE_TYPE_BOOK_BUY, // "Book buy order" object type OBJECT_DE_TYPE_BOOK_BUY_MARKET, // "Book buy order at market price" object type OBJECT_DE_TYPE_BOOK_SELL, // "Book sell order" object type OBJECT_DE_TYPE_BOOK_SELL_MARKET, // "Book sell order at market price" object type OBJECT_DE_TYPE_BOOK_SNAPSHOT, // "Book snapshot" object type OBJECT_DE_TYPE_BOOK_SERIES, // "Book snapshot series" object type OBJECT_DE_TYPE_CHART, // "Chart" object type OBJECT_DE_TYPE_CHART_WND, // "Chart window" object type OBJECT_DE_TYPE_CHART_WND_IND, // "Chart window indicator" object type OBJECT_DE_TYPE_EVENT, // "Event" object type OBJECT_DE_TYPE_EVENT_BALANCE, // "Balance operation event" object type OBJECT_DE_TYPE_EVENT_MODIFY, // "Pending order/position modification event" object type OBJECT_DE_TYPE_EVENT_ORDER_PLASED, // "Placing a pending order event" object type OBJECT_DE_TYPE_EVENT_ORDER_REMOVED, // "Pending order removal event" object type OBJECT_DE_TYPE_EVENT_POSITION_CLOSE, // "Position closure event" object type OBJECT_DE_TYPE_EVENT_POSITION_OPEN, // "Position opening event" object type OBJECT_DE_TYPE_IND_BUFFER, // "Indicator buffer" object type OBJECT_DE_TYPE_IND_BUFFER_ARROW, // "Arrow rendering buffer" object type OBJECT_DE_TYPE_IND_BUFFER_BAR, // "Bar buffer" object type OBJECT_DE_TYPE_IND_BUFFER_CALCULATE, // "Calculated buffer" object type OBJECT_DE_TYPE_IND_BUFFER_CANDLE, // "Candle buffer" object type OBJECT_DE_TYPE_IND_BUFFER_FILLING, // "Filling buffer" object type OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM, // "Histogram buffer" object type OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM2, // "Histogram 2 buffer" object type OBJECT_DE_TYPE_IND_BUFFER_LINE, // "Line buffer" object type OBJECT_DE_TYPE_IND_BUFFER_SECTION, // "Section buffer" object type OBJECT_DE_TYPE_IND_BUFFER_ZIGZAG, // "Zigzag buffer" object type OBJECT_DE_TYPE_INDICATOR, // "Indicator" object type OBJECT_DE_TYPE_IND_DATA, // "Indicator data" object type OBJECT_DE_TYPE_IND_DATA_LIST, // "Indicator data list" object type OBJECT_DE_TYPE_IND_AC, // "Accelerator Oscillator indicator" object type OBJECT_DE_TYPE_IND_AD, // "Accumulation/Distribution indicator" object type OBJECT_DE_TYPE_IND_ADX, // "Average Directional Index indicator" object type OBJECT_DE_TYPE_IND_ADXW, // "ADX indicator by Welles Wilder" object type OBJECT_DE_TYPE_IND_ALLIGATOR, // "Alligator indicator" object type OBJECT_DE_TYPE_IND_AMA, // "Adaptive Moving Average indicator" object type OBJECT_DE_TYPE_IND_AO, // "Awesome Oscillator indicator" object type OBJECT_DE_TYPE_IND_ATR, // "Average True Range" object type OBJECT_DE_TYPE_IND_BANDS, // "Bollinger Bands® indicator" object type OBJECT_DE_TYPE_IND_BEARS, // "Bears Power indicator" object type OBJECT_DE_TYPE_IND_BULLS, // "Bulls Power indicator" object type OBJECT_DE_TYPE_IND_BWMFI, // "Market Facilitation Index indicator" object type OBJECT_DE_TYPE_IND_CCI, // "Commodity Channel Index indicator" object type OBJECT_DE_TYPE_IND_CHAIKIN, // "Chaikin Oscillator indicator" object type OBJECT_DE_TYPE_IND_CUSTOM, // "Custom indicator" object type OBJECT_DE_TYPE_IND_DEMA, // "Double Exponential Moving Average indicator" object type OBJECT_DE_TYPE_IND_DEMARKER, // "DeMarker indicator" object type OBJECT_DE_TYPE_IND_ENVELOPES, // "Envelopes indicator" object type OBJECT_DE_TYPE_IND_FORCE, // "Force Index indicator" object type OBJECT_DE_TYPE_IND_FRACTALS, // "Fractals indicator" object type OBJECT_DE_TYPE_IND_FRAMA, // "Fractal Adaptive Moving Average indicator" object type OBJECT_DE_TYPE_IND_GATOR, // "Gator Oscillator indicator" object type OBJECT_DE_TYPE_IND_ICHIMOKU, // "Ichimoku Kinko Hyo indicator" object type OBJECT_DE_TYPE_IND_MA, // "Moving Average indicator" object type OBJECT_DE_TYPE_IND_MACD, // "Moving Average Convergence/Divergence indicator" object type OBJECT_DE_TYPE_IND_MFI, // "Money Flow Index indicator" object type OBJECT_DE_TYPE_IND_MOMENTUM, // "Momentum indicator" object type OBJECT_DE_TYPE_IND_OBV, // "On Balance Volume indicator" object type OBJECT_DE_TYPE_IND_OSMA, // "Moving Average of Oscillator indicator" object type OBJECT_DE_TYPE_IND_RSI, // "Relative Strength Index indicator" object type OBJECT_DE_TYPE_IND_RVI, // "Relative Vigor Index indicator" object type OBJECT_DE_TYPE_IND_SAR, // "Parabolic SAR indicator" object type OBJECT_DE_TYPE_IND_STDEV, // "Standard Deviation indicator" object type OBJECT_DE_TYPE_IND_STOCH, // "Stochastic Oscillator indicator" object type OBJECT_DE_TYPE_IND_TEMA, // "Triple Exponential Moving Average indicator" object OBJECT_DE_TYPE_IND_TRIX, // "Triple Exponential Moving Averages Oscillator indicator" object type OBJECT_DE_TYPE_IND_VIDYA, // "Variable Index Dynamic Average indicator" object type OBJECT_DE_TYPE_IND_VOLUMES, // "Volumes indicator" object type OBJECT_DE_TYPE_IND_WPR, // "Williams' Percent Range indicator" object type OBJECT_DE_TYPE_MQL5_SIGNAL, // "mql5 signal" object type OBJECT_DE_TYPE_ORDER_DEAL_POSITION, // "Order/Deal/Position" object type OBJECT_DE_TYPE_HISTORY_BALANCE, // "Historical balance operation" object type OBJECT_DE_TYPE_HISTORY_DEAL, // "Historical deal" object type OBJECT_DE_TYPE_HISTORY_ORDER_MARKET, // "Historical market order" object type OBJECT_DE_TYPE_HISTORY_ORDER_PENDING, // "Historical removed pending order" object type OBJECT_DE_TYPE_MARKET_ORDER, // "Market order" object type OBJECT_DE_TYPE_MARKET_PENDING, // "Pending order" object type OBJECT_DE_TYPE_MARKET_POSITION, // "Market position" object type OBJECT_DE_TYPE_PENDING_REQUEST, // "Pending trading request" object type OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_OPEN, // "Pending request to open a position" object type OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_CLOSE, // "Pending request to close a position" object type OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_SLTP, // "Pending request to modify position stop orders" object type OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_PLACE, // "Pending request to place a pending order" object type OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_REMOVE, // "Pending request to delete a pending order" object type OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_MODIFY, // "Pending request to modify pending order parameters" object type OBJECT_DE_TYPE_SERIES_BAR, // "Bar" object type OBJECT_DE_TYPE_SERIES_PERIOD, // "Period timeseries" object type OBJECT_DE_TYPE_SERIES_SYMBOL, // "Symbol timeseries" object type OBJECT_DE_TYPE_SYMBOL, // "Symbol" object type OBJECT_DE_TYPE_SYMBOL_BONDS, // "Bond symbol" object type OBJECT_DE_TYPE_SYMBOL_CFD, // "CFD (contract for difference) symbol" object type OBJECT_DE_TYPE_SYMBOL_COLLATERAL, // "Non-tradable asset symbol" object type" object type OBJECT_DE_TYPE_SYMBOL_COMMODITY, // "Commodity symbol" object type OBJECT_DE_TYPE_SYMBOL_COMMON, // "Common group symbol" object type OBJECT_DE_TYPE_SYMBOL_CRYPTO, // "Cryptocurrency symbol" object type OBJECT_DE_TYPE_SYMBOL_CUSTOM, // "Custom symbol" object type OBJECT_DE_TYPE_SYMBOL_EXCHANGE, // "Exchange symbol" object type OBJECT_DE_TYPE_SYMBOL_FUTURES, // "Futures symbol" object type OBJECT_DE_TYPE_SYMBOL_FX, // "Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_EXOTIC, // "Exotic Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_MAJOR, // "Major Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_MINOR, // "Minor Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_RUB, // "RUB Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_INDEX, // "Index symbol" object type OBJECT_DE_TYPE_SYMBOL_INDICATIVE, // "Indicative symbol" object type OBJECT_DE_TYPE_SYMBOL_METALL, // "Metal symbol" object type OBJECT_DE_TYPE_SYMBOL_OPTION, // "Option symbol" object type OBJECT_DE_TYPE_SYMBOL_STOCKS, // "Stock symbol" object type OBJECT_DE_TYPE_TICK, // "Tick" object type OBJECT_DE_TYPE_NEW_TICK, // "New tick" object type OBJECT_DE_TYPE_TICKSERIES, // "Tick data series" object type OBJECT_DE_TYPE_TRADE, // "Trading object" object type }; //+------------------------------------------------------------------+ //| Search and sorting data | //+------------------------------------------------------------------+
Si estudiamos detenidamente los valores de las constantes de esta enumeración, veremos que para los tipos de objeto correspondientes a los objetos gráficos estándar, usaremos el valor de la anterior constante de enumeración de los objetos de la biblioteca + 1 + el valor de la enumeración estándar para el objeto gráfico correspondiente. Ya después de completar la enumeración de las listas de objetos gráficos estándar, continuaremos enumerando las listas de los tipos de objeto de la biblioteca, partiendo desde el valor de la constante del último objeto gráfico + 1:
//--- Managing graphical objects OBJECT_DE_TYPE_GELEMENT_CONTROL, // "Managing graphical objects" object type //--- Standard graphical objects OBJECT_DE_TYPE_GSTD_VLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE, // "Vertical line" object type OBJECT_DE_TYPE_GSTD_HLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_HLINE, // "Horizontal line" object type OBJECT_DE_TYPE_GSTD_TREND = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TREND, // "Trend line" object type OBJECT_DE_TYPE_GSTD_TRENDBYANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRENDBYANGLE, // "Trend line by angle" object type OBJECT_DE_TYPE_GSTD_CYCLES = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CYCLES, // "Cyclic lines" object type OBJECT_DE_TYPE_GSTD_ARROWED_LINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROWED_LINE, // "Arrowed line" object type OBJECT_DE_TYPE_GSTD_CHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHANNEL, // "Equidistant channel" object type OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_STDDEVCHANNEL, // "Standard deviation channel" object type OBJECT_DE_TYPE_GSTD_REGRESSION = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_REGRESSION, // "Linear regression channel" object type OBJECT_DE_TYPE_GSTD_PITCHFORK = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_PITCHFORK, // "Andrews' pitchfork" object type OBJECT_DE_TYPE_GSTD_GANNLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNLINE, // "Gann line" object type OBJECT_DE_TYPE_GSTD_GANNFAN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNFAN, // "Gann fan" object type OBJECT_DE_TYPE_GSTD_GANNGRID = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNGRID, // "Gann grid" object type OBJECT_DE_TYPE_GSTD_FIBO = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBO, // "Fibo levels" object type OBJECT_DE_TYPE_GSTD_FIBOTIMES = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOTIMES, // "Fibo time zones" object type OBJECT_DE_TYPE_GSTD_FIBOFAN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOFAN, // "Fibo fan" object type OBJECT_DE_TYPE_GSTD_FIBOARC = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOARC, // "Fibo arcs" object type OBJECT_DE_TYPE_GSTD_FIBOCHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOCHANNEL, // "Fibo channel" object type OBJECT_DE_TYPE_GSTD_EXPANSION = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EXPANSION, // "Fibo expansion" object type OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5 = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE5, // "Elliott 5 waves" object type OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3 = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE3, // "Elliott 3 waves" object type OBJECT_DE_TYPE_GSTD_RECTANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE, // "Rectangle" object type OBJECT_DE_TYPE_GSTD_TRIANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRIANGLE, // "Triangle" object type OBJECT_DE_TYPE_GSTD_ELLIPSE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIPSE, // "Ellipse" object type OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_UP, // "Thumb up" object type OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_DOWN, // "Thumb down" object type OBJECT_DE_TYPE_GSTD_ARROW_UP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_UP, // "Arrow up" object type OBJECT_DE_TYPE_GSTD_ARROW_DOWN = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_DOWN, // "Arrow down" object type OBJECT_DE_TYPE_GSTD_ARROW_STOP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_STOP, // "Stop sign" object type OBJECT_DE_TYPE_GSTD_ARROW_CHECK = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_CHECK, // "Check mark" object type OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_LEFT_PRICE, // "Left price label" object type OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_RIGHT_PRICE, // "Right price label" object type OBJECT_DE_TYPE_GSTD_ARROW_BUY = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_BUY, // "Buy sign" object type OBJECT_DE_TYPE_GSTD_ARROW_SELL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_SELL, // "Sell sign" object type OBJECT_DE_TYPE_GSTD_ARROW = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW, // "Arrow" object type OBJECT_DE_TYPE_GSTD_TEXT = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TEXT, // "Text" object type OBJECT_DE_TYPE_GSTD_LABEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_LABEL, // "Text label" object type OBJECT_DE_TYPE_GSTD_BUTTON = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BUTTON, // "Button" object type OBJECT_DE_TYPE_GSTD_CHART = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHART, // "Chart" object type OBJECT_DE_TYPE_GSTD_BITMAP = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP, // "Bitmap" object type OBJECT_DE_TYPE_GSTD_BITMAP_LABEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP_LABEL, // "Bitmap label" object type OBJECT_DE_TYPE_GSTD_EDIT = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EDIT, // "Input field" object type OBJECT_DE_TYPE_GSTD_EVENT = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EVENT, // "Event object which corresponds to an event in Economic Calendar" object type OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE_LABEL, // "Rectangle Label object used to create and design the custom graphical interface" object type //--- Objects OBJECT_DE_TYPE_BASE = OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Base object for all library objects
Por consiguiente, los objetos gráficos de la biblioteca basados en objetos gráficos estándar, tienen un valor de tipo que concuerda con el valor de tipo del objeto gráfico estándar (este tipo se podrá calcular fácilmente), mientras que los tipos posteriores continuarán obteniendo los valores por encima del valor de la constante del último objeto gráfico estándar sin provocar colisiones entre los valores de las constantes de esta enumeración.
Continuamos. Ahora, necesitaremos modificar ligeramente las listas de propiedades de los objetos gráficos, es decir, añadir la enumeración de pertenencia de un objeto a un programa o terminal (cómo se ha creado, de forma programática o manualmente) y escribir esta propiedad en la enumeración de propiedades enteras del objeto gráfico:
//+------------------------------------------------------------------+ //| List of graphical objects affiliations | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_BELONG { GRAPH_OBJ_BELONG_PROGRAM, // Graphical object belongs to a program GRAPH_OBJ_BELONG_TERMINAL, // Graphical object does not belong to a program }; //+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_ELEMENT, // Element GRAPH_ELEMENT_TYPE_SHADOW_OBJ, // Shadow object GRAPH_ELEMENT_TYPE_FORM, // Form GRAPH_ELEMENT_TYPE_WINDOW, // Window }; //+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Element ID CANV_ELEMENT_PROP_TYPE, // Graphical element type CANV_ELEMENT_PROP_BELONG, // Graphical element affiliation CANV_ELEMENT_PROP_NUM, // Element index in the list CANV_ELEMENT_PROP_CHART_ID, // Chart ID CANV_ELEMENT_PROP_WND_NUM, // Chart subwindow index CANV_ELEMENT_PROP_COORD_X, // Form's X coordinate on the chart CANV_ELEMENT_PROP_COORD_Y, // Form's Y coordinate on the chart CANV_ELEMENT_PROP_WIDTH, // Element width CANV_ELEMENT_PROP_HEIGHT, // Element height CANV_ELEMENT_PROP_RIGHT, // Element right border CANV_ELEMENT_PROP_BOTTOM, // Element bottom border CANV_ELEMENT_PROP_ACT_SHIFT_LEFT, // Active area offset from the left edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_TOP, // Active area offset from the upper edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT, // Active area offset from the right edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM, // Active area offset from the bottom edge of the element CANV_ELEMENT_PROP_MOVABLE, // Element moveability flag CANV_ELEMENT_PROP_ACTIVE, // Element activity flag CANV_ELEMENT_PROP_COORD_ACT_X, // X coordinate of the element active area CANV_ELEMENT_PROP_COORD_ACT_Y, // Y coordinate of the element active area CANV_ELEMENT_PROP_ACT_RIGHT, // Right border of the element active area CANV_ELEMENT_PROP_ACT_BOTTOM, // Bottom border of the element active area }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (22) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
En consecuencia, como hemos introducido una nueva propiedad, también deberemos aumentar el número total de estas propiedades (de 21 a 22), y también añadir la clasificación según esta propiedad a la lista de posibles criterios para clasificar los objetos gráficos:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type SORT_BY_CANV_ELEMENT_BELONG, // Sort by a graphical element affiliation SORT_BY_CANV_ELEMENT_NUM, // Sort by form index in the list SORT_BY_CANV_ELEMENT_CHART_ID, // Sort by chart ID SORT_BY_CANV_ELEMENT_WND_NUM, // Sort by chart window index SORT_BY_CANV_ELEMENT_COORD_X, // Sort by the element X coordinate on the chart SORT_BY_CANV_ELEMENT_COORD_Y, // Sort by the element Y coordinate on the chart SORT_BY_CANV_ELEMENT_WIDTH, // Sort by the element width SORT_BY_CANV_ELEMENT_HEIGHT, // Sort by the element height SORT_BY_CANV_ELEMENT_RIGHT, // Sort by the element right border SORT_BY_CANV_ELEMENT_BOTTOM, // Sort by the element bottom border SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT, // Sort by the active area offset from the left edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP, // Sort by the active area offset from the top edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT, // Sort by the active area offset from the right edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM, // Sort by the active area offset from the bottom edge of the element SORT_BY_CANV_ELEMENT_MOVABLE, // Sort by the element moveability flag SORT_BY_CANV_ELEMENT_ACTIVE, // Sort by the element activity flag SORT_BY_CANV_ELEMENT_COORD_ACT_X, // Sort by X coordinate of the element active area SORT_BY_CANV_ELEMENT_COORD_ACT_Y, // Sort by Y coordinate of the element active area SORT_BY_CANV_ELEMENT_ACT_RIGHT, // Sort by the right border of the element active area SORT_BY_CANV_ELEMENT_ACT_BOTTOM, // Sort by the bottom border of the element active area //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name }; //+------------------------------------------------------------------+
En el archivo \MQL5\Include\DoEasy\Data.mqh, añadimos los índices de los nuevos mensajes de la biblioteca para la colección de objetos gráficos:
//--- CShadowObj MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE, // Error! Image size too small or blur too extensive //--- CGraphElementsCollection MSG_CHART_OBJ_COLLECTION_ERR_OBJ_ALREADY_EXISTS, // Error. A chart control object already exists with chart id MSG_CHART_OBJ_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Failed to create chart control object with chart id }; //+------------------------------------------------------------------+
y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:
//--- CShadowObj {"Ошибка! Размер изображения очень маленький или очень большое размытие","Error! Image size is very small or very large blur"}, //--- CGraphElementsCollection {"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "}, {"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "}, }; //+---------------------------------------------------------------------+
Ahora necesitaremos añadir a cada objeto significativo (no auxiliar) de la biblioteca una indicación sobre su tipo en el momento de la creación. Para nosotros, los objetos auxiliares son los objetos necesarios para el funcionamiento del objeto principal: no tenemos obligación de asignar sus tipos a dichos objetos; no son objetos para las colecciones, solo sirven para organizar el trabajo y simplificar los cálculos en los objetos principales del biblioteca.
Muchos objetos de biblioteca son herederos del objeto básico de todos los objetos de biblioteca, que ya tiene la variable m_type, encargada de guardar el valor del tipo de objeto, y también el método virtual Type(), que retorna el tipo de objeto establecido en la variable. Por consiguiente, nos bastará con especificar en los constructores de sus descendientes el valor de la variable m_type correspondiente al tipo de objeto.
Como hemos introducido el concepto de pertenencia del objeto, determinaremos esta pertenencia según la presencia del nombre del programa en el nombre del objeto gráfico. Para hacerlo, en el objeto básico de todos los objetos de la biblioteca, en el archivo \MQL5\Include\DoEasy\Objects\BaseObj.mqh , crearemos una nueva variable en la sección protegida de la clase para guardar el nombre del programa:
//+------------------------------------------------------------------+ //| Base object class for all library objects | //+------------------------------------------------------------------+ class CBaseObj : public CObject { protected: CGraphElmControl m_graph_elm; // Instance of the class for managing graphical elements ENUM_LOG_LEVEL m_log_level; // Logging level ENUM_PROGRAM_TYPE m_program; // Program type bool m_first_start; // First launch flag bool m_use_sound; // Flag of playing the sound set for an object bool m_available; // Flag of using a descendant object in the program int m_global_error; // Global error code long m_chart_id_main; // Control program chart ID long m_chart_id; // Chart ID string m_name_program; // Program name string m_name; // Object name string m_folder_name; // Name of the folder storing CBaseObj descendant objects string m_sound_name; // Object sound file name int m_type; // Object type (corresponds to the object type from the ENUM_OBJECT_DE_TYPE enumeration) public:
Y en el constructor de la clase, indicaremos el nombre del programa y el tipo del objeto como objeto básico:
//--- Constructor CBaseObj() : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)), m_name_program(::MQLInfoString(MQL_PROGRAM_NAME)), m_global_error(ERR_SUCCESS), m_log_level(LOG_LEVEL_ERROR_MSG), m_chart_id_main(::ChartID()), m_chart_id(::ChartID()), m_folder_name(DIRECTORY), m_sound_name(""), m_name(__FUNCTION__), m_type(OBJECT_DE_TYPE_BASE), m_use_sound(false), m_available(true), m_first_start(true) {} }; //+------------------------------------------------------------------+
En el mismo archivo, después de la clase del objeto básico, hemos escrito la clase del objeto básico ampliado para todos los objetos de la biblioteca. Vamos a indicar en su constructor el tipo de este objeto como básico-ampliado:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CBaseObjExt::CBaseObjExt() : m_hash_sum(0),m_hash_sum_prev(0), m_is_event(false),m_event_code(WRONG_VALUE), m_long_prop_total(0), m_double_prop_total(0) { this.m_type=OBJECT_DE_TYPE_BASE_EXT; ::ArrayResize(this.m_long_prop_event,0,100); ::ArrayResize(this.m_double_prop_event,0,100); ::ArrayResize(this.m_long_prop_event_prev,0,100); ::ArrayResize(this.m_double_prop_event_prev,0,100); ::ZeroMemory(this.m_tick); this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif); this.m_list_events.Clear(); this.m_list_events.Sort(); this.m_list_events_base.Clear(); this.m_list_events_base.Sort(); } //+------------------------------------------------------------------+
Para todos los objetos de la biblioteca herederos de estas dos clases (básica y básica ampliada), solo necesitamos indicar el tipo de objeto en sus constructores, en la variable m_type.
Para el objeto de cuenta, en el archivo \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh, esto tendrá el aspecto siguiente (el constructor completo):
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CAccount::CAccount(void) { this.m_type=OBJECT_DE_TYPE_ACCOUNT; //--- Initialize control data this.SetControlDataArraySizeLong(ACCOUNT_PROP_INTEGER_TOTAL); this.SetControlDataArraySizeDouble(ACCOUNT_PROP_DOUBLE_TOTAL); this.ResetChangesParams(); this.ResetControlsParams(); //--- Save integer properties this.m_long_prop[ACCOUNT_PROP_LOGIN] = ::AccountInfoInteger(ACCOUNT_LOGIN); this.m_long_prop[ACCOUNT_PROP_TRADE_MODE] = ::AccountInfoInteger(ACCOUNT_TRADE_MODE); this.m_long_prop[ACCOUNT_PROP_LEVERAGE] = ::AccountInfoInteger(ACCOUNT_LEVERAGE); this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS] = ::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS); this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE] = ::AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE); this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED] = ::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED); this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT] = ::AccountInfoInteger(ACCOUNT_TRADE_EXPERT); this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE] = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ; this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS] = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif ; this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE] = (::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4); this.m_long_prop[ACCOUNT_PROP_FIFO_CLOSE] = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<2155 ? false : ::AccountInfoInteger(ACCOUNT_FIFO_CLOSE) #else false #endif ); //--- Save real properties this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)] = ::AccountInfoDouble(ACCOUNT_BALANCE); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)] = ::AccountInfoDouble(ACCOUNT_CREDIT); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)] = ::AccountInfoDouble(ACCOUNT_PROFIT); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)] = ::AccountInfoDouble(ACCOUNT_EQUITY); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)] = ::AccountInfoDouble(ACCOUNT_MARGIN); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)] = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)] = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)] = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)] = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)] = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)] = ::AccountInfoDouble(ACCOUNT_ASSETS); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)] = ::AccountInfoDouble(ACCOUNT_LIABILITIES); this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED); //--- Save string properties this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)] = ::AccountInfoString(ACCOUNT_NAME); this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)] = ::AccountInfoString(ACCOUNT_SERVER); this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)] = ::AccountInfoString(ACCOUNT_CURRENCY); this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)] = ::AccountInfoString(ACCOUNT_COMPANY); //--- Account object name, object and account type (MetaTrader 5 or 4) this.m_name=CMessage::Text(MSG_LIB_PROP_ACCOUNT)+" "+(string)this.Login()+": "+this.Name()+" ("+this.Company()+")"; this.m_type=COLLECTION_ACCOUNT_ID; this.m_type_server=(::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4); //--- Filling in the current account data for(int i=0;i<ACCOUNT_PROP_INTEGER_TOTAL;i++) this.m_long_prop_event[i][3]=this.m_long_prop[i]; for(int i=0;i<ACCOUNT_PROP_DOUBLE_TOTAL;i++) this.m_double_prop_event[i][3]=this.m_double_prop[i]; //--- Update the base object data and search for changes CBaseObjExt::Refresh(); } //+-------------------------------------------------------------------+
Como podemos ver, todo lo que necesitamos es establecer el tipo de objeto necesario en la variable protegida m_type. En este caso, se trata del tipo "cuenta". La escritura de un nuevo valor del tipo de objeto en esta variable, declarada en la clase del objeto básico (y en ella se le asigna el tipo "objeto básico"), redefinirá el tipo de objeto de "básico" a "cuenta". Ahora, el método virtual Type(), que retorna el valor de la variable m_type y también se implementa en el objeto básico, retornará el valor de la variable redefinido en el constructor de la clase del objeto de cuenta.
La clase de solicitud abstracta de la profundidad de mercado en el archivo \MQL5\Include\DoEasy\Objects\Book\MarketBookOrd.mqh tiene dos constructores: por defecto y paramétrico. En ambos constructores, escribiremos el tipo de objeto.
En el constructor por defecto:
//--- Compare CMarketBookOrd objects by all properties (to search for equal request objects) bool IsEqual(CMarketBookOrd* compared_req) const; //--- Default constructor CMarketBookOrd(){ this.m_type=OBJECT_DE_TYPE_BOOK_ORDER; } protected: //--- Protected parametric constructor CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol); public: //+-------------------------------------------------------------------+ //|Methods of a simplified access to the DOM request object properties| //+-------------------------------------------------------------------+
y en el paramétrico:
//+------------------------------------------------------------------+ //| Protected parametric constructor | //+------------------------------------------------------------------+ CMarketBookOrd::CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_ORDER; //--- Save symbol’s Digits this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- Save integer object properties this.SetProperty(MBOOK_ORD_PROP_STATUS,status); this.SetProperty(MBOOK_ORD_PROP_TYPE,book_info.type); this.SetProperty(MBOOK_ORD_PROP_VOLUME,book_info.volume); //--- Save real object properties this.SetProperty(MBOOK_ORD_PROP_PRICE,book_info.price); this.SetProperty(MBOOK_ORD_PROP_VOLUME_REAL,book_info.volume_real); //--- Save additional object properties this.SetProperty(MBOOK_ORD_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol)); //--- Order time is not present in the parameters and is considered in the DOM snapshot class. Reset the time this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,0); } //+------------------------------------------------------------------+
En los herederos de la clase de solicitud abstracta de la profundidad de mercado, escribiremos los tipos de objeto correspondientes.
Solicitud de compra en la profundidad de mercado en el archivo \MQL5\Include\DoEasy\Objects\Book\MarketBookBuy.mqh:
//+------------------------------------------------------------------+ //| Buy order in DOM | //+------------------------------------------------------------------+ class CMarketBookBuy : public CMarketBookOrd { private: public: //--- Constructor CMarketBookBuy(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_BUY; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+
Solicitud de venta en la profundidad de mercado en el archivo \MQL5\Include\DoEasy\Objects\Book\MarketBookBuyMarket.mqh:
//+------------------------------------------------------------------+ //| Buy order by Market in DOM | //+------------------------------------------------------------------+ class CMarketBookBuyMarket : public CMarketBookOrd { private: public: //--- Constructor CMarketBookBuyMarket(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_BUY_MARKET; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+
Solicitud de venta en la profundidad de mercado en el archivo \MQL5\Include\DoEasy\Objects\Book\MarketBookSell.mqh:
//+------------------------------------------------------------------+ //| Sell order in DOM | //+------------------------------------------------------------------+ class CMarketBookSell : public CMarketBookOrd { private: public: //--- Constructor CMarketBookSell(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SELL; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+
Solicitud de venta a precio de mercado en la profundidad de mercado en el archivo \MQL5\Include\DoEasy\Objects\Book\MarketBookSellMarket.mqh:
//+------------------------------------------------------------------+ //| Sell order by Market in DOM | //+------------------------------------------------------------------+ class CMarketBookSellMarket : public CMarketBookOrd { private: public: //--- Constructor CMarketBookSellMarket(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SELL_MARKET; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+
La clase "instantánea de la profundidad de mercado" en el archivo \MQL5\Include\DoEasy\Objects\Book\MarketBookSnapshot.mqh tiene dos constructores: por defecto y paramétrico. Por consiguiente, aquí deberemos indicar el tipo de objeto en ambos constructores.
En el constructor por defecto:
//--- Return the DOM snapshot change string Header(void); //--- Display (1) description and (2) short description of a DOM snapshot virtual void Print(const bool full_prop=false,const bool dash=false); virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Constructors CMBookSnapshot(){ this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT; } CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]); //+----------------------------------------------------------------------+ //|Methods of a simplified access to the DOM snapshot object properties | //+----------------------------------------------------------------------+
y en el paramétrico:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time) { this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT; //--- Set a symbol this.SetSymbol(symbol); //--- Clear the list this.m_list.Clear(); //--- In the loop by the structure array int total=::ArraySize(book_array); this.m_volume_buy=this.m_volume_sell=0; this.m_volume_buy_real=this.m_volume_sell_real=0; for(int i=0;i<total;i++) { //--- Create order objects of the current DOM snapshot depending on the order type CMarketBookOrd *mbook_ord=NULL; switch(book_array[i].type) { case BOOK_TYPE_BUY : mbook_ord=new CMarketBookBuy(this.m_symbol,book_array[i]); break; case BOOK_TYPE_SELL : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]); break; case BOOK_TYPE_BUY_MARKET : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]); break; case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]); break; default: break; } if(mbook_ord==NULL) continue; //--- Set the DOM snapshot time for the order mbook_ord.SetTime(this.m_time); //--- Set the sorted list flag for the list (by the price value) and add the current order object to it //--- If failed to add the object to the DOM order list, remove the order object this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE); if(!this.m_list.InsertSort(mbook_ord)) delete mbook_ord; //--- If the order object is successfully added to the DOM order list, supplement the total snapshot volumes else { switch(mbook_ord.TypeOrd()) { case BOOK_TYPE_BUY : this.m_volume_buy+=mbook_ord.Volume(); this.m_volume_buy_real+=mbook_ord.VolumeReal(); break; case BOOK_TYPE_SELL : this.m_volume_sell+=mbook_ord.Volume(); this.m_volume_sell_real+=mbook_ord.VolumeReal(); break; case BOOK_TYPE_BUY_MARKET : this.m_volume_buy+=mbook_ord.Volume(); this.m_volume_buy_real+=mbook_ord.VolumeReal(); break; case BOOK_TYPE_SELL_MARKET : this.m_volume_buy+=mbook_ord.Volume(); this.m_volume_buy_real+=mbook_ord.VolumeReal(); break; default: break; } } } } //+------------------------------------------------------------------+
La clase "serie de instantáneas de la profundidad de mercado" también tiene dos constructores, así que escribiremos igualmente el tipo de objeto en ambos.
Por defecto:
//--- Display (1) description and (2) short description of a DOM snapshot series virtual void Print(const bool full_prop=false,const bool dash=false); virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Constructors CMBookSeries(){ this.m_type=OBJECT_DE_TYPE_BOOK_SERIES; } CMBookSeries(const string symbol,const uint required=0); //+-----------------------------------------------------------------------------+ //| Methods of working with objects and accessing their properties | //+-----------------------------------------------------------------------------+
y en el paramétrico:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CMBookSeries::CMBookSeries(const string symbol,const uint required=0) : m_symbol(symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SERIES; this.m_list.Clear(); this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC); this.SetRequiredUsedDays(required); } //+------------------------------------------------------------------+
La clase de evento abstracto en el archivo \MQL5\Include\DoEasy\Objects\Events\Event.mqh no es un heredero ni del objeto básico ni del objeto básico ampliado de todos los objetos de la biblioteca. Por ello, no existe en ella una variable m_type, ni tampoco hay un método virtual Type() que retorne el valor de esta variable (tal método existe en el objeto básico CObject, del cual se hereda esta clase, pero retorna 0, y deberá redefinirse en sus descendientes). Esto significa que necesitaremos añadir la variable y el método y escribir en los constructores de la clase el tipo necesario para la variable creada:
//+------------------------------------------------------------------+ //| Abstract event class | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Event code //--- Return the index of the array the event's (1) double and (2) string properties are located at int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Trading event bool m_is_hedge; // Hedge account flag long m_chart_id_main; // Control program chart ID int m_type; // Object type int m_digits; // Symbol's Digits() int m_digits_acc; // Number of decimal places for the account currency long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Event integer properties double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Event real properties string m_string_prop[EVENT_PROP_STRING_TOTAL]; // Event string properties //--- return the flag presence in the trading event bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value ushort GetMagicID(void) const { return ushort(this.Magic() & 0xFFFF); } uchar GetGroupID1(void) const { return uchar(this.Magic()>>16) & 0x0F; } uchar GetGroupID2(void) const { return uchar((this.Magic()>>16) & 0xF0)>>4; } uchar GetPendReqID(void) const { return uchar(this.Magic()>>24) & 0xFF; } //--- Protected parametric constructor CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- Default constructor CEvent(void){ this.m_type=OBJECT_DE_TYPE_EVENT; } //--- Set event's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Return the event's (1) integer, (2) real and (3) string properties from the property array long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the event supporting the property virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Return an object type virtual int Type(void) const { return this.m_type;} //--- Decode the event code and set the trading event, (2) return the trading event void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Send the event to the chart (implementation in descendant classes) virtual void SendEvent(void) {;} //--- Compare CEvent objects by a specified property (to sort the lists by a specified event object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CEvent objects by all properties (to search for equal event objects) bool IsEqual(CEvent* compared_event); //+------------------------------------------------------------------+ //| Methods of simplified access to event object properties | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0) { this.m_type=OBJECT_DE_TYPE_EVENT; this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif; this.m_chart_id_main=::ChartID(); } //+------------------------------------------------------------------+
En los herederos del objeto de clase de evento abstracto, deberemos indicar en sus constructores el tipo necesario del objeto.
Clase de evento de operación de balance en el archivo \MQL5\Include\DoEasy\Objects\Events\EventBalanceOperation.mqh:
//+------------------------------------------------------------------+ //| Balance operation event | //+------------------------------------------------------------------+ class CEventBalanceOperation : public CEvent { public: //--- Constructor CEventBalanceOperation(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_BALANCE,event_code,ticket) { this.m_type=OBJECT_DE_TYPE_EVENT_BALANCE; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Clase de evento de modificación de una orden pendiente o posición en el archivo \MQL5\Include\DoEasy\Objects\Events\EventModify.mqh:
//+------------------------------------------------------------------+ //| Pending order or position modification event | //+------------------------------------------------------------------+ class CEventModify : public CEvent { private: double m_price; // Price sent to an event //--- Create and return a short event message string EventsMessage(void); public: //--- Constructor CEventModify(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MODIFY,event_code,ticket),m_price(0) { this.m_type=OBJECT_DE_TYPE_EVENT_MODIFY; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Clase de evento de establecimiento de una orden pendiente en el archivo \MQL5\Include\DoEasy\Objects\Events\EventOrderPlaced.mqh:
//+------------------------------------------------------------------+ //| Pending order placing event | //+------------------------------------------------------------------+ class CEventOrderPlased : public CEvent { public: //--- Constructor CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) { this.m_type=OBJECT_DE_TYPE_EVENT_ORDER_PLASED; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Clase de evento de eliminación de una orden pendiente en el archivo \MQL5\Include\DoEasy\Objects\Events\EventOrderRemoved.mqh:
//+------------------------------------------------------------------+ //| Pending order removal event | //+------------------------------------------------------------------+ class CEventOrderRemoved : public CEvent { public: //--- Constructor CEventOrderRemoved(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_PENDING,event_code,ticket) { this.m_type=OBJECT_DE_TYPE_EVENT_ORDER_REMOVED; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Clase de evento de cierre de una posición en el archivo \MQL5\Include\DoEasy\Objects\Events\EventPositionClose.mqh:
//+------------------------------------------------------------------+ //| Position closure event | //+------------------------------------------------------------------+ class CEventPositionClose : public CEvent { private: //--- Create and return a short event message string EventsMessage(void); public: //--- Constructor CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) { this.m_type=OBJECT_DE_TYPE_EVENT_POSITION_CLOSE; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Clase de evento de apertura de una posición en el archivo \MQL5\Include\DoEasy\Objects\Events\EventPositionOpen.mqh:
//+------------------------------------------------------------------+ //| Position opening event | //+------------------------------------------------------------------+ class CEventPositionOpen : public CEvent { private: //--- Create and return a short event message string EventsMessage(void); public: //--- Constructor CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) { this.m_type=OBJECT_DE_TYPE_EVENT_POSITION_OPEN; } //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Entonces, podemos ver que todas las acciones destinadas a mejorar las clases de los objetos anteriormente creados son las siguientes:
- Si un objeto es heredero del objeto básico o del objeto básico ampliado de todos los objetos de la biblioteca, necesitaremos especificar en sus constructores (por defecto y paramétrico) el tipo de objeto de la enumeración ENUM_OBJECT_DE_TYPE que hemos creado arriba, en el archivo Defines.mqh.
- Si el objeto no es heredero del objeto básico o del objeto básico ampliado de todos los objetos de la biblioteca, tendremos que añadir en la sección protegida de la clase la variable m_type, que almacenará el tipo del objeto, y también añadir en la sección pública el método virtual Type(), que retornará el valor de la variable m_type. Asimismo, deberemos indicar en los constructores de la clase (por defecto y paramétrico) el tipo de objeto de la enumeración ENUM_OBJECT_DE_TYPE.
Todos estos cambios ya se han realizado en los archivos de las clases de objetos de la biblioteca, por lo que no tiene sentido describir aquí el mismo tipo de acciones destinadas a modificar el resto de clases de la biblioteca.
Aquí tenemos una lista de las clases modificadas en el directorio de la biblioteca \MQL5\Include\DoEasy\Objects:
- Carpeta Chart: archivos ChartObj.mqh y ChartWnd.mqh;
- Carpeta Indicators: archivos Buffer.mqh, BufferArrow.mqh, BufferBars.mqh, BufferCalculate.mqh, BufferCandles.mqh, BufferFilling.mqh, BufferHistogram.mqh, BufferHistogram2.mqh, BufferLine.mqh, BufferSection.mqh, BufferZigZag.mqh, DataInd.mqh, IndicatorDE.mqh, SeriesDataInd.mqh;
Carpeta Standart: archivos IndAC.mqh, IndAD.mqh, IndADX.mqh, IndADXW.mqh, IndAlligator.mqh, IndAMA.mqh, IndAO.mqh, IndATR.mqh, IndBands.mqh, IndBears.mqh, IndBulls.mqh, IndBWMFI.mqh, IndCCI.mqh, IndChaikin.mqh, IndCustom.mqh, IndDEMA.mqh, IndDeMarker.mqh, IndEnvelopes.mqh, IndForce.mqh, IndFractals.mqh, IndFRAMA.mqh, IndGator.mqh, IndIchimoku.mqh, IndMA.mqh, IndMACD.mqh, IndMFI.mqh, IndMomentum.mqh, IndOBV.mqh, IndOsMA.mqh, IndRSI.mqh, IndRVI.mqh, IndSAR.mqh, IndStDev.mqh, IndStoch.mqh, IndTEMA.mqh, IndTRIX.mqh, IndVIDYA.mqh, IndVolumes.mqh, IndWPR.mqh;
- Carpeta MQLSignalBase: archivo MQLSignal.mqh;
- Carpeta Orders: archivos HistoryBalance.mqh, HistoryDeal.mqh, HistoryOrder.mqh, HistoryPending.mqh, MarketOrder.mqh, MarketPending.mqh, MarketPosition.mqh, Order.mqh;
- Carpeta PendRequest: archivos PendReqClose.mqh, PendReqModify.mqh, PendReqOpen.mqh, PendReqPlace.mqh, PendReqRemove.mqh, PendReqSLTP.mqh, PendRequest.mqh;
- Carpeta Series: archivos Bar.mqh, SeriesDE.mqh, TimeSeriesDE.mqh;
- Carpeta Symbols: archivos Symbol.mqh, SymbolBonds.mqh, SymbolCFD.mqh, SymbolCollateral.mqh, SymbolCommodity.mqh, SymbolCommon.mqh, SymbolCrypto.mqh, SymbolCustom.mqh, SymbolExchange.mqh, SymbolFutures.mqh, SymbolFX.mqh, SymbolFXExotic.mqh, SymbolFXMajor.mqh, SymbolFXMinor.mqh, SymbolFXRub.mqh, SymbolIndex.mqh, SymbolIndicative.mqh, SymbolMetall.mqh, SymbolOption.mqh, SymbolStocks.mqh;
- Carpeta Ticks: archivos DataTick.mqh, NewTickObj.mqh, TickSeries.mqh;
- Carpeta Trade: archivo TradeObj.mqh;
- Carpeta Graph: archivos Form.mqh, GCnvElement.mqh, GraphElmControl.mqh, ShadowObj.mqh;
Carpeta Animations: archivos Animations.mqh, Frame.mqh, FrameGeometry.mqh, FrameQuad.mqh, FrameText.mqh;
El lector podrá ver todos estos archivos y estudiar los cambios realizados en ellos en los archivos adjuntos al artículo.
Hemos dejado fuera de la lista de archivos modificados una clase: la clase del objeto básico de todos los objetos gráficos de la biblioteca en el archivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh. En esta, además de indicar el tipo de objeto, añadimos la propiedad "pertenencia al programa/terminal": la necesitaremos para entender qué objeto gráfico ha sido creado por un programa ejecutado bajo el control de la biblioteca, y cuál se ha añadido manualmente al gráfico en el terminal:
//+------------------------------------------------------------------+ //| GBaseObj.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 "..\..\Services\DELib.mqh" #include <Graphics\Graphic.mqh> //+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGBaseObj : public CObject { private: protected: string m_name_prefix; // Object name prefix string m_name; // Object name long m_chart_id; // Chart ID int m_subwindow; // Subwindow index int m_shift_y; // Subwindow Y coordinate shift int m_type; // Object type bool m_visible; // Object visibility ENUM_GRAPH_OBJ_BELONG m_belong; // Program/terminal affiliation //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void) { return true; } virtual void StructToObject(void){;} public: //--- Return the values of class variables string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } int SubWindow(void) const { return this.m_subwindow; } ENUM_GRAPH_OBJ_BELONG Belong(void) const { return this.m_belong; } //--- (1) Set and (2) return the object visibility void SetVisible(const bool flag) { long value=(flag ? OBJ_ALL_PERIODS : 0); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) this.m_visible=flag; } bool IsVisible(void) const { return this.m_visible; } //--- The virtual method returning the object type virtual int Type(void) const { return this.m_type; } //--- Set affiliation void SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong; } //--- Constructor/destructor CGBaseObj(); ~CGBaseObj(); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM) { this.m_type=OBJECT_DE_TYPE_GBASE; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGBaseObj::~CGBaseObj() { } //+------------------------------------------------------------------+
También hemos hecho mejoras similares a las realizadas en los archivos de clase de los objetos de la biblioteca en los archivos de clase de los objetos de la biblioteca en el directorio \MQL5\Include\DoEasy\Collections\.
Aquí tenemos una lista con los archivos de las clases de colección de objetos mejoradas:
AccountsCollection.mqh, BookSeriesCollection.mqh, BuffersCollection.mqh, ChartObjCollection.mqh, EventsCollection.mqh, HistoryCollection.mqh, IndicatorsCollection.mqh, MarketCollection.mqh, MQLSignalsCollection.mqh, ResourceCollection.mqh, SymbolsCollection.mqh, TickSeriesCollection.mqh, TimeSeriesCollection.mqh.
El lector podrá ver estos archivos y estudiar los cambios realizados en ellos en los archivos adjuntos al artículo.
La mejora de las clases de la biblioteca ha finalizado.
Clase de colección de objetos gráficos
En el último artículo, hicimos una plantilla para la clase de colección de objetos gráficos de la biblioteca (archivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh). Hoy continuaremos desarrollando esta.
Necesitamos asegurarnos de que al crear cualquier objeto gráfico en cualquiera de los gráficos abiertos en el terminal, la biblioteca pueda determinar qué tipo de objeto es, si ha sido añadido o eliminado, y cómo se ha creado: de forma programática desde la biblioteca o manualmente. Los objetos añadidos de forma programática no deben agregarse automáticamente a la colección de objetos gráficos de la biblioteca: se añadirán a la lista de colecciones al ser creados (implementaremos esto en artículos posteriores). En el caso de objetos gráficos añadidos manualmente, la biblioteca deberá identificarlos, crear el objeto gráfico (elemento) para ellos y agregarlos a la lista de la colección. Al eliminar objetos gráficos del gráfico, la biblioteca deberá hacer lo mismo: los objetos creados de forma programática se borrarán de la lista durante la eliminación. En cuanto a los que se eliminan manualmente, la biblioteca deberá rastrearlos y borrar el elemento que coincida con el objeto gráfico eliminado de la lista de colección.
Como esta funcionalidad supera el volumen de un solo artículo, implementaremos todo esto de forma secuencial. Hoy crearemos en la clase de colección de objetos gráficos el monitoreo de la aparición de cualquier objeto gráfico en cualquiera de los gráficos del terminal. No importa cómo se construya este objeto gráfico (de forma programática desde la biblioteca o manualmente), la clase de colección rastreará su aparición en el gráfico o su eliminación del mismo y escribirá el resultado en el diario: cuántos objetos gráficos se han añadido/eliminado en cuál de los gráficos del terminal. Es decir, hoy nos limitaremos a rastrear el número de objetos gráficos en los gráficos y los mensajes en el diario sobre los cambios en dicho número.
Para obtener el número de objetos gráficos en el gráfico, podemos usar la función ObjectsTotal(), que retorna el número de objetos gráficos del tipo especificado en el gráfico indicado y su subventana especificada. Para obtener el número de objetos gráficos de cualquier tipo en cualquiera de las subventanas del gráfico especificado (incluida la ventana principal), deberemos transmitir a la función el identificador del gráfico necesario, dejando el resto de los parámetros por defecto (-1). De esta forma, obtendremos el número total de objetos para este gráfico, incluidas sus subventanas.
Para comprender cuántos objetos se han añadido en el momento actual, necesitaremos saber cuántos había antes y cuántos hay ahora. La diferencia entre estos dos valores será el número de objetos añadidos. Para conseguirlo, necesitamos tener dos variables: una con el número actual de objetos gráficos en el gráfico y otra con el número en la última verificación. Si la cantidad ha cambiado, deberemos comprobar qué tipo de objeto se ha añadido al gráfico o se ha eliminado del mismo.
Y aquí nos encontraremos con la dificultad de calcular correctamente esta cantidad. Si volvemos a la descripción de la función ObjectsTotal(), nos quedará claro que esta retorna el número de objetos solo para un gráfico, y no para todos al mismo tiempo. Por consiguiente, para cada gráfico necesitaremos tener nuestras propias variables para guardar el número actual y pasado de objetos gráficos. Esto significa que lo más simple es crear una pequeña clase para gestionar los objetos gráficos y tener una instancia de esta clase para cada uno de los gráficos abiertos en la terminal. Luego, para cada uno de los gráficos, podremos monitorear fácilmente los cambios en el número de objetos independientemente de otros gráficos.
Vamos a escribir esta clase directamente en el archivo de la clase de colección de objetos gráficos \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh:
//+------------------------------------------------------------------+ //| GraphElementsCollection.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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Graph\Form.mqh" //+------------------------------------------------------------------+ //| Chart object management class | //+------------------------------------------------------------------+ class CChartObjectsControl : public CObject { private: ENUM_TIMEFRAMES m_chart_timeframe; // Chart timeframe long m_chart_id; // Chart ID string m_chart_symbol; // Chart symbol bool m_is_graph_obj_event; // Event flag in the list of graphical objects int m_total_objects; // Number of graphical objects int m_last_objects; // Number of graphical objects during the previous check int m_index_object; // Index of the last graphical object added to the collection from the terminal object list int m_delta_graph_obj; // Difference in the number of graphical objects compared to the previous check public: //--- Return the variable values ENUM_TIMEFRAMES Timeframe(void) const { return this.m_chart_timeframe; } long ChartID(void) const { return this.m_chart_id; } string Symbol(void) const { return this.m_chart_symbol; } bool IsEvent(void) const { return this.m_is_graph_obj_event; } int TotalObjects(void) const { return this.m_total_objects; } int Delta(void) const { return this.m_delta_graph_obj; } //--- Check the chart objects void Refresh(void); //--- Constructors CChartObjectsControl(void) { this.m_chart_id=::ChartID(); this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id); this.m_chart_symbol=::ChartSymbol(this.m_chart_id); this.m_is_graph_obj_event=false; this.m_total_objects=0; this.m_last_objects=0; this.m_index_object=0; this.m_delta_graph_obj=0; } CChartObjectsControl(const long chart_id) { this.m_chart_id=chart_id; this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id); this.m_chart_symbol=::ChartSymbol(this.m_chart_id); this.m_is_graph_obj_event=false; this.m_total_objects=0; this.m_last_objects=0; this.m_index_object=0; this.m_delta_graph_obj=0; } //--- Compare CChartObjectsControl objects by a chart ID (for sorting the list by an object property) virtual int Compare(const CObject *node,const int mode=0) const { const CChartObjectsControl *obj_compared=node; return(this.ChartID()>obj_compared.ChartID() ? 1 : this.ChartID()<obj_compared.ChartID() ? -1 : 0); } }; //+------------------------------------------------------------------+ //| CChartObjectsControl Check objects on a chart | //+------------------------------------------------------------------+ void CChartObjectsControl::Refresh(void) { //--- Graphical objects on the chart this.m_total_objects=::ObjectsTotal(this.ChartID()); int i=this.m_index_object; int delta=this.m_total_objects-this.m_last_objects; //--- If the number of objects has changed if(delta!=0) { //--- Create the string and display it in the journal with the chart ID, its symbol and timeframe string txt=", "+(delta>0 ? "Added: " : "Deleted: ")+(string)fabs(delta)+" obj"; Print(DFUN,"ChartID=",this.ChartID(),", ",this.Symbol(),", ",TimeframeDescription(this.Timeframe()),txt); } //--- save the index of the last added order and the difference with the last check this.m_delta_graph_obj=i-this.m_index_object; this.m_index_object=i; this.m_last_objects=this.m_total_objects; this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj; } //+------------------------------------------------------------------+
En general, todo está claro aquí: tenemos variables propias para almacenar el número de objetos gráficos "ahora" y en la última comprobación. Una variable para almacenar el valor actual del índice del ciclo, para no realizar el ciclo desde el principio cada vez; recordamos el valor actual del índice, y la próxima vez no iniciamos el ciclo desde el principio, sino desde el valor almacenado. Así es como tenemos ciclos para controlar órdenes, transacciones y posiciones: este también es el caso aquí. Dos constructores: el primero crea un objeto para el gráfico actual; el segundo, para el gráfico indicado según su identificador. El método Compare() compara dos objetos según el identificador del gráfico. Es necesario para determinar que tal objeto ya existe para el gráfico con el identificador indicado.
En el método Refresh(), por el momento, simplemente comprobaremos el número de objetos ahora y en la última comprobación. Si el número ha cambiado, mostraremos una entrada sobre ello en el diario. Posteriormente, realizaremos un ciclo por los objetos, partiendo desde el índice de bucle almacenado en la variable m_index_object, para monitorear todos los objetos nuevos y crear un objeto de evento. Pero ahora, el índice del ciclo está almacenado en una variable para iniciar posteriormente el ciclo de cálculo económico; esto es una reserva para el futuro.
En estos momentos, si creamos tales objetos en la clase de colección para cada uno de los gráficos abiertos en el terminal, podremos monitorear el cambio en el número de objetos para cada gráfico de forma independiente entre sí.
Vamos a añadir las nuevas variables y métodos a la clase CGraphElementsCollection creada anteriormente.
En la sección privada de la clase, declaramos la lista de punteros a los objetos de control de gráficos, una variable de bandera para el evento de adición/eliminación de un objeto gráfico en el gráfico, una variable para guardar el número de objetos en todos los gráficos abiertos y una variable para almacenar el número total de objetos añadidos/eliminados en todos los gráficos abiertos del terminal:
//+------------------------------------------------------------------+ //| Collection of graphical objects | //+------------------------------------------------------------------+ class CGraphElementsCollection : public CBaseObj { private: CArrayObj m_list_charts_control; // List of chart management objects CListObj m_list_all_graph_obj; // List of all graphical objects bool m_is_graph_obj_event; // Event flag in the list of graphical objects int m_total_objects; // Number of graphical objects int m_delta_graph_obj; // Difference in the number of graphical objects compared to the previous check //--- Return the flag indicating the graphical element object in the list of graphical objects bool IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
En la misma sección, declaramos el método que retorna el puntero al objeto de control de objetos del gráfico especificado, el método para crear un nuevo objeto de control de objetos gráficos para el gráfico especificado y añadirlo al lista y el método para actualizar la lista de objetos gráficos según el identificador del gráfico:
//--- Return the flag indicating the graphical element object in the list of graphical objects bool IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj); //--- Return the pointer to the object of managing objects of the specified chart CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id); //--- Create a new object of managing graphical objects of a specified chart and add it to the list CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id); //--- Update the list of graphical objects by chart ID void RefreshByChartID(const long chart_id); public:
En la sección pública, escribimos el método que retorna la bandera de cambio sucedido en la lista de objetos gráficos, declaramos el método que crea la lista de objetos de control de los gráficos, y dos métodos que actualizan las listas de objetos gráficos en los gráficos del terminal:
public: //--- Return itself CGraphElementsCollection *GetObject(void) { return &this; } //--- Return the full collection list 'as is' CArrayObj *GetList(void) { return &this.m_list_all_graph_obj; } //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode); } //--- Return the number of new graphical objects, (3) the flag of the occurred change in the list of graphical objects int NewObjects(void) const { return this.m_delta_graph_obj; } bool IsEvent(void) const { return this.m_is_graph_obj_event; } //--- Constructor CGraphElementsCollection(); //--- Display the description of the object properties in the journal (full_prop=true - all properties, false - supported ones only - implemented in descendant classes) virtual void Print(const bool full_prop=false,const bool dash=false); //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Create the list of chart management objects and return the number of charts int CreateChartControlList(void); //--- Update the list of (1) all graphical objects, (2) on the specified chart, fill in the data on the number of new ones and set the event flag void Refresh(void); void Refresh(const long chart_id); }; //+------------------------------------------------------------------+
En el constructor de la clase, asignamos al objeto su tipo; asimismo, asignamos a la lista de punteros a los objetos de control de gráficos la bandera de lista clasificada y limpiamos la lista. Además, establecemos un número total de objetos en todos los gráficos igual a cero y reseteamos el indicador de evento de la colección de objetos gráficos:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGraphElementsCollection::CGraphElementsCollection() { this.m_type=COLLECTION_GRAPH_OBJ_ID; ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true); ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true); this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID); this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID); this.m_list_all_graph_obj.Clear(); this.m_list_charts_control.Sort(); this.m_list_charts_control.Clear(); this.m_total_objects=0; this.m_is_graph_obj_event=false; } //+------------------------------------------------------------------+
Veamos la implementación de los métodos declarados.
Método que crea un nuevo objeto para gestionar los objetos gráficos del gráfico especificado y lo añade a la lista:
//+------------------------------------------------------------------+ //| Create a new graphical object management object | //| for a specified and add it to the list | //+------------------------------------------------------------------+ CChartObjectsControl *CGraphElementsCollection::CreateChartObjectCtrlObj(const long chart_id) { //--- Create a new object for managing chart objects by ID CChartObjectsControl *obj=new CChartObjectsControl(chart_id); //--- If the object is not created, inform of the error and return NULL if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_CHART_OBJ_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ),(string)chart_id); return NULL; } //--- If failed to add the object to the list, inform of the error, remove the object and return NULL if(!this.m_list_charts_control.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; return NULL; } //--- Return the pointer to the object that was created and added to the list return obj; } //+------------------------------------------------------------------+
Método que retorna el puntero a un objeto de control de los objetos gráficos especificados:
//+------------------------------------------------------------------+ //| Return the pointer to the object | //| for managing objects of a specified chart | //+------------------------------------------------------------------+ CChartObjectsControl *CGraphElementsCollection::GetChartObjectCtrlObj(const long chart_id) { //--- In the loop by the total number of objects in the list for(int i=0;i<this.m_list_charts_control.Total();i++) { //--- Get the pointer to the next object CChartObjectsControl *obj=this.m_list_charts_control.At(i); //--- If failed to get the pointer, move on to the next one if(obj==NULL) continue; //--- If the object chart ID is equal to the required one, return the pointer to the object in the list if(obj.ChartID()==chart_id) return obj; } //--- Failed to find the object - return NULL return NULL; } //+------------------------------------------------------------------+
Método que crea una lista de objetos de control de gráficos:
//+------------------------------------------------------------------+ //| Create the list of chart management objects | //+------------------------------------------------------------------+ int CGraphElementsCollection::CreateChartControlList(void) { //--- Clear the list of chart management objects and set the sorted list flag to it this.m_list_charts_control.Clear(); this.m_list_charts_control.Sort(); //--- Declare variables to search for charts long chart_id=0; int i=0; //--- In the loop by all open charts in the terminal (no more than 100) while(i<CHARTS_MAX) { //--- Get the chart ID chart_id=::ChartNext(chart_id); if(chart_id<0) break; //--- Create the object for managing chart objects based on the current chart ID in the loop and add it to the list CChartObjectsControl *chart_control=new CChartObjectsControl(chart_id); if(chart_control==NULL) continue; //--- If such an object is already present in the list, inform of that, delete the object abd move on to the next chart if(this.m_list_charts_control.Search(chart_control)>WRONG_VALUE) { ::Print(DFUN,CMessage::Text(MSG_CHART_OBJ_COLLECTION_ERR_OBJ_ALREADY_EXISTS),(string)chart_id); delete chart_control; continue; } //--- If failed to add the object to the list, inform of that, remove the object and move on to the next chart if(!this.m_list_charts_control.Add(chart_control)) { CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete chart_control; continue; } //--- Increase the loop index i++; } //--- The list filled in successfully - return the number of its elements return this.m_list_charts_control.Total(); } //+------------------------------------------------------------------+
Método que actualiza la lista de objetos gráficos según el identificador del gráfico:
//+------------------------------------------------------------------+ //| Update the list of graphical objects by chart ID | //+------------------------------------------------------------------+ void CGraphElementsCollection::RefreshByChartID(const long chart_id) { //--- Get the pointer to the object for managing graphical objects CChartObjectsControl *obj=GetChartObjectCtrlObj(chart_id); //--- If there is no such an object in the list, create a new one and add it to the list if(obj==NULL) obj=this.CreateChartObjectCtrlObj(chart_id); //--- If the pointer to the object is valid, update the list of graphical objects on a specified chart if(obj!=NULL) obj.Refresh(); } //+------------------------------------------------------------------+
Método que actualiza la lista de objetos gráficos en el gráfico indicado:
//+------------------------------------------------------------------+ //| Update the list of graphical objects on a specified chart | //+------------------------------------------------------------------+ void CGraphElementsCollection::Refresh(const long chart_id) { //--- Get the pointer to the object for managing graphical objects CChartObjectsControl *obj=GetChartObjectCtrlObj(chart_id); //--- If failed to get the pointer, exit the method if(obj==NULL) return; //--- Update the list of graphical objects on a specified chart obj.Refresh(); } //+------------------------------------------------------------------+
Método que actualiza la lista de todos los objetos gráficos en todos los gráficos abiertos del terminal:
//+------------------------------------------------------------------+ //| Update the list of all graphical objects | //+------------------------------------------------------------------+ void CGraphElementsCollection::Refresh(void) { //--- Declare variables to search for charts long chart_id=0; int i=0; //--- In the loop by all open charts in the terminal (no more than 100) while(i<CHARTS_MAX) { //--- Get the chart ID chart_id=::ChartNext(chart_id); if(chart_id<0) break; //--- Update the list of graphical objects by chart ID this.RefreshByChartID(chart_id); //--- Increase the loop index i++; } } //+------------------------------------------------------------------+
La lógica de cada uno de los métodos mostrados se describe con detalle en los comentarios a los métodos. Esperamos que no requieran explicaciones adicionales. En cualquier caso, el lector podrá escribir cualquier duda en los comentarios al artículo.
Ahora necesitamos conectar la colección creada de objetos gráficos al objeto principal de la biblioteca CEngine, para que podamos tener acceso normal a la funcionalidad de la colección de objetos gráficos desde nuestros programas.
En el archivo \MQL5\Include\DoEasy\Engine.mqh, en la sección privada de la clase, declaramos una instancia de la clase de colección de objetos gráficos, una variable de bandera de evento en la lista de objetos gráficos y declaramos el método para controlar los eventos de los objetos gráficos:
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CTimeSeriesCollection m_time_series; // Timeseries collection CBuffersCollection m_buffers; // Collection of indicator buffers CIndicatorsCollection m_indicators; // Indicator collection CTickSeriesCollection m_tick_series; // Collection of tick series CMBookSeriesCollection m_book_series; // Collection of DOM series CMQLSignalsCollection m_signals_mql5; // Collection of MQL5.com Signals service signals CChartObjCollection m_charts; // Chart collection CGraphElementsCollection m_graph_objects; // Collection of graphical objects CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CPause m_pause; // Pause object CArrayObj m_list_counters; // List of timer counters int m_global_error; // Global error code bool m_first_start; // First launch flag bool m_is_hedge; // Hedge account flag bool m_is_tester; // Flag of working in the tester bool m_is_market_trade_event; // Account trading event flag bool m_is_history_trade_event; // Account history trading event flag bool m_is_account_event; // Account change event flag bool m_is_symbol_event; // Symbol change event flag bool m_is_graph_obj_event; // Event flag in the list of graphical objects ENUM_TRADE_EVENT m_last_trade_event; // Last account trading event int m_last_account_event; // Last event in the account properties int m_last_symbol_event; // Last event in the symbol properties ENUM_PROGRAM_TYPE m_program; // Program type string m_name; // Program name //--- Return the counter index by id int CounterIndex(const int id) const; //--- Return the first launch flag bool IsFirstStart(void); //--- Handling events of (1) orders, deals and positions, (2) accounts and (3) graphical objects void TradeEventsControl(void); void AccountEventsControl(void); void GraphObjEventsControl(void); //--- (1) Working with a symbol collection and (2) symbol list events in the market watch window void SymbolEventsControl(void); void MarketWatchEventsControl(void); //--- Return the last (1) market pending order, (2) market order, (3) last position, (4) position by ticket COrder *GetLastMarketPending(void); COrder *GetLastMarketOrder(void); COrder *GetLastPosition(void); COrder *GetPosition(const ulong ticket); //--- Return the last (1) removed pending order, (2) historical market order, (3) historical order (market or pending one) by its ticket COrder *GetLastHistoryPending(void); COrder *GetLastHistoryOrder(void); COrder *GetHistoryOrder(const ulong ticket); //--- Return the (1) first and the (2) last historical market orders from the list of all position orders, (3) the last deal COrder *GetFirstOrderPosition(const ulong position_id); COrder *GetLastOrderPosition(const ulong position_id); COrder *GetLastDeal(void); //--- Retrieve a necessary 'ushort' number from the packed 'long' value ushort LongToUshortFromByte(const long source_value,const uchar index) const; public:
En el constructor de la clase, creamos un contador del temporizador para la colección de objetos gráficos:
//+------------------------------------------------------------------+ //| CEngine constructor | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true), m_last_trade_event(TRADE_EVENT_NO_EVENT), m_last_account_event(WRONG_VALUE), m_last_symbol_event(WRONG_VALUE), m_global_error(ERR_SUCCESS) { this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_is_tester=::MQLInfoInteger(MQL_TESTER); this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_name=::MQLInfoString(MQL_PROGRAM_NAME); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE); this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE); this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1); this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2); this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE); this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE); this.CreateCounter(COLLECTION_IND_TS_COUNTER_ID,COLLECTION_IND_TS_COUNTER_STEP,COLLECTION_IND_TS_PAUSE); this.CreateCounter(COLLECTION_TICKS_COUNTER_ID,COLLECTION_TICKS_COUNTER_STEP,COLLECTION_TICKS_PAUSE); this.CreateCounter(COLLECTION_CHARTS_COUNTER_ID,COLLECTION_CHARTS_COUNTER_STEP,COLLECTION_CHARTS_PAUSE); this.CreateCounter(COLLECTION_GRAPH_OBJ_COUNTER_ID,COLLECTION_GRAPH_OBJ_COUNTER_STEP,COLLECTION_GRAPH_OBJ_PAUSE); ::ResetLastError(); #ifdef __MQL5__ if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } //---__MQL4__ #else if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } #endif //--- } //+------------------------------------------------------------------+
En el temporizador de la clase, añadimos el procesamiento del temporizador para la colección de objetos gráficos:
//+------------------------------------------------------------------+ //| CEngine timer | //+------------------------------------------------------------------+ void CEngine::OnTimer(SDataCalculate &data_calculate) { //--- If this is not a tester, work with collection events by timer if(!this.IsTester()) { //--- Timer of the collections of historical orders and deals, as well as of market orders and positions int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID); CTimerCounter* cnt1=this.m_list_counters.At(index); if(cnt1!=NULL) { //--- If unpaused, work with the order, deal and position collections events if(cnt1.IsTimeDone()) this.TradeEventsControl(); } //--- Account collection timer index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID); CTimerCounter* cnt2=this.m_list_counters.At(index); if(cnt2!=NULL) { //--- If unpaused, work with the account collection events if(cnt2.IsTimeDone()) this.AccountEventsControl(); } //--- Timer 1 of the symbol collection (updating symbol quote data in the collection) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1); CTimerCounter* cnt3=this.m_list_counters.At(index); if(cnt3!=NULL) { //--- If the pause is over, update quote data of all symbols in the collection if(cnt3.IsTimeDone()) this.m_symbols.RefreshRates(); } //--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2); CTimerCounter* cnt4=this.m_list_counters.At(index); if(cnt4!=NULL) { //--- If the pause is over if(cnt4.IsTimeDone()) { //--- update data and work with events of all symbols in the collection this.SymbolEventsControl(); //--- When working with the market watch list, check the market watch window events if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH) this.MarketWatchEventsControl(); } } //--- Trading class timer index=this.CounterIndex(COLLECTION_REQ_COUNTER_ID); CTimerCounter* cnt5=this.m_list_counters.At(index); if(cnt5!=NULL) { //--- If unpaused, work with the list of pending requests if(cnt5.IsTimeDone()) this.m_trading.OnTimer(); } //--- Timeseries collection timer index=this.CounterIndex(COLLECTION_TS_COUNTER_ID); CTimerCounter* cnt6=this.m_list_counters.At(index); if(cnt6!=NULL) { //--- If the pause is over, work with the timeseries list (update all except the current one) if(cnt6.IsTimeDone()) this.SeriesRefreshAllExceptCurrent(data_calculate); } //--- Timer of timeseries collection of indicator buffer data index=this.CounterIndex(COLLECTION_IND_TS_COUNTER_ID); CTimerCounter* cnt7=this.m_list_counters.At(index); if(cnt7!=NULL) { //--- If the pause is over, work with the timeseries list of indicator data (update all except for the current one) if(cnt7.IsTimeDone()) this.IndicatorSeriesRefreshAll(); } //--- Tick series collection timer index=this.CounterIndex(COLLECTION_TICKS_COUNTER_ID); CTimerCounter* cnt8=this.m_list_counters.At(index); if(cnt8!=NULL) { //--- If the pause is over, work with the tick series list (update all except the current one) if(cnt8.IsTimeDone()) this.TickSeriesRefreshAllExceptCurrent(); } //--- Chart collection timer index=this.CounterIndex(COLLECTION_CHARTS_COUNTER_ID); CTimerCounter* cnt9=this.m_list_counters.At(index); if(cnt9!=NULL) { //--- If unpaused, work with the chart list if(cnt9.IsTimeDone()) this.ChartsRefreshAll(); } //--- Graphical objects collection timer index=this.CounterIndex(COLLECTION_GRAPH_OBJ_COUNTER_ID); CTimerCounter* cnt10=this.m_list_counters.At(index); if(cnt10!=NULL) { //--- If unpaused, work with the list of graphical objects if(cnt10.IsTimeDone()) this.GraphObjEventsControl(); } } //--- If this is a tester, work with collection events by tick else { //--- work with events of collections of orders, deals and positions by tick this.TradeEventsControl(); //--- work with events of collections of accounts by tick this.AccountEventsControl(); //--- update quote data of all collection symbols by tick this.m_symbols.RefreshRates(); //--- work with events of all symbols in the collection by tick this.SymbolEventsControl(); //--- work with the list of pending orders by tick this.m_trading.OnTimer(); //--- work with the timeseries list by tick this.SeriesRefresh(data_calculate); //--- work with the timeseries list of indicator buffers by tick this.IndicatorSeriesRefreshAll(); //--- work with the list of tick series by tick this.TickSeriesRefreshAll(); //--- work with the list of graphical objects by tick this.GraphObjEventsControl(); } } //+------------------------------------------------------------------+
Fuera del cuerpo de la clase, escribimos la implementación del método para comprobar los eventos de los objetos gráficos:
//+------------------------------------------------------------------+ //| Check the events of graphical objects | //+------------------------------------------------------------------+ void CEngine::GraphObjEventsControl(void) { //--- Check the changes in the list of graphical objects and set the flag of their events this.m_graph_objects.Refresh(); this.m_is_graph_obj_event=this.m_graph_objects.IsEvent(); //--- If there are changes in the list of graphical objects if(this.m_is_graph_obj_event) { Print(DFUN,"Graph obj is event. NewObjects: ",m_graph_objects.NewObjects()); //--- Get the last event of the graphical object property change // ... } } //+------------------------------------------------------------------+
Aquí, simplemente llamamos al método de actualización de todos los gráficos abiertos del terminal para cambiar el número de objetos gráficos en ellos.
Todas las líneas posteriores del método después de la seleccionada no se procesarán de ninguna forma: implementaremos esto en artículos posteriores. Por el momento, el método Refresh() de la clase de colección de objetos gráficos llamará uno por uno a los métodos encargados de buscar los eventos de todos los gráficos usando para ello la llamada a los métodos Refresh() correspondientes de los objetos de control de objetos gráficos que hemos analizado anteriormente. Este método (el suyo propio para cada uno de los gráficos abiertos) se imprimirá en el diario de registro sobre los cambios en el número de objetos gráficos en el gráfico correspondiente, que pondremos a prueba ahora.
Simulación
Para la simulación, vamos a tomar el asesor del artículo anterior y guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part82\ con el nuevo nombre TestDoEasyPart82.mq5.
Aquí necesitaremos realizar muy pocos cambios.
Vamos a escribir el manejador OnTimer(), que llama al temporizador de la biblioteca si el trabajo no se realiza en el simulador:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- Launch the library timer (only not in the tester) if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(rates_data); } //+------------------------------------------------------------------+
En el manejador OnChartEvent(), añadimos la prohibición de llamar al menú contextual con el botón derecho del ratón si está presionada la tecla Ctrl, y el permiso de llamar si la tecla no está pulsada: cuando la mantengamos presionada, crearemos elementos gráficos a partir del objeto de barra con una descripción del tipo de esta barra (ya implementamos esto en el último artículo), y aumentaremos ligeramente la anchura del objeto creado para que la descripción larga de la barra ("Vela con cuerpo cero") encaje por completa en ella:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If working in the tester, exit if(MQLInfoInteger(MQL_TESTER)) return; //--- If the mouse is moved if(id==CHARTEVENT_MOUSE_MOVE) { CForm *form=NULL; datetime time=0; double price=0; int wnd=0; //--- If Ctrl is not pressed, if(!IsCtrlKeyPressed()) { //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu list_forms.Clear(); ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true); return; } //--- If X and Y chart coordinates are successfully converted into time and price, if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price)) { //--- get the bar index the cursor is hovered over int index=iBarShift(Symbol(),PERIOD_CURRENT,time); if(index==WRONG_VALUE) return; //--- Get the bar index by index CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index); if(bar==NULL) return; //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates int x=(int)lparam,y=(int)dparam; if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y)) return; //--- Disable moving a chart with the mouse and showing the context menu ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false); //--- Create the form object name and hide all objects except one having such a name string name="FormBar_"+(string)index; HideFormAllExceptOne(name); //--- If the form object with such a name does not exist yet, if(!IsPresentForm(name)) { //--- create a new form object form=bar.CreateForm(index,name,x,y,114,16); if(form==NULL) return; //--- Set activity and unmoveability flags for the form form.SetActive(true); form.SetMovable(false); //--- Set the opacity of 200 form.SetOpacity(200); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(C'47,70,59'); //--- Draw the shadow drawing flag form.SetShadow(true); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(2,2,clr,200,3); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity()); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); //--- If failed to add the form object to the list, remove the form and exit the handler if(!list_forms.Add(form)) { delete form; return; } //--- Capture the form appearance form.Done(); } //--- If the form object exists, if(form!=NULL) { //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21'); form.Show(); } //--- Redraw the chart ChartRedraw(); } } } //+------------------------------------------------------------------+
Y estas son todas las mejoras que necesitábamos hacer en el asesor.
Lo iniciamos en el gráfico de un símbolo (en este caso, debería haber más de un gráfico abierto) y añadimos los objetos gráficos a cada uno de los gráficos: los mensajes sobre esto se mostrarán en el diario. Luego, en cada gráfico, presionamos la tecla Delete; esto eliminará todos los objetos gráficos seleccionados. Y los mensajes sobre esto también se mostrarán en el diario:
¿Qué es lo próximo?
En el próximo artículo, continuaremos trabajando en la colección de objetos gráficos.
Nos gustaría señalar que no todos nuestros objetos gráficos están listos: la transición hacia la creación de una colección de objetos gráficos no se ha implementado por casualidad; tendremos que almacenar los punteros a estos objetos en una lista de colecciones para su posterior desarrollo. Nos ocuparemos de ellos después de preparar su colección.
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. Podrá descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.
*Artículos de esta serie:
Gráficos en la biblioteca DoEasy (Parte 73): Objeto de formulario del elemento gráfico
Gráficos en la biblioteca DoEasy (Parte 74): Elemento gráfico básico sobre la clase CCanvas
Gráficos en la biblioteca DoEasy (Parte 75): Métodos de trabajo con primitivas y texto en el elemento gráfico básico.
Gráficos en la biblioteca DoEasy (Parte 76): Objeto de formulario y temas de color predeterminados
Gráficos en la biblioteca DoEasy (Parte 77): Clase de objeto Sombra
Gráficos en la biblioteca DoEasy (Parte 78): Fundamentos de animación en la biblioteca. Cortando las imágenes
Gráficos en la biblioteca DoEasy (Parte 79): Clase de objeto "Fotograma de animación" y sus objetos herederos
Gráficos en la biblioteca DoEasy (Parte 80): Clase de objeto "Fotograma de animación geométrica"
Gráficos en la biblioteca DoEasy (Parte 81): Integrando gráficos en los objetos de la biblioteca
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/9850
![Combinatoria y teoría de la probabilidad en el trading (Parte III): Primer modelo matemático](https://c.mql5.com/2/43/gix1_2.png)
![Combinatoria y teoría de la probabilidad en el trading (Parte II): Fractal universal](https://c.mql5.com/2/42/Centropolis2.png)
![Análisis de spread Bid/Ask en MetaTrader 5](https://c.mql5.com/2/43/bid-ask-spread.png)
![Cómo ser un mejor programador (parte 04): Aprendiendo a desarrollar más rápido](https://c.mql5.com/2/43/speed.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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