English Русский 中文 Deutsch 日本語 Português
preview
Simulación de mercado (Parte 03): Una cuestión de rendimiento

Simulación de mercado (Parte 03): Una cuestión de rendimiento

MetaTrader 5Ejemplos |
425 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, "Simulación de mercado (Parte 02): Orden cruzada (II)", mostré cómo controlar el símbolo o contrato observado en Chart Trade. Esto cuando estuviéramos usando un sistema en el cual no accedíamos directamente al contrato negociado, sino a otro símbolo para realizar tales operaciones. Tal símbolo, en realidad, sería el historial del contrato, y los motivos de tal procedimiento fueron explicados en el artículo anterior.

Pues bien, a pesar de que ya estamos en una etapa bastante avanzada en el desarrollo de todo el sistema, siempre necesitamos garantizar que las cosas se mantengan dentro de un nivel adecuado de seguridad, confiabilidad y rendimiento. Hacer que eso ocurra no es una tarea simple. Todo lo contrario, esto está lejos de ser simple. Tarde o temprano, acabamos enfrentándonos a algún tipo de falla o problema. Y es en esos momentos cuando la plena comprensión de cómo todo el sistema está siendo implementado será de suma importancia. Si tú no sabes o no entiendes qué está haciendo cada parte, terminarás en un callejón sin salida, donde las cosas se degradarán muy rápidamente.

Durante varias semanas, hemos estado desarrollando algunas de las aplicaciones que de hecho necesitaremos. Sin embargo, cuando comencé a implementar una nueva aplicación, que será presentada en breve, los problemas empezaron a surgir, y todo el sistema pasó a tener un rendimiento muy inferior al observado hasta entonces. Como no pretendo construir el sistema de forma monolítica, es decir, como si fuera un solo bloque, fue necesario analizar y verificar cuáles eran los motivos que estaban generando tal degradación en el rendimiento.

Pero, al estudiar el diagrama de flujo, noté que existían algunas brechas. Tales brechas deben cerrarse, pues tarde o temprano terminarán generando problemas. Entonces, resolverlas y evitar que tales problemas lleguen a surgir también forma parte del desarrollo de la aplicación final. Además de esos detalles, que serán explicados a lo largo del artículo, existe una pequeña falla que puedes notar al usar el indicador de mouse. Tal falla estaba pasando desapercibida, pero, al realizar un análisis más detallado, acabó siendo notada y corregida, y tú, querido lector, podrás ver de qué se trata.

Muchas veces, este tipo de cosa no se muestra ni se explica, y eso hace que tú, querido lector y aspirante a programador profesional, imagines erróneamente que un programador nunca comete errores. Que todo el código nace y se desarrolla sin errores ni fallas. Sin embargo, ningún código, por más bien pensado que haya sido durante la fase de planificación, está libre de fallas. Entonces, me gustaría que este artículo, además de explicativo, sirviera para mostrarte lo siguiente: un código jamás está totalmente listo. Siempre hay algo que puede mejorarse o corregirse en él.

Dichas estas palabras, vamos al artículo propiamente dicho, comenzando con el siguiente tema.


Mejorar el encapsulamiento

Uno de los peores problemas que pueden afectar un software en desarrollo es la fuga de información. Tal fuga se configura de diversas formas. Aquí, sin embargo, el problema es bastante específico. Esto se debe a que todo el código está siendo diseñado para hacer un uso intensivo de las clases y, así, de la programación orientada a objetos. La fuga ocurre cuando una clase o aplicación que está usando una determinada clase consigue acceso a algo que no debería tener. Tal falla normalmente ocurre por fallos en el encapsulamiento o porque una función o procedimiento no debería, en efecto, estar presente en la clase objeto. En nuestro caso particular, existe un procedimiento que no debería poder ser accedido por otros códigos, salvo el código específico al cual está ligado. No obstante, como yo no me había percatado del asunto, la falla permanecía.

La falla en cuestión es la función SetBuffer, presente en la clase C_Mouse. Pero ¿por qué este procedimiento constituye una falla? ¿Hay algo incorrecto en que exista ahí? Yo no diría que está mal, pero, tras observar con atención, acabé notando que aquello no era, de hecho, correcto. El problema no es que otro programa, ya sea un indicador o Asesor Experto, pueda escribir en el buffer del indicador de mouse. Eso no ocurre debido a cuestiones de seguridad de la propia implementación de MQL5. Sin embargo, no tiene ningún sentido que tal procedimiento esté presente en la clase C_Mouse, siendo que el único proceso que de hecho lo utiliza es el indicador de mouse. Al removerlo de la clase C_Mouse, mejoramos su encapsulamiento. Al mismo tiempo, garantizamos que solo el indicador de mouse haga algo en su buffer.

No obstante, al remover el procedimiento de la clase, necesitamos garantizar que siga funcionando de la misma manera. De esta forma, aseguramos la compatibilidad con cualquier cosa ya implementada y desarrollada que necesite los datos provenientes del indicador de mouse. Antes de mostrar cómo quedó la clase C_Mouse, ya que sufrió otros cambios además del mencionado arriba, veremos cómo se encuentra ahora el código del indicador de mouse. Puede verse en su totalidad justo abajo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.82"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12580"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. double GL_PriceClose;
14. datetime GL_TimeAdjust;
15. //+------------------------------------------------------------------+
16. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
17. //+------------------------------------------------------------------+
18. C_Study *Study       = NULL;
19. //+------------------------------------------------------------------+
20. input color user01   = clrBlack;                   //Price Line
21. input color user02   = clrPaleGreen;               //Positive Study
22. input color user03   = clrLightCoral;              //Negative Study
23. //+------------------------------------------------------------------+
24. C_Study::eStatusMarket m_Status;
25. int m_posBuff = 0;
26. double m_Buff[];
27. //+------------------------------------------------------------------+
28. int OnInit()
29. {
30.    Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03);
31.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
32.    MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
33.    OnBookEvent((*Study).GetInfoTerminal().szSymbol);
34.    m_Status = C_Study::eCloseMarket;
35.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
36.    ArrayInitialize(m_Buff, EMPTY_VALUE);
37.    
38.    return INIT_SUCCEEDED;
39. }
40. //+------------------------------------------------------------------+
41. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 
42.                 const double& high[], const double& low[], const double& close[], const long& tick_volume[], 
43.                 const long& volume[], const int& spread[]) 
44. {
45.    GL_PriceClose = close[rates_total - 1];
46.    if (_Symbol == def_SymbolReplay)
47.       GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService);
48.    m_posBuff = rates_total;
49.    (*Study).Update(m_Status);   
50.    
51.    return rates_total;
52. }
53. //+------------------------------------------------------------------+
54. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
55. {
56.     uCast_Double info;
57.     C_Mouse::st_Mouse sMouse;
58.     
59.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
60.    sMouse = (*Study).GetInfoMouse();            
61.    info._8b[0] = (uchar)(sMouse.ExecStudy == C_Mouse::eStudyNull ? sMouse.ButtonStatus : 0);
62.    info._16b[1] = (ushort)sMouse.Position.X_Graphics;
63.    info._16b[2] = (ushort)sMouse.Position.Y_Graphics;
64.    if (m_posBuff > 0) m_Buff[m_posBuff - 1] = info.dValue;
65. 
66.    ChartRedraw((*Study).GetInfoTerminal().ID);
67. }
68. //+------------------------------------------------------------------+
69. void OnBookEvent(const string &symbol)
70. {
71.    MqlBookInfo book[];
72.    C_Study::eStatusMarket loc = m_Status;
73.    
74.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
75.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
76.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading));
77.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
78.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
79.    if (loc != m_Status) (*Study).Update(m_Status);
80. }
81. //+------------------------------------------------------------------+
82. void OnDeinit(const int reason)
83. {
84.    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
85. 
86.    delete Study;
87. }
88. //+------------------------------------------------------------------+

Código fuente del indicador de Mouse

Observa que prácticamente no hubo cambios con respecto al código original. Puedes consultar el código original en los artículos anteriores. Sin embargo, si los seguiste detalladamente, notarás que algo diferente apareció en el procedimiento OnChartEvent. Lo que estamos haciendo allí se realizaba anteriormente en un procedimiento dentro de la clase C_Mouse, más precisamente en el SetBuffer existente en la clase. Como tal procedimiento fue eliminado, necesitamos garantizar que el buffer siga siendo escrito. Y es precisamente eso lo que hacemos en el procedimiento OnChartEvent.

¿Por qué no hacer esto en la función OnCalculate, que sería lo más natural? El motivo es que estamos tratando eventos relacionados con el mouse. La función OnCalculate es más apropiada cuando se trata de algo relacionado con el precio. No obstante, como la intención del indicador de mouse es proporcionar datos referentes al mouse, necesitamos usar una función más adecuada.

Entonces, el mismo código que estaba en SetBuffer ahora se encuentra entre las líneas 61 y 64. Sin embargo, en la línea 64, es necesario ejecutar una prueba que antes no se hacía. Esto ocurrió porque, durante algunos experimentos en el sistema de repetición/simulador, se notaron fallas extrañas relacionadas con este indicador de mouse. Al ejecutar esta prueba en la línea 64, tales fallas dejaron de existir. Por lo tanto, esa modificación llegó en buen momento, pues también resolvió otra falla.

Muy bien, además de esta mejora en el código del indicador de mouse, existen otros dos cambios que quiero mostrar. A pesar de que aún no se utilizan de hecho, ya están siendo implementados. Esto se debe a que estoy uniendo otras aplicaciones a este sistema de repetición/simulador. Tal vez incluso muestre los cambios en algunos artículos en el futuro, pero, si eso no ocurre, no quiero tener el trabajo de tener que eliminar tales códigos todo el tiempo. Pueden ser útiles en algunos casos. De cualquier forma, si tú deseas implementar algo parecido con lo que estoy mostrando, esos códigos pueden aparecer en algún momento. Si ese es el caso, ya tendrás acceso a ellos.

El primero es el archivo de encabezado, que contiene códigos de macros.mqh. Puede verse en su totalidad justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define macroRemoveSec(A)            (A - (A % 60))
05. #define macroGetDate(A)              (A - (A % 86400))
06. #define macroGetSec(A)               (A - (A - (A % 60)))
07. #define macroGetTime(A)              (A % 86400)
08. #define macroGetMin(A)               (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
09. #define macroGetHour(A)              (A - (A - ((A % 86400) - (A % 3600))))
10. #define macroHourBiggerOrEqual(A, B) ((A * 3600) < (B - (B - ((B % 86400) - (B % 3600)))))
11. #define macroMinusMinutes(A, B)      (B - ((A * 60) + (B % 60)))
12. #define macroMinusHours(A, B)        (B - (A * 3600))
13. #define macroAddHours(A, B)          (B + (A * 3600))
14. #define macroAddMin(A, B)            (B + (A * 60))
15. #define macroSetHours(A, B)          ((A * 3600) + (B - ((B % 86400))))
16. #define macroSetMin(A, B)            ((A * 60) + (B - (B % 3600)))
17. #define macroSetTime(A, B, C)        ((A * 3600) + (B * 60) + (C - (C % 86400)))
18. //+------------------------------------------------------------------+
19. #define macroColorRGBA(A, B) ((uint)((B << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
20. #define macroTransparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
21. //+------------------------------------------------------------------+

Archivo de cabecera Macros.mqh

A pesar de que gran parte de este archivo está orientada a la manipulación del tiempo, tales funciones son de gran valor para una infinidad de trabajos relacionados con dicha manipulación, cuando estés utilizando el tipo datetime o el tipo ulong para informar fechas y horarios. Esto ocurre porque el procesador es más rápido para realizar un cálculo que para llamar a una función de la biblioteca MQL5 para ejecutar el mismo trabajo. Tal vez no consigas notar la utilidad de esos cálculos, pero, de cualquier forma, ahora el archivo macros.mqh tendrá ese contenido.

El otro archivo de encabezado que también sufrió cambios fue el archivo defines.mqh. Aunque el cambio se haya restringido a la adición de una única línea, el archivo puede verse en su totalidad justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.    #define macro_DEBUG_MODE(A) \
08.                Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.    #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay         "RePlay"
14. #define def_MaxPosSlider          400
15. #define def_MaskTimeService      0xFED00000
16. #define def_IndicatorTimeFrame   (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame         4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.    double   dValue;
22.    long     _long;                                 // 1 Information
23.    datetime _datetime;                             // 1 Information
24.    uint     _32b[sizeof(double) / sizeof(uint)];   // 2 Informations
25.    ushort   _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations
26.    uchar    _8b [sizeof(double) / sizeof(uchar)];  // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents    {
30.          evTicTac,                     //Event of tic-tac
31.          evHideMouse,                  //Hide mouse price line
32.          evShowMouse,                  //Show mouse price line
33.          evHideBarTime,                //Hide bar time
34.          evShowBarTime,                //Show bar time
35.          evHideDailyVar,               //Hide daily variation
36.          evShowDailyVar,               //Show daily variation
37.          evHidePriceVar,               //Hide instantaneous variation
38.          evShowPriceVar,               //Show instantaneous variation
39.          evCtrlReplayInit,             //Initialize replay control
40.          evChartTradeBuy,              //Market buy event
41.          evChartTradeSell,             //Market sales event 
42.          evChartTradeCloseAll,         //Event to close positions
43.          evChartTrade_At_EA,           //Event to communication
44.          evEA_At_ChartTrade            //Event to communication
45.                   };
46. //+------------------------------------------------------------------+

Archivo de cabecera Defines.mqh

La línea referida es la línea 30, pero el hecho de que haya sido añadida al inicio de la enumeración cambia completamente la situación. El evento llamado "tic-tac" es un evento de sincronización. En el momento actual, aún no se usa en el código del sistema de repetición/simulador. No obstante, en el momento en que sea necesario sincronizar diversas cosas, este evento será muy solicitado. Por esta razón, y debido a algunas aplicaciones que ya están siendo desarrolladas y que necesitan dicha sincronización para mi uso personal, este evento fue añadido. Sin embargo, esto se debe al hecho que comenté arriba, donde estoy unificando todos mis proyectos dentro de este relacionado con el sistema de repetición/simulador.

Entonces, querido lector, este evento Tic-Tac es precisamente una de las partes que no vas a entender realmente por qué forma parte del código del sistema de repetición/simulador. Pero no te preocupes, pues quién sabe si en el futuro no llegue a mostrar tales aplicaciones que hacen uso de este evento.

Con esto, finalizamos esta parte. Podemos pasar a la próxima parte, donde el interés real es evitar la degradación del rendimiento del sistema. Esto porque, de hecho, comenzó a degradarse. Para separar las cosas, sin embargo, vamos a un nuevo tema.


Disminuir la degradación del rendimiento

Durante un buen tiempo, el sistema tuvo un rendimiento adecuado. No obstante, al iniciar una nueva fase, en la que empiezo a hacer un uso más intensivo de algunas cosas, se notó una degradación general en el rendimiento del sistema de repetición/simulador. Incluso cuando utilizamos los programas desarrollados para ser utilizados en cuenta real o demo, tal degradación tiene motivos muy específicos, y aquí mostraré dónde está la causa y una solución que se ajusta, por lo menos en este primer momento.

El principal problema, responsable de la degradación del rendimiento general, es el indicador de mouse. ¿Pero cómo así? No es que el indicador de mouse esté consumiendo más recursos de lo previsto, sino que, cuando empezamos a hacer un uso un poco más intenso de la lectura del buffer del indicador, las cosas comienzan a volverse algo lentas.

El motivo de esto es un procedimiento existente en la clase C_Mouse. En realidad, el problema es que, al leer el buffer del indicador, comenzamos a acumular pequeños retrasos. Esto es causado por el sistema de traducción de coordenadas gráficas en coordenadas de precio x tiempo. Si esto se hace con demasiada frecuencia, hay un retardo perceptible en la posición de la línea de precio asociada al indicador de mouse.

La forma de solucionar esto es aislar, de alguna manera, tal efecto. Esto puede hacerse leyendo menos el buffer del indicador o usando alguna otra técnica. Desafortunadamente, necesitamos mucho del mouse, ya que todo el sistema es gráfico. Sin embargo, existe una solución para este problema, sin recurrir a una solución más extrema.

Dicha solución deriva del hecho de que necesitamos, de hecho, que el indicador de mouse nos permita diferenciar cuándo estamos haciendo un estudio de cuándo no lo estamos haciendo. Este es el verdadero motivo de su creación. Cuando se está realizando un estudio, cualquier clic que ocurra deberá ser ignorado por nuestras aplicaciones en el gráfico.

Para que las aplicaciones sepan si el mouse está siendo usado en un estudio, debemos realizar una prueba. Esta prueba se ejecuta al llamar a la función CheckClick. Cuando esta retorna verdadero para el autor de llamada, sabemos que la posición del mouse podrá ser utilizada. Cuando retorna falso, indica que el mouse está en modo de estudio. Entonces, cualquier posición, en este caso, deberá ser ignorada por la aplicación que esté utilizando el mouse. Esto se aplica cuando la aplicación tiene conocimiento de la posición del mouse.

Para eso, necesitamos el indicador en el gráfico. Diversos cambios fueron necesarios para resolver todos estos problemas. Para no volver el texto muy extenso y cansador, no entraré en detalles minuciosos de los cambios, ya que, en gran parte, lo que fue necesario hacer fue remover algo de una clase y enviarlo a otra.

Lo que necesitas entender es que, antes, todo el trabajo se hacía dentro de la clase C_Mouse. Esto permitía que cualquier aplicación que fuera a hacer uso del indicador de mouse pudiera acceder a los datos necesarios. Ahora, ese trabajo está dividido entre las clases C_Mouse y C_Terminal. Sin embargo, sin que el indicador de mouse esté en el gráfico, no tendremos las aplicaciones que dependen del mouse funcionando de forma integral. Así pues, todos los cambios hicieron que la nueva jerarquía de clases pasara a ser como se muestra en la imagen abajo, considerando las aplicaciones ya construidas hasta el momento.

Imagen

La imagen de arriba muestra exactamente cómo las clases pasaron a estar estructuradas. Por lo tanto, debes imaginarlas como se muestra arriba. Con un detalle: cada rectángulo indica una aplicación diferente. Es decir, aunque la clase C_ChartFloatingRAD herede la clase C_Mouse, el indicador Chart Trade no será funcional sin el indicador de mouse en el gráfico.

Esto puede sonar extraño para muchos que, al mirar esta jerarquía de clases, piensen que el hecho de que la clase C_ChartFloatingRAD herede la clase C_Mouse haría que el indicador Chart Trade no necesitara del indicador de mouse en el mismo gráfico. Sin embargo, la forma en que tales indicadores fueron construidos hace que el indicador Chart Trade dependa, de hecho, del indicador de mouse. Si este último no está presente en el gráfico, el Chart Trade quedará inaccesible para el usuario.

Aunque este tipo de comportamiento no tenga sentido para muchos, fue pensado y admitido como adecuado. Pero observa que tanto el teclado como el mouse se conectan, de hecho, a la clase C_Terminal y a la clase C_Mouse. No obstante, la construcción de ambas clases fue hecha de forma que garantizara una menor degradación del rendimiento general. Aun así, si es necesario, el buffer del indicador de mouse contendrá los datos que podrán ser accedidos por el indicador Chart Trade. Esto no es una exclusividad del indicador Chart Trade.

Sin embargo, si deseas transferir esa información por medio de otras aplicaciones o acceder a ella en un servicio, será posible leer el buffer del indicador y obtener la misma información. Sin embargo, si deseas transferir esa información por medio de otras aplicaciones o acceder a ella en un servicio, será posible leer el buffer del indicador y obtener la misma información. Pero, en ese caso, el peso será transferido del indicador a la otra aplicación, sin que haya una degradación del rendimiento general del sistema que está siendo creado.

Además de este hecho mencionado arriba, se realizaron diversos cambios para reducir el número de llamadas a determinadas funciones. Esto ocurre porque, muchas veces, algunos valores pueden ser almacenados dentro de alguna variable privada de la clase. Cuando esto es posible y el valor de esa variable es constante durante todo el tiempo de vida de la aplicación, debemos hacer uso, preferentemente, de una variable en lugar de ejecutar una llamada para obtener el mismo valor en todo momento.

De esta forma, querido lector, notarás que muchas llamadas para obtener el ID del gráfico fueron trasladadas al constructor de la clase. En lugar de esas llamadas, se pasó a referenciar una variable que contiene el mismo valor de ID del gráfico, dado que ese valor no será modificado mientras el gráfico permanezca abierto. Aunque este tipo de procedimiento ahorre solo algunos ciclos de procesamiento, puede marcar una gran diferencia, principalmente cuando el número de llamadas a una función específica es relativamente alto.

Es poco probable que notes una gran mejora en el rendimiento general al tomar solo esta medida. Pero no subestimes esas pequeñas pérdidas, pues, a medida que se acumulan, se vuelven significativas.

Muy bien, entonces veamos cómo quedaron los nuevos códigos. Como la intención de estos artículos no es proporcionar códigos fuente, sino transmitir conocimiento, mantendré las cosas como ya se han venido haciendo desde hace algún tiempo. Es decir, no habrá código fuente en el anexo. Sin embargo, los tendrás en su totalidad justo abajo. Esto porque tales aplicaciones hacen uso de más de un archivo, y puedes notar en la imagen de arriba que existe una conexión entre ellos. Así, si publico el código no en el anexo, sino en el artículo, quien desee simplemente usar el código sin, de hecho, entender programación, no logrará construir la estructura de directorios necesaria para que el código sea compilado y genere una aplicación.

Pido disculpas a los principiantes por hacer esto de esta manera. Pero no quiero que seas del tipo que simplemente utiliza CTRL + C y CTRL + V, pues mucha gente hace precisamente eso y acaba creando verdaderos desastres, para luego pedir que alguien los ayude a hacer que el código funcione. No quiero ni deseo incentivar a nadie a hacer eso. Quiero que aprendas y comprendas lo que está ocurriendo en el programa que está siendo desarrollado. Así pues, pido disculpas a quienes desearían el código adjunto. Pero, si ya tienes cierto conocimiento de programación, lograrás obtenerlo rápidamente. Entonces, empecemos con la clase C_Terminal. La nueva clase puede verse en su totalidad justo abajo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+      
010.    public   :
011. //+------------------------------------------------------------------+      
012.       struct st_Mouse
013.       {
014.          struct st00
015.          {
016.             short    X_Adjusted,
017.                      Y_Adjusted,
018.                      X_Graphics,
019.                      Y_Graphics;
020.             double   Price;
021.             datetime dt;
022.          }Position;
023.          uchar      ButtonStatus;
024.          bool       ExecStudy;
025.       };
026. //+------------------------------------------------------------------+
027.    protected:
028.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
029. //+------------------------------------------------------------------+
030.       struct st_Terminal
031.       {
032.          ENUM_SYMBOL_CHART_MODE   ChartMode;
033.          ENUM_ACCOUNT_MARGIN_MODE TypeAccount;
034.          long           ID;
035.          string         szSymbol;
036.          int            Width,
037.                         Height,
038.                         nDigits,
039.                         SubWin,
040.                         HeightBar;
041.          double         PointPerTick,
042.                         ValuePerPoint,
043.                         VolumeMinimal,
044.                         AdjustToTrade;
045.       };
046. //+------------------------------------------------------------------+
047.       void CurrentSymbol(bool bUsingFull)
048.          {
049.             MqlDateTime mdt1;
050.             string sz0, sz1;
051.             datetime dt = macroGetDate(TimeCurrent(mdt1));
052.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
053.       
054.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
055.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
056.             switch (eTS)
057.             {
058.                case DOL   :
059.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
060.                case IND   :
061.                case WIN   : sz1 = "GJMQVZ";       break;
062.                default    : return;
063.             }
064.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
065.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
066.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
067.          }
068. //+------------------------------------------------------------------+
069. inline void DecodeMousePosition(int xi, int yi)
070.          {
071.             int w = 0;
072.             
073.             xi = (xi > 0 ? xi : 0);
074.             yi = (yi > 0 ? yi : 0);
075.             ChartXYToTimePrice(m_Infos.ID, m_Mouse.Position.X_Graphics = (short)xi, m_Mouse.Position.Y_Graphics = (short)yi, w, m_Mouse.Position.dt, m_Mouse.Position.Price);
076.             m_Mouse.Position.dt = AdjustTime(m_Mouse.Position.dt);
077.             m_Mouse.Position.Price = AdjustPrice(m_Mouse.Position.Price);
078.             ChartTimePriceToXY(m_Infos.ID, w, m_Mouse.Position.dt, m_Mouse.Position.Price, xi, yi);
079.             yi -= (int)ChartGetInteger(m_Infos.ID, CHART_WINDOW_YDISTANCE, m_Infos.SubWin);
080.             m_Mouse.Position.X_Adjusted = (short) xi;
081.             m_Mouse.Position.Y_Adjusted = (short) yi;
082.          }
083. //+------------------------------------------------------------------+
084.    private   :
085.       st_Terminal m_Infos;
086.       st_Mouse    m_Mouse;
087.       struct mem
088.       {
089.          long    Show_Descr,
090.                  Show_Date;
091.          bool    AccountLock;
092.       }m_Mem;
093. //+------------------------------------------------------------------+
094. inline void ChartChange(void)
095.          {
096.             int x, y, t;
097.             
098.             m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
099.             m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
100.             ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t);
101.             ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y);
102.             m_Infos.HeightBar = (int)(t - y) / 100;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+      
107.       C_Terminal(const long id = 0, const uchar sub = 0)
108.          {
109.             m_Infos.ID = (id == 0 ? ChartID() : id);
110.             m_Mem.AccountLock = false;
111.             m_Infos.SubWin = (int) sub;
112.             CurrentSymbol(false);
113.             ZeroMemory(m_Mouse);
114.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
115.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
116.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
117.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
118.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
119.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
120.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
121.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
122.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
123.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
124.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
125.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
126.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
127.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
128.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
129.             ChartChange();
130.          }
131. //+------------------------------------------------------------------+
132.       ~C_Terminal()
133.          {
134.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
135.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
136.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
138.          }
139. //+------------------------------------------------------------------+
140. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
141.          {
142.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
143.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
144.          }
145. //+------------------------------------------------------------------+
146. inline const st_Terminal GetInfoTerminal(void) const
147.          {
148.             return m_Infos;
149.          }
150. //+------------------------------------------------------------------+
151. inline const st_Mouse GetPositionsMouse(void) const
152.          {
153.             return m_Mouse;
154.          }
155. //+------------------------------------------------------------------+
156. const double AdjustPrice(const double arg) const
157.          {
158.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
159.          }
160. //+------------------------------------------------------------------+
161. inline datetime AdjustTime(const datetime arg)
162.          {
163.             int nSeconds= PeriodSeconds();
164.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
165.             
166.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
167.          }
168. //+------------------------------------------------------------------+
169. inline double FinanceToPoints(const double Finance, const uint Leverage)
170.          {
171.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
172.             
173.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
174.          };
175. //+------------------------------------------------------------------+
176.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
177.          {
178.             static string st_str = "";
179.             
180.             switch (id)
181.             {
182.                case CHARTEVENT_CHART_CHANGE:
183.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
184.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
185.                   ChartChange();
186.                   break;
187.                case CHARTEVENT_MOUSE_MOVE:
188.                   DecodeMousePosition((int)lparam, (int)dparam);
189.                   break;
190.                case CHARTEVENT_OBJECT_CLICK:
191.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
192.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
193.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
194.                   break;
195.                case CHARTEVENT_OBJECT_CREATE:
196.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
197.                   st_str = sparam;
198.                   break;
199.             }
200.          }
201. //+------------------------------------------------------------------+
202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const
203.          {
204.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
205.             ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0);
206.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
207.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
208.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
209.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
210.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
211.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
212.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
213.          }
214. //+------------------------------------------------------------------+
215.       bool IndicatorCheckPass(const string szShortName)
216.          {
217.             string szTmp = szShortName + "_TMP";
218.             
219.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
220.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
221.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
222.             {
223.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
224.                Print("Only one instance is allowed...");
225.                SetUserError(C_Terminal::ERR_NoMoreInstance);
226.                
227.                return false;
228.             }
229.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
230.    
231.             return true;
232.          }
233. //+------------------------------------------------------------------+
234. };

Archivo de cabecera C_Terminal.mqh

Quien está visualizando este código por primera vez puede imaginar que es grande y complejo. Sin embargo, gran parte de él permanece intacta. Se realizaron cambios, pues ahora ayudará a la clase C_Mouse a ejecutar sus tareas. Por eso, ahora tenemos el evento CHARTEVENT_MOUSE_MOVE en el procedimiento DispatchMessage. Esto porque gran parte del trabajo será realizado aquí.

Observa que, en la línea 12, ahora tenemos una estructura para proporcionar los datos del mouse. Sin embargo, el procedimiento DecodeMousePosition, presente en la línea 69, no irá, de hecho, a rellenar los datos referentes a los botones. Tal cosa será responsabilidad de la clase C_Mouse. Así que lo próximo que veremos es precisamente la clase C_Mouse, que se encuentra justo abajo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_"
007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0))
008. //+------------------------------------------------------------------+
009. class C_Mouse : public C_Terminal
010. {
011.    public   :
012.       enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
013.       enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
014. //+------------------------------------------------------------------+
015.    protected:
016. //+------------------------------------------------------------------+
017.       void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
018.          {
019.             if (!m_Mem.IsInitOk) return;
020.             CreateObjectGraphics(szName, OBJ_BUTTON);
021.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_STATE, true);
022.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_BORDER_COLOR, clrBlack);
023.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_COLOR, clrBlack);
024.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_BGCOLOR, backColor);
025.             ObjectSetString(m_Mem.id, szName, OBJPROP_FONT, "Lucida Console");
026.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_FONTSIZE, 10);
027.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
028.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_XDISTANCE, x);
029.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
030.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_XSIZE, w); 
031.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_YSIZE, 18);
032.          }
033. //+------------------------------------------------------------------+
034.    private   :
035.       enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
036.       struct st01
037.       {
038.          st_Mouse Data;
039.          color    corLineH,
040.                   corTrendP,
041.                   corTrendN;
042.          eStudy   Study;
043.       }m_Info;
044.       struct st_Mem
045.       {
046.          bool     CrossHair,
047.                   IsFull,
048.                   IsInitOk;
049.          datetime dt;
050.          string   szShortName,
051.                   szLineH,
052.                   szLineV,
053.                   szLineT,
054.                   szBtnS;
055.          long     id;
056.       }m_Mem;
057. //+------------------------------------------------------------------+
058.       void GetDimensionText(const string szArg, int &w, int &h)
059.          {
060.             TextSetFont("Lucida Console", -100, FW_NORMAL);
061.             TextGetSize(szArg, w, h);
062.             h += 5;
063.             w += 5;
064.          }
065. //+------------------------------------------------------------------+
066.       void CreateStudy(void)
067.          {
068.             if (m_Mem.IsFull)
069.             {
070.                CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH);
071.                CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH);
072.                ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_WIDTH, 2);
073.                CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy);
074.             }
075.             m_Info.Study = eStudyCreate;
076.          }
077. //+------------------------------------------------------------------+
078.       void ExecuteStudy(const double memPrice)
079.          {
080.             double v1 = GetPositionsMouse().Position.Price - memPrice;
081.             int w, h;
082.             
083.             if (!CheckClick(eClickLeft))
084.             {
085.                m_Info.Study = eStudyNull;
086.                ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, true);
087.                if (m_Mem.IsFull)   ObjectsDeleteAll(m_Mem.id, def_MousePrefixName + "T");
088.             }else if (m_Mem.IsFull)
089.             {
090.                string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
091.                                          MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetPositionsMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0));
092.                GetDimensionText(sz1, w, h);
093.                ObjectSetString(m_Mem.id, m_Mem.szBtnS, OBJPROP_TEXT, sz1);                                                
094.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
095.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XSIZE, w);
096.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YSIZE, h);
097.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetPositionsMouse().Position.X_Adjusted - w);
098.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));            
099.                ObjectMove(m_Mem.id, m_Mem.szLineT, 1, GetPositionsMouse().Position.dt, GetPositionsMouse().Position.Price);
100.                ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetPositionsMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
101.             }
102.             m_Info.Data.ButtonStatus = eKeyNull;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+
107.       C_Mouse(const long id, const string szShortName)
108.          :C_Terminal(id)
109.          {
110.             m_Mem.IsInitOk = false;
111.             m_Mem.id = GetInfoTerminal().ID;
112.             m_Mem.szShortName = szShortName;
113.          }
114. //+------------------------------------------------------------------+
115.       C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
116.          :C_Terminal(id)
117.          {
118.             m_Mem.id = GetInfoTerminal().ID;
119.             if (!(m_Mem.IsInitOk = IndicatorCheckPass(m_Mem.szShortName = szShortName))) return;
120.             m_Mem.CrossHair = (bool)ChartGetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL);
121.             ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, true);
122.             ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, false);
123.             ZeroMemory(m_Info);
124.             m_Info.corLineH  = corH;
125.             m_Info.corTrendP = corP;
126.             m_Info.corTrendN = corN;
127.             m_Info.Study = eStudyNull;
128.             if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
129.                CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH);
130.             ChartRedraw(m_Mem.id);
131.          }
132. //+------------------------------------------------------------------+
133.       ~C_Mouse()
134.          {
135.             if (!m_Mem.IsInitOk) return;
136.             ChartSetInteger(m_Mem.id, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(m_Mem.id, m_Mem.szShortName) != -1);
138.             ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
139.             ObjectsDeleteAll(m_Mem.id, def_MousePrefixName);
140.          }
141. //+------------------------------------------------------------------+
142. inline bool CheckClick(const eBtnMouse value) 
143.          {
144.             return (m_Info.Data.ButtonStatus & value) == value;
145.          }
146. //+------------------------------------------------------------------+
147. inline const st_Mouse GetInfoMouse(void)
148.          {
149.             if (!m_Mem.IsInitOk)
150.             {
151.                double Buff[];
152.                uCast_Double loc;
153.                int handle = ChartIndicatorGet(m_Mem.id, 0, m_Mem.szShortName);
154. 
155.                ZeroMemory(m_Info.Data);
156.                if (CopyBuffer(handle, 0, 0, 1, Buff) == 1)
157.                {
158.                   loc.dValue = Buff[0];
159.                   DecodeMousePosition((int)loc._16b[1], (int)loc._16b[2]);
160.                   m_Info.Data = GetPositionsMouse();
161.                   m_Info.Data.ButtonStatus = loc._8b[0];
162.                }
163.                IndicatorRelease(handle);
164.             }
165.             return m_Info.Data;
166.          }
167. //+------------------------------------------------------------------+*/
168.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
169.          {
170.             int w = 0;
171.             static double memPrice = 0;
172.       
173.             C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
174.             switch (id)
175.             {
176.                case (CHARTEVENT_CUSTOM + evHideMouse):
177.                   if (m_Mem.IsFull)   ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, clrNONE);
178.                   break;
179.                case (CHARTEVENT_CUSTOM + evShowMouse):
180.                   if (m_Mem.IsFull) ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH);
181.                   break;
182.                case CHARTEVENT_MOUSE_MOVE:
183.                   m_Info.Data = GetPositionsMouse();
184.                   if (m_Mem.IsFull) ObjectMove(m_Mem.id, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price);
185.                   if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(m_Mem.id, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0);
186.                   m_Info.Data.ButtonStatus = (uchar) sparam;
187.                   if (CheckClick(eClickMiddle) && (m_Info.Study != eStudyCreate))
188.                      if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
189.                   if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
190.                   {
191.                      ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, false);
192.                      if (m_Mem.IsFull)   ObjectMove(m_Mem.id, m_Mem.szLineT, 0, m_Mem.dt = m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
193.                      m_Info.Study = eStudyExecute;
194.                   }
195.                   if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
196.                   m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
197.                   break;
198.                case CHARTEVENT_OBJECT_DELETE:
199.                   if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH))
200.                      CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH);
201.                   break;
202.             }
203.          }
204. //+------------------------------------------------------------------+
205. };
206. //+------------------------------------------------------------------+
207. #undef macro_NameObjectStudy
208. //+------------------------------------------------------------------+

Archivo de cabecera C_Mouse.mqh

A pesar de que, aparentemente, esta clase parezca igual a la anterior, cuenta con algunos cambios. Tales cambios tienen como objetivo hacer que el uso del indicador de mouse sea más evidente. Puedes ver que ocurrieron algunos cambios. Dejo como tarea para los entusiastas y aspirantes a programador profesional analizar y encontrar esas diferencias entre el código de arriba y el código antiguo de esta clase. Créeme, será algo bastante divertido y que mostrará diversas cosas interesantes sobre la herencia de clases. A pesar de ello, quiero dejarte con la mosca detrás de la oreja, solo para despertar tu curiosidad. Quiero que mires la línea 187 y te preguntes: ¿por qué estoy probando si existe un estudio en curso durante la creación de un estudio?

Como no podía ser de otra manera, el código de la clase C_Study también pasó por cambios. No obstante, tales cambios tienen únicamente el objetivo de evitar que un valor constante, en este caso el ID del gráfico, sea buscado a cada momento mediante una llamada de función. Así, el nuevo código de la clase C_Study puede verse justo abajo, también en su totalidad.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. //+------------------------------------------------------------------+
008. class C_Study : public C_Mouse
009. {
010.    private   :
011. //+------------------------------------------------------------------+
012.       struct st00
013.       {
014.          eStatusMarket  Status;
015.          MqlRates       Rate;
016.          string         szInfo,
017.                         szBtn1,
018.                         szBtn2,
019.                         szBtn3;
020.          color          corP,
021.                         corN;
022.          int            HeightText;
023.          bool           bvT, bvD, bvP;
024.          long           id;
025.       }m_Info;
026. //+------------------------------------------------------------------+
027.       void Draw(void)
028.          {
029.             double v1;
030.             
031.             if (m_Info.bvT)
032.             {
033.                ObjectSetInteger(m_Info.id, m_Info.szBtn1, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 18);
034.                ObjectSetString(m_Info.id, m_Info.szBtn1, OBJPROP_TEXT, m_Info.szInfo);
035.             }
036.             if (m_Info.bvD)
037.             {
038.                v1 = NormalizeDouble((((GetPositionsMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
039.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
040.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
041.                ObjectSetString(m_Info.id, m_Info.szBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
042.             }
043.             if (m_Info.bvP)
044.             {
045.                v1 = NormalizeDouble((((GL_PriceClose - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
046.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
047.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
048.                ObjectSetString(m_Info.id, m_Info.szBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
049.             }
050.          }
051. //+------------------------------------------------------------------+
052. inline void CreateObjInfo(EnumEvents arg)
053.          {
054.             switch (arg)
055.             {
056.                case evShowBarTime:
057.                   C_Mouse::CreateObjToStudy(2, 110, m_Info.szBtn1 = (def_ExpansionPrefix + (string)ObjectsTotal(0)), clrPaleTurquoise);
058.                   m_Info.bvT = true;
059.                   break;
060.                case evShowDailyVar:
061.                   C_Mouse::CreateObjToStudy(2, 53, m_Info.szBtn2 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
062.                   m_Info.bvD = true;
063.                   break;
064.                case evShowPriceVar:
065.                   C_Mouse::CreateObjToStudy(58, 53, m_Info.szBtn3 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
066.                   m_Info.bvP = true;
067.                   break;
068.             }
069.          }
070. //+------------------------------------------------------------------+
071. inline void RemoveObjInfo(EnumEvents arg)
072.          {
073.             string sz;
074.             
075.             switch (arg)
076.             {
077.                case evHideBarTime:
078.                   sz = m_Info.szBtn1;
079.                   m_Info.bvT = false;
080.                   break;
081.                case evHideDailyVar:
082.                   sz = m_Info.szBtn2;
083.                   m_Info.bvD   = false;
084.                   break;
085.                case evHidePriceVar:
086.                   sz = m_Info.szBtn3;
087.                   m_Info.bvP = false;
088.                   break;
089.             }
090.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, false);
091.             ObjectDelete(m_Info.id, sz);
092.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, true);
093.          }
094. //+------------------------------------------------------------------+
095.    public   :
096. //+------------------------------------------------------------------+
097.       C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
098.          :C_Mouse(IdParam, szShortName, corH, corP, corN)
099.          {
100.             ZeroMemory(m_Info);
101.             m_Info.id = GetInfoTerminal().ID;
102.             if (_LastError >= ERR_USER_ERROR_FIRST) return;
103.             m_Info.corP = corP;
104.             m_Info.corN = corN;
105.             CreateObjInfo(evShowBarTime);
106.             CreateObjInfo(evShowDailyVar);
107.             CreateObjInfo(evShowPriceVar);
108.             ResetLastError();
109.          }
110. //+------------------------------------------------------------------+
111.       void Update(const eStatusMarket arg)
112.          {
113.             int i0;
114.             datetime dt;
115.                
116.             if (m_Info.Rate.close == 0)
117.                m_Info.Rate.close = iClose(NULL, PERIOD_D1, ((_Symbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(NULL, PERIOD_D1, 0))) ? 0 : 1));
118.             switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
119.             {
120.                case eCloseMarket   :
121.                   m_Info.szInfo = "Closed Market";
122.                   break;
123.                case eInReplay      :
124.                case eInTrading     :
125.                   i0 = PeriodSeconds();
126.                   dt = (m_Info.Status == eInReplay ? (datetime) GL_TimeAdjust : TimeCurrent());
127.                   m_Info.Rate.time = (m_Info.Rate.time <= dt ? (datetime)(((ulong) dt / i0) * i0) + i0 : m_Info.Rate.time);
128.                   if (dt > 0) m_Info.szInfo = TimeToString((datetime)m_Info.Rate.time - dt, TIME_SECONDS);
129.                   break;
130.                case eAuction      :
131.                   m_Info.szInfo = "Auction";
132.                   break;
133.                default            :
134.                   m_Info.szInfo = "ERROR";
135.             }
136.             Draw();
137.          }
138. //+------------------------------------------------------------------+
139. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
140.          {
141.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
142.             switch (id)
143.             {
144.                case CHARTEVENT_CUSTOM + evHideBarTime:
145.                   RemoveObjInfo(evHideBarTime);
146.                   break;
147.                case CHARTEVENT_CUSTOM + evShowBarTime:
148.                   CreateObjInfo(evShowBarTime);
149.                   break;
150.                case CHARTEVENT_CUSTOM + evHideDailyVar:
151.                   RemoveObjInfo(evHideDailyVar);
152.                   break;
153.                case CHARTEVENT_CUSTOM + evShowDailyVar:
154.                   CreateObjInfo(evShowDailyVar);
155.                   break;
156.                case CHARTEVENT_CUSTOM + evHidePriceVar:
157.                   RemoveObjInfo(evHidePriceVar);
158.                   break;
159.                case CHARTEVENT_CUSTOM + evShowPriceVar:
160.                   CreateObjInfo(evShowPriceVar);
161.                   break;
162.                case CHARTEVENT_MOUSE_MOVE:
163.                   Draw();
164.                   break;
165.             }
166.             ChartRedraw(m_Info.id);
167.          }
168. //+------------------------------------------------------------------+
169. };
170. //+------------------------------------------------------------------+
171. #undef def_ExpansionPrefix
172. #undef def_MousePrefixName
173. //+------------------------------------------------------------------+

Archivo de cabecera C_Study.mqh

Bien, si haces uso de todos estos códigos fuente presentados hasta ahora, lograrás, de hecho, crear el indicador de mouse. Sin embargo, aún faltan dos códigos más para terminar este artículo, ya que no quiero dejarlos para otro artículo, porque el contenido del próximo será diferente. Así, quiero dejar todo listo para evitar volver a este mismo asunto, al menos durante un buen tiempo. De esta forma, veremos los dos últimos archivos que contienen código fuente, comenzando con el código de la clase C_ChartFloatingRAD, que puede verse en su totalidad justo abajo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", m_Init.id, A)
008. #define macro_CloseIndicator(A)   {           \
009.                OnDeinit(REASON_INITFAILED);   \
010.                SetUserError(A);               \
011.                return;                        \
012.                                  }
013. //+------------------------------------------------------------------+
014. class C_ChartFloatingRAD : private C_Mouse
015. {
016.    private   :
017.       enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
018.       struct st00
019.       {
020.          short    x, y, minx, miny,
021.                   Leverage;
022.          string   szObj_Chart,
023.                   szObj_Editable,
024.                   szFileNameTemplate;
025.          long     WinHandle;
026.          double   FinanceTake,
027.                   FinanceStop;
028.          bool     IsMaximized,
029.                   IsDayTrade,
030.                   IsSaveState;
031.          struct st01
032.          {
033.             short  x, y, w, h;
034.             color  bgcolor;
035.             int    FontSize;
036.             string FontName;
037.          }Regions[MSG_NULL];
038.       }m_Info;
039.       struct st01
040.       {
041.          short     y[2];
042.          bool      bOk;
043.          long      id;
044.       }m_Init;
045. //+------------------------------------------------------------------+
046.       void CreateWindowRAD(int w, int h)
047.          {
048.             m_Info.szObj_Chart = "Chart Trade IDE";
049.             m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
050.             ObjectCreate(m_Init.id, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
051.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
052.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
053.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
054.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
055.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
056.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
057.             m_Info.WinHandle = ObjectGetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_CHART_ID);
058.          };
059. //+------------------------------------------------------------------+
060.       void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
061.          {
062.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++)
063.                if (bArg)
064.                {
065.                   Template.Add(EnumToString(c0), "bgcolor", NULL);
066.                   Template.Add(EnumToString(c0), "fontsz", NULL);
067.                   Template.Add(EnumToString(c0), "fontnm", NULL);
068.                }
069.                else
070.                {
071.                   m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
072.                   m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
073.                   m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
074.                }
075.          }
076. //+------------------------------------------------------------------+
077. inline void AdjustTemplate(const bool bFirst = false)
078.          {
079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
080.             
081.             C_AdjustTemplate    *Template;
082.             
083.             if (bFirst)
084.             {
085.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl", true);
086.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
087.                {
088.                   (*Template).Add(EnumToString(c0), "size_x", NULL);
089.                   (*Template).Add(EnumToString(c0), "size_y", NULL);
090.                   (*Template).Add(EnumToString(c0), "pos_x", NULL);
091.                   (*Template).Add(EnumToString(c0), "pos_y", NULL);
092.                }
093.                AdjustEditabled(Template, true);
094.             }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
095.             if (_LastError >= ERR_USER_ERROR_FIRST)
096.             {
097.                delete Template;
098.                
099.                return;
100.             }
101.             m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
102.             m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
103.             m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
104.             (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
105.             (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
106.             (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
107.             (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
108.             (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
109.             (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
110.             if (!(*Template).Execute())
111.             {
112.                delete Template;
113. 
114.                macro_CloseIndicator(C_Terminal::ERR_FileAcess);
115.             };
116.             if (bFirst)
117.             {
118.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
119.                {
120.                   m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x"));
121.                   m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y"));
122.                   m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x"));
123.                   m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y"));
124.                }
125.                m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
126.                AdjustEditabled(Template, false);
127.             };
128.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6));
129.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
130.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
131. 
132.             delete Template;
133.             
134.             ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
135.             ChartRedraw(m_Info.WinHandle);
136. 
137. #undef macro_PointsToFinance
138.          }
139. //+------------------------------------------------------------------+
140.       eObjectsIDE CheckMousePosition(short &x, short &y)
141.          {
142.             int xi, yi, xf, yf;
143.             st_Mouse loc;
144.             
145.             loc = GetPositionsMouse();
146.             x = loc.Position.X_Graphics;
147.             y = loc.Position.Y_Graphics;
148.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
149.             {
150.                xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
151.                yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
152.                xf = xi + m_Info.Regions[c0].w;
153.                yf = yi + m_Info.Regions[c0].h;
154.                if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
155.             }
156.             return MSG_NULL;
157.          }
158. //+------------------------------------------------------------------+
159. inline void DeleteObjectEdit(void)
160.          {
161.             ChartRedraw();
162.             ObjectsDeleteAll(m_Init.id, m_Info.szObj_Editable);
163.          }
164. //+------------------------------------------------------------------+
165.       template <typename T >
166.       void CreateObjectEditable(eObjectsIDE arg, T value)
167.          {
168.             DeleteObjectEdit();
169.             CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
170.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
171.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
172.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
173.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
174.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
175.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
176.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
177.             ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
178.             ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
179.             ChartRedraw();
180.          }
181. //+------------------------------------------------------------------+
182.       bool RestoreState(void)
183.          {
184.             uCast_Double info;
185.             bool bRet;
186.             C_AdjustTemplate *Template;
187.             
188.             if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue))
189.             {
190.                m_Info.x = (short) info._16b[0];
191.                m_Info.y = (short) info._16b[1];
192.                m_Info.minx = (short) info._16b[2];
193.                m_Info.miny = (short) info._16b[3];
194.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl");
195.                if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else
196.                {
197.                   (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL);
198.                   (*Template).Add("MSG_TAKE_VALUE", "descr", NULL);
199.                   (*Template).Add("MSG_STOP_VALUE", "descr", NULL);
200.                   (*Template).Add("MSG_DAY_TRADE", "state", NULL);
201.                   (*Template).Add("MSG_MAX_MIN", "state", NULL);
202.                   if (!(*Template).Execute()) bRet = false; else
203.                   {
204.                      m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1;
205.                      m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1;
206.                      m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr"));
207.                      m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr"));
208.                      m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr"));
209.                   }
210.                };               
211.                delete Template;
212.             };
213.             
214.             GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
215.             
216.             return bRet;
217.          }
218. //+------------------------------------------------------------------+
219.    public   :
220. //+------------------------------------------------------------------+
221.       C_ChartFloatingRAD(string szShortName, const short Leverage, const double FinanceTake, const double FinanceStop)
222.          :C_Mouse(0, "")
223.          {
224.             m_Init.id = GetInfoTerminal().ID;
225.             m_Info.IsSaveState = false;
226.             if (!IndicatorCheckPass(szShortName)) return;
227.             if (!RestoreState())
228.             {
229.                m_Info.Leverage = Leverage;
230.                m_Info.IsDayTrade = true;
231.                m_Info.FinanceTake = FinanceTake;
232.                m_Info.FinanceStop = FinanceStop;
233.                m_Info.IsMaximized = true;
234.                m_Info.minx = m_Info.x = 115;
235.                m_Info.miny = m_Info.y = 64;
236.             }
237.             m_Init.y[false] = 150;
238.             m_Init.y[true] = 210;
239.             CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]);
240.             AdjustTemplate(true);
241.          }
242. //+------------------------------------------------------------------+
243.       ~C_ChartFloatingRAD()
244.          {
245.             ChartRedraw();
246.             ObjectsDeleteAll(m_Init.id, m_Info.szObj_Chart);
247.             if (!m_Info.IsSaveState)
248.                FileDelete(m_Info.szFileNameTemplate);
249.          }
250. //+------------------------------------------------------------------+
251.       void SaveState(void)
252.          {
253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
254.             
255.             uCast_Double info;
256.             
257.             info._16b[0] = m_Info.x;
258.             info._16b[1] = m_Info.y;
259.             info._16b[2] = m_Info.minx;
260.             info._16b[3] = m_Info.miny;
261.             macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue);
262.             m_Info.IsSaveState = true;
263.             
264. #undef macro_GlobalVariable
265.          }
266. //+------------------------------------------------------------------+
267.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
268.          {
269. #define macro_AdjustMinX(A, B)    {                          \
270.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;   \
271.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;        \
272.             A = (B ? (mx > 0 ? mx : 0) : A);                 \
273.                                  }
274. #define macro_AdjustMinY(A, B)   {                           \
275.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;   \
276.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;        \
277.             A = (B ? (my > 0 ? my : 0) : A);                 \
278.                                  }
279.                               
280.             static short sx = -1, sy = -1, sz = -1;
281.             static eObjectsIDE obj = MSG_NULL;
282.             short x, y, mx, my;
283.             double dvalue;
284.             bool b1, b2, b3, b4;
285.             ushort ev = evChartTradeCloseAll;
286. 
287.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
288.             switch (id)
289.             {
290.                case CHARTEVENT_CUSTOM + evEA_At_ChartTrade:
291.                   if (m_Init.bOk = ((lparam >= 0) && (lparam < 2)))
292.                      CurrentSymbol((bool)lparam);
293.                   AdjustTemplate(true);
294.                   break;
295.                case CHARTEVENT_CHART_CHANGE:
296.                   if (!m_Init.bOk)
297.                      EventChartCustom(m_Init.id, evChartTrade_At_EA, 0, 0, "");
298.                   x = (short)ChartGetInteger(m_Init.id, CHART_WIDTH_IN_PIXELS);
299.                   y = (short)ChartGetInteger(m_Init.id, CHART_HEIGHT_IN_PIXELS);
300.                   macro_AdjustMinX(m_Info.x, b1);
301.                   macro_AdjustMinY(m_Info.y, b2);
302.                   macro_AdjustMinX(m_Info.minx, b3);
303.                   macro_AdjustMinY(m_Info.miny, b4);
304.                   if (b1 || b2 || b3 || b4) AdjustTemplate();
305.                   break;
306.                case CHARTEVENT_MOUSE_MOVE:
307.                   if (CheckClick(C_Mouse::eClickLeft))
308.                   {                  
309.                      switch (CheckMousePosition(x, y))
310.                      {
311.                         case MSG_MAX_MIN:
312.                            if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
313.                            break;
314.                         case MSG_DAY_TRADE:
315.                            if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
316.                            break;
317.                         case MSG_LEVERAGE_VALUE:
318.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage);
319.                            break;
320.                         case MSG_TAKE_VALUE:
321.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake);
322.                            break;
323.                         case MSG_STOP_VALUE:
324.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop);
325.                            break;
326.                         case MSG_TITLE_IDE:
327.                            if (sx < 0)
328.                            {
329.                               ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, false);
330.                               sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
331.                               sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
332.                            }
333.                            if ((mx = x - sx) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
334.                            if ((my = y - sy) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
335.                            if (m_Info.IsMaximized)
336.                            {
337.                               m_Info.x = (mx > 0 ? mx : m_Info.x);
338.                               m_Info.y = (my > 0 ? my : m_Info.y);
339.                            }else
340.                            {
341.                               m_Info.minx = (mx > 0 ? mx : m_Info.minx);
342.                               m_Info.miny = (my > 0 ? my : m_Info.miny);
343.                            }
344.                            break;
345.                         case MSG_BUY_MARKET:
346.                            ev = evChartTradeBuy;
347.                         case MSG_SELL_MARKET:
348.                            ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
349.                         case MSG_CLOSE_POSITION:
350.                            if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk))
351.                            {
352.                               string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
353.                                                           m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
354.                               PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
355.                               EventChartCustom(m_Init.id, ev, 0, 0, szTmp);
356.                            }
357.                            break;
358.                      }
359.                      if (sz < 0)
360.                      {
361.                         sz = x;
362.                         AdjustTemplate();
363.                         if (obj == MSG_NULL) DeleteObjectEdit();
364.                      }
365.                   }else
366.                   {
367.                      sz = -1;
368.                      if (sx > 0)
369.                      {
370.                         ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, true);                  
371.                         sx = sy = -1;
372.                      }
373.                   }
374.                   break;
375.                case CHARTEVENT_OBJECT_ENDEDIT:
376.                   switch (obj)
377.                   {
378.                      case MSG_LEVERAGE_VALUE:
379.                      case MSG_TAKE_VALUE:
380.                      case MSG_STOP_VALUE:
381.                         dvalue = StringToDouble(ObjectGetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT));
382.                         if (obj == MSG_TAKE_VALUE)
383.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
384.                         else if (obj == MSG_STOP_VALUE)
385.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
386.                         else
387.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
388.                         AdjustTemplate();
389.                         obj = MSG_NULL;
390.                         ObjectDelete(m_Init.id, m_Info.szObj_Editable);
391.                         break;
392.                   }
393.                   break;
394.                case CHARTEVENT_OBJECT_DELETE:
395.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
396.                   break;
397.             }
398.             ChartRedraw();
399.          }
400. //+------------------------------------------------------------------+
401. };
402. //+------------------------------------------------------------------+
403. #undef macro_NameGlobalVariable
404. #undef macro_CloseIndicator
405. //+------------------------------------------------------------------+

Archivo de cabecera C_ChartFloatingRAD.mqh

A pesar de parecer extenso, gran parte del código visto arriba permanece inalterado para quien ha estado siguiendo esta serie sobre repetición/simulación. No es necesario que aquellos que siguen y estudian los códigos lo modifiquen. Sin embargo, notarás que muchas de las llamadas a GetInfoTerminal fueron modificadas por m_Init.id. Esto ocurrió porque acceder a una variable es considerablemente más rápido que acceder a una función.

Entonces, en la línea 224, es posible ver dónde se captura el valor del ID del gráfico para ser utilizado posteriormente por toda la clase. No obstante, la gran cuestión aquí se da en el evento CHARTEVENT_MOUSE_MOVE, presente en la línea 306. Observa que, a pesar de que hacemos una llamada a la clase C_Mouse en la línea 307, esta devolverá un valor falso en caso de que el indicador de mouse no esté presente. Sin embargo, cuando el indicador esté presente, se devolverá un valor verdadero.

En este caso, debes prestar atención al hecho de que, en la línea 309, el procedimiento CheckMousePosition no usará los datos de lparam y dparam. Esos serían los puntos donde se encontrarían los valores de posición del mouse. Al observar este procedimiento, que se encuentra en la línea 140, podrás notar que la línea 145 irá, de hecho, a buscar los datos de posición. Estos datos fueron interpretados en la clase C_Terminal. Entonces, en las líneas 145 y 146, informamos qué valor deseamos usar. En este caso, estamos usando valores absolutos, pero también podríamos usar valores relativos.

De cualquier forma, los valores provienen de la interpretación presente en la clase C_Terminal, y no del buffer del indicador de mouse. Este tipo de cosa hace que la ejecución sea más eficiente y, en consecuencia, más rápida. No obstante, esos mismos valores estarán en el buffer del indicador de mouse.

Por último, veremos el último archivo de código fuente, ya que también pasó por cambios. Esto se debe a las modificaciones en el constructor de la clase C_ChartFloatingRAD. Estoy hablando del archivo que contiene el código fuente del indicador Chart Trade. Puede verse justo abajo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.82"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12580"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. input ushort user01 = 1;         //Leverage
18. input double user02 = 100.1;     //Finance Take
19. input double user03 = 75.4;      //Finance Stop
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.    chart = new C_ChartFloatingRAD(def_ShortName, user01, user02, user03);
24.    
25.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
26. 
27.    return INIT_SUCCEEDED;
28. }
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31. {
32.    return rates_total;
33. }
34. //+------------------------------------------------------------------+
35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
36. {
37.    if (_LastError < ERR_USER_ERROR_FIRST) 
38.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
39. }
40. //+------------------------------------------------------------------+
41. void OnDeinit(const int reason)
42. {
43.    switch (reason)
44.    {
45.       case REASON_INITFAILED:
46.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
47.          break;
48.       case REASON_CHARTCHANGE:
49.          (*chart).SaveState();
50.          break;
51.    }
52. 
53.    delete chart;
54. }
55. //+------------------------------------------------------------------+

Código fuente del indicador Chart Trade

Observa que el único cambio ocurrió en la línea 23. Sin embargo, para quienes no vieron las versiones antiguas, es posible apreciar el código en su totalidad.


Consideraciones finales

En este artículo, presenté todos los cambios que ocurrieron en el código, con el objetivo de hacerlo más sostenible. En el próximo artículo, trataremos otro tema. En el anexo de este artículo, tendrás acceso a las aplicaciones compiladas del indicador de mouse y del indicador Chart Trade. Esto te permitirá acceder a la versión más reciente, ya que puedes estar haciendo uso particular de los indicadores en tus operaciones. Entonces, nos vemos en el próximo artículo.

Archivo 03 Descripción
Experts\Expert Advisor.mq5
Demuestra la interacción entre Chart Trade y el Asesor Experto (Es necesario el Mouse Study para la interacción)
Indicadores\Chart Trade.mq5 Crea la ventana para la configuración de la orden a ser enviada (Es necesario el Mouse Study para la interacción)
Indicadores\Market Replay.mq5 Crea los controles para la interacción con el servicio de repetición/simulador (Es necesario el Mouse Study para la interacción)
Indicadores\Mouse Study.mq5 Permite la interacción entre los controles gráficos y el usuario (Necesario tanto para operar el sistema de repetición como en el mercado real)
Servicios\Market Replay.mq5 Crea y mantiene el servicio de repetición y simulación de mercado (Archivo principal de todo el sistema)


Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/12580

Archivos adjuntos |
Anexo.zip (490.53 KB)
Características del Wizard MQL5 que debe conocer (Parte 42): Oscilador ADX Características del Wizard MQL5 que debe conocer (Parte 42): Oscilador ADX
El ADX es otro indicador técnico relativamente popular utilizado por algunos traders para medir la fuerza de una tendencia predominante. Actuando como una combinación de otros dos indicadores, se presenta como un oscilador cuyos patrones exploramos en este artículo con la ayuda del asistente de ensamblaje MQL5 y sus clases de soporte.
Codificación ordinal para variables nominales Codificación ordinal para variables nominales
En este artículo, analizamos y demostramos cómo convertir predictores nominales en formatos numéricos adecuados para algoritmos de aprendizaje automático, utilizando tanto Python como MQL5.
Del básico al intermedio: Plantilla y Typename (I) Del básico al intermedio: Plantilla y Typename (I)
En este artículo, comenzaremos a tratar uno de los conceptos que muchos principiantes evitan. Esto se debe a que las plantillas no son un tema sencillo de entender y utilizar, ya que muchos no comprenden el principio básico detrás de lo que sería una plantilla: la sobrecarga de funciones y procedimientos.
Simulación de mercado (Parte 02): Orden cruzada (II) Simulación de mercado (Parte 02): Orden cruzada (II)
A diferencia de lo que se vio en el artículo anterior, aquí vamos a hacer el control de selección en el Asesor Experto. Aunque esta no es aún una solución definitiva, nos servirá por ahora. Así que acompaña el artículo para entender cómo implementar una de las soluciones posibles.