Português
preview
Del básico al intermedio: Eventos en objetos (I)

Del básico al intermedio: Eventos en objetos (I)

MetaTrader 5Ejemplos |
42 0
CODE X
CODE X

Introducción

En el artículo anterior, Del básico al intermedio: Objetos (IV), se mostró cómo podríamos modificar un objeto presente en MetaTrader 5 para construir otro objeto. Y esto con un objetivo que podría ser bastante interesante, además de ser algo que despierta mucha curiosidad. En ese artículo también se dejó un reto para ti, mi querido lector. Un reto cuya solución no voy a mostrar. Esto se debe a que quiero que procures practicar y estudiar los artículos, a fin de aprender cómo resolver, de verdad, los problemas que, con toda seguridad, irán surgiendo a medida que aprendas e intentes implementar cosas nuevas.

Aunque se trata de algo muy simple de resolver, y el problema no dificulta el uso del código con fines de estudio, creo que te sentirás mucho más orgulloso y seguro de lo que estás aprendiendo si consigues resolverlo por tu cuenta, estudiando y analizando cada situación hasta encontrar la mejor solución posible. Por lo tanto, en este artículo abordaremos otro tema que, en cierta manera, está relacionado con el trabajo con objetos gráficos, aunque sea un poco diferente de lo que se ha mostrado hasta aquí.

Tal vez ya estés imaginando que estoy dando bastante énfasis a esta cuestión de los objetos. Tal vez incluso más de lo que muchos consideran realmente necesario. Es necesario que entiendas varias cosas antes de que podamos profundizar un poco más en lo que se puede hacer en MQL5. Todo esto con un objetivo algo más amplio: crear aplicaciones más elaboradas y con algún propósito particular, que no encuentras en ninguna otra aplicación, al menos si pensamos en MetaTrader 5.

Muy bien, entonces, como de costumbre, vamos a iniciar un nuevo tema. Así podremos empezar a hablar de lo que será el tema principal de este artículo.


Eventos en objetos

Hasta el momento, se ha mostrado y explicado cómo podemos trabajar y estudiar los eventos que el usuario generará al interactuar con MetaTrader 5, vinculando dichos eventos al gráfico. Como, por ejemplo, cuando presionamos una tecla o incluso cuando usamos el ratón para alguna actividad. Como, por ejemplo, colocar algún objeto en el gráfico, lo que, aunque es un evento realizado básicamente con el uso del ratón, también genera otros eventos vinculados a él.

Y estos eventos, básicamente, hacen que MetaTrader 5 dispare otros eventos que, en este caso, están relacionados con los objetos. Y, por esta razón, se manejan de una forma diferente. Bueno, al menos eso es lo que se espera que ocurra. MetaTrader 5 dispara estos eventos precisamente para ayudar al programador y, en consecuencia, a la aplicación a lidiar con alguna actividad realizada sobre un objeto presente en el gráfico.

Estos eventos pueden dispararse cuando hacemos clic, movemos, creamos, eliminamos o incluso modificamos algo en un objeto. Pero no todos los eventos se dispararán, ya que algunos deben habilitarse. Por la sencilla razón de que, de forma predeterminada, estarán desactivados en MetaTrader 5. Sería más o menos como ocurre con los eventos del ratón. Esto se debe a que la aplicación puede no tener interés en recibir notificaciones sobre esos eventos. MetaTrader 5 simplemente ignora el disparo de esos eventos. Así, tenemos un entorno relativamente más rápido y con menos probabilidades de problemas relacionados con el rendimiento, o incluso con la estabilidad de la propia plataforma.

Pero hay momentos en que esos eventos se vuelven necesarios. Y, en ese caso, necesitamos entender cómo manejarlos. Porque puede ser necesario realizar alguna corrección o ajuste, precisamente por alguna modificación que el usuario haya hecho en un objeto gráfico.

Analizar manualmente si un objeto gráfico sufrió o no algún cambio, además de consumir mucho tiempo de CPU y volver el código absurdamente más complicado, no es algo que tú, mi querido lector y entusiasta, querrás hacer. Pero, precisamente porque MetaTrader 5 nos proporciona información sobre los cambios ocurridos en el gráfico, podemos reducir, y mucho, el trabajo de tener que observar cada uno de los objetos que coloquemos en el gráfico, lo que nos permite saber cuándo, dónde y por qué un usuario realizó algún cambio en un objeto cuyo único propósito es informar.

En este momento, tal vez no tengas idea de lo importante que es este tipo de evento, que MetaTrader 5 genera para notificar a nuestra aplicación sobre algún cambio en un objeto. Y aún más, tal vez no comprendas hasta qué punto un usuario malintencionado puede perjudicar tu aplicación cuando empieza a modificar ciertas propiedades de algunos objetos. Y por eso necesitamos hablar sobre eventos en objetos. Se trata, sin duda, de un tema que puede ayudarte a evitar problemas, o incluso a entender ciertos fallos que tu aplicación puede presentar a lo largo de su vida útil.

Al actualizar tu aplicación, también puedes reducir el riesgo de fallos. Pero, al mismo tiempo, también puedes estar dejando de analizar cuestiones que sería importante analizar.

Como, muchas veces, ciertos eventos que MetaTrader 5 dispara, originados por alguna actividad en un objeto, pueden interpretarse de forma diferente según cada aplicación, no intentaremos crear un modelo o protocolo de cómo hacer las cosas. Quiero que tú, mi querido lector, procures entender el concepto que se va a utilizar. Pero también quiero que procures entender que hay momentos en los que cada cosa debe observarse y hay momentos en los que podemos ignorar por completo ciertos tipos de actividades realizadas en un objeto, ya sea un objeto que tu aplicación colocó en el gráfico o un objeto que el usuario colocó en ese mismo gráfico. Hay un momento para todo. Así que procura entender primero el concepto utilizado, y no el código en sí, porque este puede cambiar enormemente.

Bien, entonces, antes de empezar, es necesario que una cosa quede muy clara. Normalmente, el manejo que MetaTrader 5 dará a un objeto puede no ser el más adecuado si pensamos en un propósito más específico, en el que estamos implementando algo con un objetivo muy concreto. Por lo tanto, en este tipo de situación, necesitamos establecer otras reglas, además de las que MetaTrader 5 ya adopta de forma predeterminada.

Y, para que estas reglas que estableceremos en nuestra aplicación puedan cumplirse, utilizamos el recurso que ofrece MetaTrader 5 para saber qué ocurrió con un determinado objeto. De este modo, eliminamos la necesidad de estar observando todo el tiempo lo que un usuario pueda hacer con ese objeto en particular. Si ocurre algo con un objeto, ya sea porque el usuario añade alguno al gráfico, porque lo ha modificado o incluso porque lo ha eliminado, MetaTrader 5 nos notificará y así podremos adoptar las medidas necesarias.

Entender esto es extremadamente importante. Tal vez incluso más que entender el propio código. De todos modos, hay eventos que deberán habilitarse, mientras que otros no. Y saber el momento adecuado para habilitar o incluso deshabilitar esos eventos es una de las cosas que necesitarás practicar para entender. En cualquier caso, tenemos que empezar de alguna manera. Entonces, hagámoslo de la forma más suave posible. Así pues, qué te parece si empezamos por el código que veremos a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_Prefix  "Demo"
005. //+------------------------------------------------------------------+
006. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
007. //+------------------------------------------------------------------+
008. #include <Tutorial\File 01.mqh>
009. //+------------------------------------------------------------------+
010. input double    user01 = 1.5;                   //Stop-Target Relationship
011. input bool      user02 = true;                  //Extend lines to the right
012. //+------------------------------------------------------------------+
013. st_Cross gl_Cross;
014. //+------------------------------------------------------------------+
015. int OnInit()
016. {
017.     gl_Cross.Init();
018.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
019.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
020.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
021. 
022.     return INIT_SUCCEEDED;
023. };
024. //+------------------------------------------------------------------+
025. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
026. {
027.     return rates_total;
028. };
029. //+------------------------------------------------------------------+
030. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
031. {
032. #define macro_CLEAN_EVENT   {                               \
033.             isPaint = "";                                   \
034.             gl_Cross.Hide();                                \
035.             bMouseL = false;                                \
036.             ChartSetInteger(0, CHART_MOUSE_SCROLL, true);   \
037.                             }
038. 
039.     st_TimePrice    tp;
040.     static string   isPaint = "";
041.     static bool     bMouseL = false;
042. 
043.     switch (id)
044.     {
045.         case CHARTEVENT_KEYDOWN:
046.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) macro_CLEAN_EVENT;
047.             break;
048.         case CHARTEVENT_MOUSE_MOVE:
049.             tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
050.             if (((uchar)sparam & MOUSE_LEFT) != 0)
051.             {
052.                 if (isPaint != "")
053.                 {
054.                     bMouseL = true;
055.                     if (!ObjectGetInteger(0, isPaint, OBJPROP_TIME)) ObjectMove(0, isPaint, 0, tp.Time, tp.Price);
056.                     ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
057.                 }
058.             }else if (bMouseL) macro_CLEAN_EVENT;
059.             if ((((uchar)sparam & MOUSE_MIDDLE) != 0) && (isPaint == ""))
060.             {
061.                 ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, 0, 0);
062.                 ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
063.                 Modifier_OBJ_FIBO(isPaint);
064.                 gl_Cross.Show();
065.             }
066.             break;
067.         case CHARTEVENT_MOUSE_WHEEL:
068.             break;
069.         case CHARTEVENT_OBJECT_CLICK:
070.             Comment(StringFormat("CHARTEVENT_OBJECT_CLICK\nX: %03d    Y: %03d    Object: %s", (ushort)lparam, (ushort)dparam, sparam));
071.             break;
072.         case CHARTEVENT_OBJECT_DRAG :
073.             Comment(StringFormat("CHARTEVENT_OBJECT_DRAG\nObject: %s", sparam));
074.             break;
075.     }
076.     ChartRedraw();
077. 
078. #undef macro_CLEAN_EVENT
079. };
080. //+------------------------------------------------------------------+
081. void OnDeinit(const int reason)
082. {
083.     Comment("");
084.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
085.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
086. 
087.     gl_Cross.Hide();
088. 
089.     if (reason == REASON_REMOVE)
090.         ObjectsDeleteAll(0, def_Prefix);
091. };
092. //+------------------------------------------------------------------+
093. void Modifier_OBJ_FIBO(const string szNameObj)
094. {
095. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
096.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
097.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
098.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
099.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
100.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
101.             levels++;                                                           \
102.                                                         }
103. 
104.     int levels = 0;
105. 
106.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
107.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
108.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, user02);
109. 
110.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
111.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
112.     macro_Mod_OBJ_FIBO("Partial", 1 + (user01 / 2), clrYellowGreen, 1, STYLE_DASHDOTDOT);
113.     macro_Mod_OBJ_FIBO("Take", 1 + user01, clrGreen, 2, STYLE_SOLID);
114.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
115. 
116. #undef macro_Mod_OBJ_FIBO
117. }
118. //+------------------------------------------------------------------+

Código 01

Aquí tenemos un código que vimos, explicamos y pusimos a disposición en el artículo anterior. Bueno, en realidad no es exactamente este, ya que aquí estamos haciendo dos nuevas incorporaciones al código original. Lo hago deliberadamente, basándome en algo ya conocido, precisamente para que llegues a notar que una aplicación muy compleja no surge de un momento a otro. Se va construyendo poco a poco, paso a paso. Aunque aquí no estamos centrados en crear ninguna aplicación específica. Solo en demostrar y explicar aspectos relacionados con cómo programar en MQL5, a fin de controlar el propio MetaTrader 5.

Bien, una vez compilado y añadido a un gráfico, este código 01 tendrá un comportamiento bastante interesante de observar. Ahora nos proporcionará información útil. Conviene recordar que estos datos los genera originalmente MetaTrader 5, a partir de alguna actividad realizada en el gráfico. En la siguiente animación, puedes ver un poco de lo que cabe esperar al usar la aplicación en cualquier gráfico.


Animación 01

Presta atención a la esquina superior izquierda de esta animación 01. Verás algunos mensajes cuyo propósito es informarnos cuándo ocurrió algún evento en algunos de los objetos presentes en el gráfico. Observa que se nos informa qué tipo de evento ocurrió, además del nombre del objeto y de la posición del ratón, en el caso de un evento CHARTEVENT_OBJECT_CLICK. Este tipo de eventos nos abre diversas posibilidades de uso. Son realmente muchas posibilidades, solo por el simple hecho de estar observando estos dos eventos.

Pero, como aquí estamos tratando algo muy simple y directo, no veo la necesidad de explicar realmente qué está ocurriendo en el código 01. Basta con que lo observes con calma y uses el conocimiento mostrado en estos artículos hasta este momento, para que puedas entender lo que está ocurriendo en la aplicación. Sin embargo, ten en cuenta que incluso los objetos no creados por nuestra aplicación podrán ser observados por ella.

Eso es precisamente lo que deseo que entiendas, mi querido lector. Lo que estamos haciendo es observar el comportamiento predeterminado de MetaTrader 5 para entender cómo maneja los objetos y también cómo nos notificará si llega a ocurrir algo.

Muy bien, aunque podríamos seguir añadiendo cosas nuevas al código 01, no lo haremos. Ya que esto acabaría ocultando nuestro objetivo principal, que es precisamente enseñar y transmitir un poco de mi experiencia y conocimiento sobre programación, para que consigas controlar cómo la plataforma trabajará a tu favor y no en tu contra. Así pues, vamos a crear un nuevo tipo de aplicación. Esta estará orientada a explicar algo que puede hacerse, siempre que, claro está, entiendas cómo usar MQL5 para controlar MetaTrader 5.

Y, para ello, necesito que hayas comprendido lo que se explicó esencialmente en este tema actual. Pues, sin entender esta parte, que, a mi modo de ver, es la base de todas las demás, será algo confuso entender lo que se hará un poco más adelante.


Evitando duplicados

Una de las cosas que realmente me deja un tanto aburrido es el famoso CTRL+C CTRL+V. Pero esto no se aplica aquí en MetaTrader 5. Bueno, al menos no de la forma en que gran parte espera que pueda ocurrir. Esto se debe a que, cuando seleccionas un objeto y accedes al menú de cosas que podemos hacer con él, notarás que, de forma predeterminada, MetaTrader 5 NO TIENE ESE CTRL+C CTRL+V para aplicarlo a los objetos. Lo que, para muchos, puede ser perfectamente normal y natural, mientras que para otros resulta un tanto extraño y desconcertante. Puedes verlo en la siguiente imagen.


Imagen 01

Bien, esto puede parecer un tanto extraño. Pero, aquí en MetaTrader 5, no se accede al CTRL+C CTRL+V de esta manera. Y, para decir la verdad, creo que muy pocos usuarios saben cómo hacer un CTRL+C CTRL+V en MetaTrader 5 para duplicar algún objeto presente en el gráfico.

Para entender cómo se hace esto, sería necesario explicar otras cosas. Pero, aquí, eso volvería el artículo muy aburrido y, además, no sería algo que, a mi modo de ver, despertara gran interés. Esto se debe a que gran parte de los usuarios ya se conforma con utilizar la plataforma tal como ya la utiliza. Entonces, ¿por qué cambiar eso? ¿No es así?

Pero, para lo que quiero explicar más adelante, realmente vale mucho la pena explicarte, mi querido lector, cómo podemos hacer un CTRL+C CTRL+V en cualquier objeto, aquí en MetaTrader 5, porque creo que es algo que no sabes hacer. Además, termina siendo como contenido extra para quien, por casualidad, llegue a estudiar estos artículos míos. Bien, en la siguiente animación podemos ver cómo podemos hacer un CTRL+C CTRL+V en cualquier objeto.


Animación 02

Observa que es algo muy fácil, simple e incluso divertido. Sin embargo, pocos saben que, para crear una copia de algún objeto presente en el gráfico, todo lo que tenemos que hacer es presionar la tecla CTRL en el momento en que arrastramos el objeto. Así, cuando hacemos esto, creamos una copia exacta del objeto. Aunque, en realidad, no es exactamente una copia exacta, como se explicará en breve. Y menos mal que no lo es, porque, si lo fuera, sería difícil hacer otra cosa que quizá todavía no sepas que podemos hacer.

Pues bien, cuando hacemos esto, tal como se muestra en la animación 02, estamos creando un duplicado de un objeto que ya existía en el gráfico. Pero, en algunos momentos, esto no es exactamente lo que queremos que ocurra. O, mejor dicho, hay ciertos tipos de objetos que NO QUEREMOS que tengan un duplicado en el gráfico. Esto se debe a que esos objetos pueden contener información que, si se duplica, puede terminar por confundir al operador o incluso al usuario.

Más aún cuando estos objetos pueden estar recibiendo información procedente de nuestra aplicación. En ese caso, podemos terminar viendo información conflictiva en dos objetos aparentemente iguales. Y, para dejar esto más claro, hasta el punto de que puedas entender adecuadamente lo que estoy diciendo, vamos a hacer una pequeña prueba. Esta se hará con el código que veremos a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. string gl_NameObj;
09. //+------------------------------------------------------------------+
10. int OnInit()
11. {
12.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
13. 
14.     ObjectCreate(0, gl_NameObj = macro_NameObject, OBJ_LABEL, 0, 0, 0);
15.     ObjectSetInteger(0, gl_NameObj, OBJPROP_SELECTABLE, true);
16.     ObjectSetInteger(0, gl_NameObj, OBJPROP_XDISTANCE, 50);
17.     ObjectSetInteger(0, gl_NameObj, OBJPROP_YDISTANCE, 50);
18.     ObjectSetInteger(0, gl_NameObj, OBJPROP_XSIZE, 150);
19.     ObjectSetInteger(0, gl_NameObj, OBJPROP_COLOR, clrMediumBlue);
20.     ObjectSetInteger(0, gl_NameObj, OBJPROP_FONTSIZE, 20);
21.     ObjectSetString(0, gl_NameObj, OBJPROP_FONT, "Lucida Console");
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
27. {
28.     return rates_total;
29. };
30. //+------------------------------------------------------------------+
31. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
32. {
33.     switch (id)
34.     {
35.         case CHARTEVENT_MOUSE_MOVE:
36.             ObjectSetString(0, gl_NameObj, OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam));
37.             break;
38.     }
39.     ChartRedraw();
40. };
41. //+------------------------------------------------------------------+
42. void OnDeinit(const int reason)
43. {
44.     ObjectsDeleteAll(0, def_Prefix);
45.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
46.     ChartRedraw();
47. };
48. //+------------------------------------------------------------------+

Código 02

Este código 02 servirá para ilustrar a dónde quiero llegar con esta historia del duplicado. Como es muy simple, y tú puedes entenderlo fácilmente, mi querido y estimado lector, siempre que estés estudiando el material explicado en mis artículos, no voy a explicar lo que hace. En lugar de eso, solo mostraré el tipo de resultado que podemos ver cuando lo explicado en los párrafos anteriores se pone en práctica. El resultado puede verse en la animación 03, a continuación.


Animación 03

Aquí tenemos una cuestión, que es la velocidad a la que se producen los eventos. Como estamos tratando con eventos del ratón, la velocidad es bastante alta y acabas percibiendo que hay algo extraño. Pero piensa en eventos que pueden ocurrir cada cinco minutos, o incluso cada hora. En ese caso, sería difícil notarlo. Así que ahora tal vez estés empezando a ver a dónde quiero llegar. Como quizá no sabías cómo duplicar un objeto en un gráfico, este tipo de situación, que vemos en la animación 03, difícilmente llegaría a ocurrir.

Pero, ahora que ya sabes lo sencillo que es crear un duplicado de un objeto en MetaTrader 5, este tipo de situación puede volverse bastante frecuente. Y eso, en algún momento, puede acabar generándote serios problemas. Más aún si el objeto duplicado queda delante del objeto de interés. Esto es mucho más habitual de lo que puedas imaginar y, así, genera todavía más confusión y perjuicio.

Sin embargo, podemos evitar que un objeto llegue a duplicarse. O, como mínimo, reducir la posibilidad de que esto cause conflictos. Ya que algunos tipos de objetos pueden ocultar otros objetos, según cómo se configuren. Pero aquí, y para nuestra suerte, el objeto creado es completamente inofensivo, y está orientado únicamente a fines didácticos.

Bien, ahora necesitas entender una cosa, mi querido lector. Cuando duplicamos un objeto, como se mostró aquí, MetaTrader 5 creará un nombre para ese nuevo objeto. Ese nombre sigue una regla de estandarización, por así decirlo, y se divide en tres partes. La primera parte se refiere al marco temporal del gráfico que estamos utilizando. La segunda parte será una referencia al tipo de objeto utilizado. Y la tercera parte es un número generado aleatoriamente. Nosotros ignoramos esta tercera parte, ya que su objetivo es evitar colisiones entre objetos del mismo tipo.

Bien, ¿pero qué nos dice esto? ¿Y cómo podemos utilizar este conocimiento a nuestro favor? Bien, esta será la parte divertida, mi querido lector. A partir de ciertas características, que puedes comprobar al usar el código 01 para ver cómo se crea un duplicado, podemos averiguar si se produjo o no un duplicado de un objeto. Aunque esto no es una ciencia exacta. Ya que existen complicaciones en cuanto a cómo podemos hacer las cosas y hasta dónde podemos llegar sin salir del MQL5 puro.

Una de estas complicaciones se mostrará en breve. Pero antes podemos hacer algo un poco más interesante, pensando en implementar algo cuyo objetivo sea analizar los propios eventos que MetaTrader 5 está generando en respuesta a alguna interacción del usuario con un objeto. Así, el código 02 pasará a ser el código que veremos a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. enum eBtnMouse  {
09.         MOUSE_LEFT      = 0x01, 
10.         MOUSE_RIGHT     = 0x02,
11.         MOUSE_KEY_SHIFT = 0x04,
12.         MOUSE_KEY_CTRL  = 0x08,
13.         MOUSE_MIDDLE    = 0x10,
14.         MOUSE_EXTRA_1   = 0x20,
15.         MOUSE_EXTRA_2   = 0x40
16.                 };
17. //+------------------------------------------------------------------+
18. string gl_NameObj;
19. //+------------------------------------------------------------------+
20. int OnInit()
21. {
22.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
23. 
24.     ObjectCreate(0, gl_NameObj = macro_NameObject, OBJ_LABEL, 0, 0, 0);
25.     ObjectSetInteger(0, gl_NameObj, OBJPROP_SELECTABLE, true);
26.     ObjectSetInteger(0, gl_NameObj, OBJPROP_XDISTANCE, 50);
27.     ObjectSetInteger(0, gl_NameObj, OBJPROP_YDISTANCE, 50);
28.     ObjectSetInteger(0, gl_NameObj, OBJPROP_XSIZE, 150);
29.     ObjectSetInteger(0, gl_NameObj, OBJPROP_COLOR, clrMediumBlue);
30.     ObjectSetInteger(0, gl_NameObj, OBJPROP_FONTSIZE, 20);
31.     ObjectSetString(0, gl_NameObj, OBJPROP_FONT, "Lucida Console");
32. 
33.     ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
34. 
35.     return INIT_SUCCEEDED;
36. };
37. //+------------------------------------------------------------------+
38. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
39. {
40.     return rates_total;
41. };
42. //+------------------------------------------------------------------+
43. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
44. {
45.     static bool check = true;
46. 
47.     switch (id)
48.     {
49.         case CHARTEVENT_MOUSE_MOVE:
50.             ObjectSetString(0, gl_NameObj, OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam));
51.             if (((uchar)sparam & (MOUSE_LEFT | MOUSE_KEY_CTRL)) == (MOUSE_LEFT | MOUSE_KEY_CTRL)) 
52.             {
53.                 if (check) Print(StringFormat("CHARTEVENT_MOUSE_MOVE\n%s", StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam)));
54.                 check = false;
55.             }else
56.                 check = true;
57.             break;
58.         case CHARTEVENT_OBJECT_CREATE:
59.             Print(StringFormat("CHARTEVENT_OBJECT_CREATE\nObject: %s", sparam));
60.             break;
61.         case CHARTEVENT_OBJECT_CLICK:
62.             Print(StringFormat("CHARTEVENT_OBJECT_CLICK\nX: %03d    Y: %03d    Object: %s", (ushort)lparam, (ushort)dparam, sparam));
63.             break;
64.         case CHARTEVENT_OBJECT_DRAG :
65.             Print(StringFormat("CHARTEVENT_OBJECT_DRAG\nObject: %s", sparam));
66.             break;
67.     }
68.     ChartRedraw();
69. };
70. //+------------------------------------------------------------------+
71. void OnDeinit(const int reason)
72. {
73.     Comment("");
74.     ObjectsDeleteAll(0, def_Prefix);
75.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
76.     ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, false);
77.     ChartRedraw();
78. };
79. //+------------------------------------------------------------------+

Código 03

Al mirar este código, quizá estés pensando: Vamos, pero este código es muy complicado. ¿De verdad necesitamos tanta complicación para evitar duplicar objetos en el gráfico? Bien, mi querido lector, aquí, en este código 03, todavía no estamos intentando evitar que los objetos lleguen a duplicarse. Y no, este código no es tan complicado como parece. Tal vez te lo parezca porque has caído de paracaídas justo en este artículo, sin tener una base suficiente de lo que ya se explicó en los artículos anteriores. Entonces sí, este código 03 parece una tremenda complicación.

Aun así, y por respeto a quienes siguen y estudian lo que se ha explicado en mis artículos, aquí me centraré solo en lo que es nuevo. Todo lo demás, creo que incluso los principiantes podrán entenderlo, siempre que estudien y practiquen lo que se ha explicado.

Muy bien, la primera parte que es nueva y que aquí nos interesa es la línea 33. Presta atención a esto, mi querido lector. Un evento de creación de objeto, de forma predeterminada, estará desactivado en MetaTrader 5. Sin embargo, siempre que no quieras que un objeto que estés creando dentro de tu aplicación sea detectado y dispare un evento de creación, debes desactivar este evento que estamos habilitando en esta línea 33. Como estamos habilitando este evento solo DESPUÉS de crear el objeto OBJ_LABEL, este objeto no disparará el evento de creación que, a su vez, será capturado por el procedimiento OnChartEvent.

Ahora bien, una vez que este evento haya sido habilitado, cualquier objeto colocado en el gráfico disparará un evento de creación. Esto será capturado por la línea 58, que imprimirá así un mensaje en el terminal. Al final, cuando se ejecute la línea 76, desactivaremos este evento de creación, evitando así que MetaTrader 5 siga disparando este evento en el gráfico.

Bien, el resto del código es simple de entender. Entonces, podemos pasar a la parte en la que hacemos un uso práctico de este código 03. Vamos a empezar con lo que puede verse en la siguiente animación.


Animación 04

En esta animación, podemos ver qué objetos están presentes en el gráfico. También podemos notar que el código no mostró ningún mensaje, lo que muestra que los eventos están alineados con lo que se esperaba que ocurriera, como se explicó antes.

Una vez que el código 03 esté en ejecución, podemos abrir la ventana cuyo objetivo es comprobar la lista de objetos y ver qué hay presente en el gráfico. Al abrir la lista, podemos ver la imagen que aparece a continuación.


Imagen 02

Ahora, presta atención a una cosa aquí, mi querido lector. Existen, básicamente, dos formas de evitar que los objetos lleguen a duplicarse. La primera es mantener y analizar esta lista de objetos que puedes ver en la imagen 02. Es posible hacerlo mediante código. Pero, a mi modo de ver, esto acaba consumiendo más recursos de los necesarios. Esto se debe a que, cada vez que la línea 58 capture un evento de creación, tendríamos que analizar qué ocurrió con esta lista de objetos. Lo que, a mi entender, complica el código sin necesidad, además de aumentar el costo en términos de uso de CPU.

La otra forma consiste en analizar los eventos del ratón que capture la línea 49, pero sobre todo los que hagan que el filtro de la línea 51 evalúe como verdadero. Sin embargo, este no es un mecanismo 100% perfecto. Porque hay situaciones en las que este mismo filtro puede llegar a disparar un falso positivo.

Para entender qué podría causar un falso positivo, consulta este otro artículo mío, Desarrollo de un EA de trading desde cero (Parte 23): Un nuevo sistema de órdenes (VI). Allí usamos esta combinación, que activa la línea 51 y se utiliza para duplicar un objeto, precisamente para poder enviar órdenes pendientes al servidor de trading. Pero, con un poco de paciencia, cuidado y atención, podemos llegar a sortear esta cuestión del falso positivo. Sin embargo, esto es algo que veremos en otro momento, ya que implica algunas manipulaciones que no corresponde mostrar ahora mismo. El foco aquí es exactamente otro. Es decir, entender cómo evitar que se cree un duplicado de un objeto.

Muy bien, entonces, veamos un poco más de cómo el código 03 nos informará lo que está ocurriendo en el gráfico. Esto puede verse en la siguiente animación.


Animación 05

En este caso, los únicos eventos que se generaron fueron: CHARTEVENT_OBJECT_CLICK y CHARTEVENT_OBJECT_DRAG, lo que confirma que todo está de acuerdo con lo esperado. Ahora vamos a hacer algo un poco diferente con el botón presente en el gráfico. Esto puede verse en la siguiente animación.


Animación 06

Observa algo aquí. En el momento exacto en que se hace la copia, es decir, cuando se genera un duplicado, MetaTrader 5 dispara dos eventos. Estos pueden verse mejor en la siguiente imagen.


Imagen 03

En esta imagen 03, podemos ver claramente el momento en que el objeto se copia, o en que se genera un duplicado. Ahora presta atención. Observa que el objeto original quedó inmóvil. Lo que se movió fue la copia. Como puedes ver al comparar la imagen 02 con la imagen 04, que aparece a continuación.


Imagen 04

¿Y por qué es importante saber esto? El motivo es que, según lo que quieras hacer, puede ser interesante eliminar la copia o el objeto original. Personalmente, me parece más simple eliminar la copia, ya que su nombre se nos informa al capturar el evento de creación. Sin embargo, nada impide que quieras eliminar el original y después renombrar la copia, para que todo permanezca como antes. Tal vez la intención fuera mover el objeto y acabar creando un duplicado del objeto. Aun así, me parece una opción demasiado trabajosa. Así que seguiremos por el camino más simple.

Como lo que vamos a hacer implica algunas modificaciones en el código 03, y estas eliminarán lo que se hizo y mostró anteriormente, vamos a hacerlo de otra manera. Voy a crear un nuevo archivo. Así tendrás este código 03 en el anexo, para que puedas estudiarlo con más calma, sin tener que escribirlo tú mismo. Por lo tanto, el nuevo código, con las modificaciones necesarias, puede verse a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. enum eBtnMouse  {
09.         MOUSE_LEFT      = 0x01, 
10.         MOUSE_RIGHT     = 0x02,
11.         MOUSE_KEY_SHIFT = 0x04,
12.         MOUSE_KEY_CTRL  = 0x08,
13.         MOUSE_MIDDLE    = 0x10,
14.         MOUSE_EXTRA_1   = 0x20,
15.         MOUSE_EXTRA_2   = 0x40
16.                 };
17. //+------------------------------------------------------------------+
18. string gl_NameObj;
19. //+------------------------------------------------------------------+
20. int OnInit()
21. {
22.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
23. 
24.     ObjectCreate(0, gl_NameObj = macro_NameObject, OBJ_LABEL, 0, 0, 0);
25.     ObjectSetInteger(0, gl_NameObj, OBJPROP_SELECTABLE, true);
26.     ObjectSetInteger(0, gl_NameObj, OBJPROP_XDISTANCE, 50);
27.     ObjectSetInteger(0, gl_NameObj, OBJPROP_YDISTANCE, 50);
28.     ObjectSetInteger(0, gl_NameObj, OBJPROP_XSIZE, 150);
29.     ObjectSetInteger(0, gl_NameObj, OBJPROP_COLOR, clrMediumBlue);
30.     ObjectSetInteger(0, gl_NameObj, OBJPROP_FONTSIZE, 20);
31.     ObjectSetString(0, gl_NameObj, OBJPROP_FONT, "Lucida Console");
32. 
33.     ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
34. 
35.     return INIT_SUCCEEDED;
36. };
37. //+------------------------------------------------------------------+
38. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
39. {
40.     return rates_total;
41. };
42. //+------------------------------------------------------------------+
43. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
44. {
45.     static bool check = true;
46.     static string szObjName = "";
47. 
48.     switch (id)
49.     {
50.         case CHARTEVENT_MOUSE_MOVE:
51.             ObjectSetString(0, gl_NameObj, OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam));
52.             if (((uchar)sparam & (MOUSE_LEFT | MOUSE_KEY_CTRL)) == (MOUSE_LEFT | MOUSE_KEY_CTRL)) 
53.             {
54.                 if (check)
55.                     if (ObjectDelete(0, szObjName))
56.                         Comment("Removing copy object: " + szObjName);
57.                 check = false;
58.             }else
59.                 check = true;
60.             break;
61.         case CHARTEVENT_OBJECT_CREATE:
62.             szObjName = sparam;
63.             break;
64.         case CHARTEVENT_OBJECT_CLICK:
65.             Comment(StringFormat("CHARTEVENT_OBJECT_CLICK\nX: %03d    Y: %03d    Object: %s", (ushort)lparam, (ushort)dparam, sparam));
66.             break;
67.         case CHARTEVENT_OBJECT_DRAG :
68.             Comment(StringFormat("CHARTEVENT_OBJECT_DRAG\nObject: %s", sparam));
69.             break;
70.     }
71.     ChartRedraw();
72. };
73. //+------------------------------------------------------------------+
74. void OnDeinit(const int reason)
75. {
76.     Comment("");
77.     ObjectsDeleteAll(0, def_Prefix);
78.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
79.     ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, false);
80.     ChartRedraw();
81. };
82. //+------------------------------------------------------------------+

Código 04

Cuando ejecutes este código 04, tendrás una experiencia muy parecida a la que puede verse en la siguiente animación.


Animación 07

Observa que aquí añadí un nuevo objeto sin ningún problema. Justo después, intentamos crear un duplicado de ese objeto usando el mecanismo visto en este artículo. Y fíjate en lo que se reportará en la esquina superior izquierda del gráfico. Es decir, acabamos de encontrar una forma de evitar que los objetos lleguen a duplicarse, aunque podrían hacerse otras cosas para obtener el mismo resultado.


Consideraciones finales

Aunque este artículo se haya centrado única y exclusivamente en tratar los eventos en objetos, aún falta hablar de otros tres eventos que pueden ocurrir en un objeto. Pero eso se verá en el próximo artículo. No quiero complicar demasiado las cosas para quienes están empezando ahora a aprender cómo controlar MetaTrader 5 utilizando MQL5.

Entonces, mi querido lector, procura estudiar y practicar lo que se vio en este artículo. Este tipo de conocimiento, con toda seguridad, te ayudará a entender muchas cosas sobre el funcionamiento de MetaTrader 5. Cosas que deberán comprenderse muy bien antes de que puedas decir: Sí, sé programar para controlar lo que MetaTrader 5 hará y podrá hacer.

Así que diviértete con los códigos del anexo, y nos vemos en el próximo artículo.

Archivo MQ5 Descripción
Code 01 Demostración de eventos en objetos
Code 02 Demostración de eventos en objetos
Code 03 Demostración de eventos en objetos 

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

Archivos adjuntos |
Anexo.zip (3.8 KB)
Del básico al intermedio: Eventos en Objetos (II) Del básico al intermedio: Eventos en Objetos (II)
En este artículo, veremos cómo funcionan los tres últimos tipos de eventos que puede disparar un objeto. Entender esto será muy divertido, ya que, al final, haremos algo que, para muchos, puede parecer una especie de locura, pero que es perfectamente posible y tiene un resultado bastante sorprendente.
Simulación de mercado: Iniciando SQL en MQL5 (IV) Simulación de mercado: Iniciando SQL en MQL5 (IV)
Muchos suelen infrautilizar SQL, o incluso no utilizarlo, porque no comprenden bien cómo funciona en realidad. Al consultar una base de datos SQL, no siempre buscamos una respuesta genérica; en algunos casos queremos una respuesta muy concreta y práctica. Si tú creas una base de datos con cierta estructuración y modelado, podrás introducir prácticamente cualquier tipo de información en ella.
Creación de un Panel de administración de operaciones en MQL5 (Parte XI): Interfaz moderna de funciones de comunicación (I) Creación de un Panel de administración de operaciones en MQL5 (Parte XI): Interfaz moderna de funciones de comunicación (I)
Hoy nos centramos en mejorar la interfaz de mensajería del Panel de Comunicaciones para adaptarla a los estándares de las aplicaciones de comunicación modernas y de alto rendimiento. Esta mejora se logrará actualizando la clase CommunicationsDialog. Únase a nosotros en este artículo y debate mientras exploramos ideas clave y describimos los próximos pasos para avanzar en la programación de interfaces utilizando MQL5.
Del básico al intermedio: Objetos (IV) Del básico al intermedio: Objetos (IV)
Puede que este sea el artículo más divertido hasta ahora. Esto ocurre porque aquí implementaremos una modificación de un objeto presente en MetaTrader 5 para crear otro que no existe originalmente en la plataforma. Claro, lo que verás aquí puede parecer una locura, pero funciona y tiene un objetivo muy interesante.