Del básico al intermedio: Objetos (II)
Introducción
En el artículo anterior, Del básico al intermedio: Objetos (I), tuvimos lo que podríamos denominar como el primer contacto con objetos gráficos presentes en MQL5, además, claro, de mostrar cómo podríamos colocar tales objetos en un gráfico, manipulándolos de una manera relativamente simple, aunque poco eficiente en términos generales, ya que no estamos, de hecho, interesados en crear aplicaciones, sino en mantener el código lo más simple y didáctico que sea posible.
Debo confesar que el artículo anterior fue bastante divertido. Sin embargo, cerca de lo que vamos a hacer, aquello fue una chatarrera sin igual. Esto se debe a que, a partir del momento en que empezamos, de hecho, a manipular y controlar objetos vía código, cosas que antes parecerían imposibles de hacer pasan a volverse cada vez más comunes.
Pero, como aún estamos bien al comienzo, tenemos diversas cosas que necesitan verse antes de poder soltar la imaginación y transformar MetaTrader 5 en algo realmente interesante y divertido, esto desde el punto de vista de programadores. Desde el punto de vista de un usuario, lo que vamos a hacer puede parecer insano y sin ningún propósito. Bien, como de costumbre, vamos a iniciar un nuevo tema para empezar a divertirnos.
Primeras manipulaciones
Lo que se hizo en el artículo anterior no puede considerarse, necesariamente, como las primeras manipulaciones de objetos. Esto por el simple hecho de que solo los colocamos en el gráfico y listo. Cualquier otra cosa que fuera necesario hacer exigiría abrir la caja de diálogo, donde tendríamos acceso a algunas propiedades del objeto y manipularlas allí. Sin embargo, cuando estamos programando las cosas, podemos ir mucho más allá, y, teóricamente, no existe límite para lo que podemos hacer, solo nuestra imaginación, esto en teoría. Ya que el hecho de que MQL5 fue diseñado con el objetivo de programar aplicaciones para ser utilizadas en MetaTrader 5, y la plataforma MetaTrader 5 tiene como objetivo la presentación de gráficos de cotización, sí existe, sí, un límite real de lo que podemos hacer directamente en MQL5.
Pero créeme, mi querido lector, este límite es realmente algo muy más allá de lo que gran parte de las personas imaginan que sería. Y, cuando se alcanza este límite, podemos utilizar programación en C y C++, a fin de ampliarlo. Pero esto ya es otro nivel, el cual esta secuencia de artículos no abordará, ya que iremos solo hasta el borde, o umbral, de lo que separaría, a mi ver, un nivel intermedio y un nivel avanzado. Donde este nivel avanzado empieza a ocurrir cuando necesitamos utilizar lenguajes como C y C++ para ampliar las capacidades de MQL5.
Entonces, volviendo a nuestra cuestión principal, para efectuar manipulaciones en los objetos, necesitamos utilizar mecanismos de entrada e interacción con el usuario. Tales mecanismos implican el uso del teclado y, o, del mouse, tratando las cosas de la forma más simple posible. Sin embargo, como tú ya debes, posiblemente, estar pensando, utilizar tales mecanismos implica capturar eventos, pues el propio sistema operativo, sea Windows, Linux u otro cualquiera, básicamente utiliza eventos para la comunicación entre el usuario y cualquier aplicación. Esto se vio en el artículo Del básico al intermedio: Eventos (I). Sin embargo, allí hablamos de manera muy superficial respecto a este tipo de cosas. Aquí, vamos a profundizar un poco más en ese tema.
Para empezar, vamos a crear un pequeño código de ejemplo, algo que todos puedan entender, incluso sin que yo necesite explicar una sola línea. Este primer código se ve justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameObj "Demo" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0); 09. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple); 10. 11. return INIT_SUCCEEDED; 12. }; 13. //+------------------------------------------------------------------+ 14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 15. { 16. static double p = 0; 17. 18. ObjectMove(0, def_NameObj, 0, 0, p); 19. 20. if (prev_calculated > 0) 21. p = price[prev_calculated - 1]; 22. 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+ 26. void OnDeinit(const int reason) 27. { 28. ObjectDelete(0, def_NameObj); 29. ChartRedraw(); 30. }; 31. //+------------------------------------------------------------------+
Código 01
Bien, si vienes estudiando y practicando lo que estoy mostrando en estos artículos, entiendes perfectamente este código, pues, de hecho, es bastante interesante y muy simple. Digo que es interesante por lo que consigue hacer. Y, tal vez, tú lo estés mirando y pensando: caramba, no consigo entender por qué dices que este código es interesante. Y, peor aún, no entiendo por qué estás haciendo este tipo de cosa en la función OnCalculate, que se muestra en la línea 14. ¿Podrías, por favor, explicarme qué está pasando aquí? Bien, mi querido lector, si tienes dudas sobre cómo funciona este código, es porque no has practicado lo que vengo mostrando en estos artículos.
Este código, cuando se ejecute, producirá algo semejante a lo que podemos ver en la animación justo abajo.

Animación 01
En este caso, usar solo una única imagen no sería suficiente, ya que tú, al observar la animación, notarás que la línea púrpura siempre va un paso detrás de la línea de cotización. A veces, esta línea púrpura toca la línea de cotización. Pero la parte interesante es: ¿por qué ocurre esto? Ya que, la mayor parte de las veces, la línea púrpura siempre estará apuntando a lo que fue el precio de la cotización anterior. Sin embargo, a veces, se iguala al precio de la cotización actual. Parece que tenemos un problema en este indicador que vemos en el código 01.
Pues bien, mi querido lector, no existe ningún problema en el código, y tampoco en la plataforma. De hecho, la línea púrpura está haciendo exactamente aquello para lo cual fue planificada, o sea, mostrar dónde estaría la última cotización. Sin embargo, si, después de haber recibido una cotización, MetaTrader dispara un nuevo evento Calculate, sin que el precio de la nueva cotización haya sido modificado, la línea púrpura coincidirá con la línea de la última cotización. Y es justamente esto lo que vemos en la animación 01. Y el motivo es justamente la línea 18. Pues, entre tomar lo que sería el último precio y lo que sería un nuevo precio de cotización, podemos tener un descuadre entre los valores. En caso de que esto esté ocurriendo, la línea púrpura siempre quedará detrás de la línea de precio. Sin embargo, si ocurre una coincidencia entre los precios, la línea púrpura coincidirá con la línea del último precio.
Genial y bastante didáctico, ¿verdad? Muchos operadores y programadores no tienen esta noción que tú acabas de conquistar, e imaginan que, siempre que se haya disparado un evento Calculate, es porque el precio cambió. Sin embargo, tú acabas de notar que esto no siempre ocurre. Por esta razón, es importante saber muy bien cómo optimizar tu código, para no hacer que una aplicación termine degradando por completo el rendimiento de MetaTrader 5.
Muy bien, ahora que ya jugamos con algo simple y aparentemente banal, vamos por fin a manipular algún tipo de evento. En este caso, vamos a empezar por lo más simple, o sea, la manipulación de eventos de teclado. Y digo que los eventos de teclado son más simples por el hecho de que no necesitamos habilitarlos ni deshabilitarlos. Siempre que se presione una tecla, MetaTrader 5 disparará un evento automáticamente. Si alguna de nuestras aplicaciones presentes en el gráfico tiene un controlador para el evento ChartEvent, tendremos la posibilidad de tratar y responder de manera adecuada al presionar una tecla. Para demostrar este primer sistema, vamos a crear un nuevo código para que responda a eventos de teclado, algo que sea simple de entender y con un objetivo bastante interesante para cualquier principiante. Este código se ve justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. switch(id) 17. { 18. case CHARTEVENT_KEYDOWN: 19. Print(lparam); 20. break; 21. } 22. }; 23. //+------------------------------------------------------------------+
Código 02
¿Alguna vez te has preguntado cómo un programador sabe qué tecla fue presionada? ¿Cómo consigue saber si se presionó una flecha o una tecla como Home o End? Parece magia, ya que las teclas alfanuméricas tú las puedes imaginar. Sin embargo, ¿y las demás teclas? Bien, mi querido lector, de hecho, el programador NO SABE eso. Pero sí sabe cómo buscar esa información. Para esto, utiliza un código parecido a este que tú puedes observar justo arriba. Este código, cuando se ejecute, te permitirá saber cuál es el código numérico que una determinada tecla, al ser presionada, generará.
En realidad, la tecla, al ser presionada, generará un código diferente de lo que estaremos viendo aquí. Sin embargo, como el sistema operativo "traduce", por así decirlo, el valor de la tecla presionada, MetaTrader 5, cuando esté ejecutando una aplicación parecida a la que se muestra en el código 02, nos mostrará cuál es el valor que debemos utilizar. En la animación justo abajo, podemos ver el resultado de una captura realizada.

Animación 02
Nota que es muy intuitivo. Cuando presionamos una tecla, se nos muestra un valor en el terminal. Así, podemos usar este valor para probar si una tecla fue o no presionada. Recordando que existe, en la biblioteca estándar de MQL5, una función que nos permite probar si algunas teclas especiales fueron o no presionadas. Pero lo que esa función hace es crear una abstracción de lo que tú acabas de aprender a hacer. Genial, ¿verdad?
Bien, pero ¿cómo podemos usar esto? Parece un tanto sin sentido hacer este tipo de cosa. Bien, entonces veamos cómo podemos usar este tipo de información que conseguimos al usar el código 02. Para esto, vamos a modificar el código 01, a fin de hacer ahora algo un poco diferente. Esta modificación se puede observar justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameObj "Demo" 05. #define def_KEY_UP 38 06. #define def_KEY_DOWN 40 07. //+------------------------------------------------------------------+ 08. int OnInit() 09. { 10. ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0); 11. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple); 12. 13. return INIT_SUCCEEDED; 14. }; 15. //+------------------------------------------------------------------+ 16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 17. { 18. return rates_total; 19. }; 20. //+------------------------------------------------------------------+ 21. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 22. { 23. static int p = 0; 24. MqlRates rate[1]; 25. 26. switch(id) 27. { 28. case CHARTEVENT_KEYDOWN: 29. switch ((int)lparam) 30. { 31. case def_KEY_DOWN: 32. p = (p < Bars(_Symbol, _Period) ? p + 1 : p); 33. break; 34. case def_KEY_UP: 35. p = (p > 0 ? p - 1 : p); 36. break; 37. default: 38. return; 39. } 40. Comment(StringFormat("Current bar analyzed: %d", p)); 41. CopyRates(_Symbol, _Period, p, rate.Size(), rate); 42. ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close); 43. break; 44. } 45. ChartRedraw(); 46. }; 47. //+------------------------------------------------------------------+ 48. void OnDeinit(const int reason) 49. { 50. Comment(""); 51. ObjectDelete(0, def_NameObj); 52. ChartRedraw(); 53. }; 54. //+------------------------------------------------------------------+
Código 03
Este código 03 es un poco más complicado que los demás que vimos en este artículo. Sin embargo, aun así, tiene muy pocas cosas nuevas haciéndose aquí. Siendo así, me voy a centrar solo en las cosas nuevas, dejando el resto de lado, ya que las demás partes serán fáciles de entender.
Muy bien, en la línea 23, iniciamos un contador local. Este servirá para decirnos cuántas barras estamos desplazando con relación a la barra más reciente en el gráfico. Para dejar esto claro, usamos la línea 40, a fin de imprimir cierta información directamente en el gráfico. Ya el código entre las líneas 29 y 39 sirve para garantizar que solo las teclas previamente definidas sean capturadas y tratadas. Ya la línea 41 buscará los datos de cotización de la barra actualmente señalada por el contador p. Nota que estamos buscando solo una única barra. Ya la línea 42 moverá el objeto en el gráfico. Así, cuando este código se ejecuta, tenemos lo que se ve en la animación justo abajo.

Animación 03
Esto sí fue interesante, pero un tanto confuso, ya que necesitamos ir contando las barras para saber cuál se está utilizando en ese momento. Sin embargo, casi me olvido de mencionar lo que hace la línea 50. Cuando quitamos el indicador del gráfico, usamos la línea 50 para eliminar textos antiguos colocados allí, algo muy simple, pero que algunos terminan olvidando hacer, lo que deja el gráfico sucio sin necesidad.
Bien, todo está muy lindo y maravilloso. Ya sabemos cómo hacer pequeñas y simples manipulaciones en un objeto. Sin embargo, hay una cosa que necesitamos aprender a hacer ahora, ya que, si lo notaste, hasta el momento, todos los objetos recibieron el mismo nombre, lo cual no es algo realmente interesante ni tampoco práctico. Y, como cada objeto presente en el gráfico necesita tener un nombre único, necesitamos pensar en una forma de poder colocar más de un objeto, al mismo tiempo, en el gráfico. Pero, para explicar cómo podemos hacer esto, vamos a un nuevo tema.
Más de un objeto en el mismo gráfico
Hay una cuestión que deja a muchos principiantes completamente perdidos, que es justamente el hecho de no entender cómo funciona un código, pero haberse interesado por él. Y, cuando decide unir diversos códigos, o incluso usar diferentes códigos en un mismo gráfico, cuyo propósito de cada uno de esos códigos sería crear algún objeto en el gráfico, termina teniendo un problema. Y, mira que estoy siendo amable al decir que el usuario no añadió objetos por cuenta propia en el gráfico. Estoy considerando solo el hecho de que esté intentando usar códigos que hacen eso por él.
El problema es que, para simplificar tanto la vida de los desarrolladores como la del propio MetaTrader 5, cada objeto que esté presente en el gráfico necesita, OBLIGATORIAMENTE, tener un nombre único. Si tú intentas crear un objeto con un nombre que ya existe en el gráfico, te generará problemas. Esto se debe a que, cuando estés intentando manipular algo que, de hecho, no estará en el gráfico, estarás, sin embargo, manipulando otra cosa que sí estará presente en el gráfico. Y esto provoca una tremenda confusión en la mente de un principiante en programación o incluso en la mente de un usuario menos experimentado.
Como, la mayor parte de las veces, los programas o aplicaciones se piensan para usarse con un propósito dado, no existe, de hecho, un cuidado por parte de un desarrollador en explicar al usuario que deberá tener cuidado con los nombres de los objetos presentes en el gráfico. Básicamente, todo desarrollador cree que ningún usuario cometerá el error de intentar unir diversas aplicaciones en una sola aplicación o incluso usar diferentes aplicaciones en un mismo gráfico, al mismo tiempo.
Pero, volviendo a la cuestión de la programación, el problema real ocurre debido al hecho de que gran parte de los principiantes solo usa la técnica de copiar y pegar, sin entender, de hecho, lo que están haciendo en el código y las consecuencias de esto durante la ejecución. Para demostrar qué tipo de problema puede causar esta técnica de copiar y pegar, que muchos principiantes utilizan, vamos a crear un pequeño código de ejemplo. Este se ve justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameObj "Demo" 05. #define def_KEY_UP 38 06. #define def_KEY_DOWN 40 07. //+------------------------------------------------------------------+ 08. int OnInit() 09. { 10. ObjectCreate(0, def_NameObj, OBJ_VLINE, 0, 0, 0); 11. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrRoyalBlue); 12. ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0); 13. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple); 14. 15. return INIT_SUCCEEDED; 16. }; 17. //+------------------------------------------------------------------+ 18. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 19. { 20. return rates_total; 21. }; 22. //+------------------------------------------------------------------+ 23. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 24. { 25. static int p = 0; 26. MqlRates rate[1]; 27. 28. switch(id) 29. { 30. case CHARTEVENT_KEYDOWN: 31. switch ((int)lparam) 32. { 33. case def_KEY_DOWN: 34. p = (p < Bars(_Symbol, _Period) ? p + 1 : p); 35. break; 36. case def_KEY_UP: 37. p = (p > 0 ? p - 1 : p); 38. break; 39. default: 40. return; 41. } 42. Comment(StringFormat("Current bar analyzed: %d", p)); 43. CopyRates(_Symbol, _Period, p, rate.Size(), rate); 44. ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close); 45. ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close); 46. break; 47. } 48. ChartRedraw(); 49. }; 50. //+------------------------------------------------------------------+ 51. void OnDeinit(const int reason) 52. { 53. Comment(""); 54. ObjectDelete(0, def_NameObj); 55. ChartRedraw(); 56. }; 57. //+------------------------------------------------------------------+
Código 04
Aquí, estoy mostrando un error bastante tosco, pero muy común en códigos de principiantes en programación, ya que su objetivo, aparentemente, sería crear una cruz en el gráfico. Sin embargo, cuando este código se ejecuta, el resultado es lo que podemos ver en la animación justo abajo.

Animación 04
Hum, qué cosa extraña. Allí, en el controlador del evento OnInit, estamos creando dos objetos, uno que sería una línea horizontal y otro que sería una línea vertical. Pero solo la línea vertical se está mostrando. ¿Por qué? Bien, mi querido lector, no es solo eso. Si te fijas con atención, terminarás notando que la línea vertical no fue definida para tener ese color que se muestra en la animación 04. Entonces, ¿qué ocurrió, de hecho, aquí?
El problema es que, a pesar de que estamos definiendo que se creen dos objetos, uno en la línea diez y el otro en la línea doce, solo se crea el objeto que se declara en la línea diez. Esto se debe a que, en el momento en que se ejecute la línea doce, MetaTrader 5 informará que ya tenemos un objeto con ese mismo nombre, definido en la línea doce, en el gráfico. Bien, en realidad, todavía no está en el gráfico, sino en la cola de ejecución, listo para ser colocado en el gráfico tan pronto como sea posible.
De cualquier manera, la creación del objeto declarado en la línea doce será rechazada. Como gran parte de los códigos son relativamente simples, muchos terminan sin prestar atención a este tipo de detalle y, peor aún, por falta de interés en estudiar la documentación, pero, principalmente, por falta de experiencia, el programador principiante acaba creyendo que el error puede estar en otro lugar. Pero, cuando esto le ocurre a un usuario que está intentando usar dos o más aplicaciones, al mismo tiempo, en el mismo gráfico y, por coincidencia, el nombre de los objetos es igual, la cosa realmente se vuelve muy confusa. Aunque sea algo extremadamente raro que ocurra, puede que tú llegues a experimentar una situación así.
Bien, existen diversos métodos para trabajar con más de un objeto en el gráfico. Pero, independientemente de esto, si tú intentas crear un objeto con el mismo nombre que otro que ya existe en el gráfico, podrías tener dificultades para lidiar con los errores que, eventualmente, surgirán. Hay casos en los que podemos usar objetos diferentes con un mismo nombre, pero no al mismo tiempo. Pero, como este es un tipo de situación muy específica, no voy a entrar en detalles, por ahora, sobre ella, para no confundir a quien esté intentando aprender la forma correcta de hacer las cosas y, así, evitar errores extraños.
De cualquier forma, una de las soluciones más simples que existe, para casi todo tipo de casos, es crear un array de objetos, donde cada objeto que tú coloques en el gráfico será representado como un elemento dentro de ese array. Esta, a mi ver, es quizá la solución más simple y genérica que consigo imaginar en este momento. Así, ese mismo código 04 puede modificarse a lo que se ve justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_KEY_UP 38 05. #define def_KEY_DOWN 40 06. //+----------------+ 07. #define macro_NameObject "Demo" + (string)ObjectsTotal(0) 08. //+------------------------------------------------------------------+ 09. string gl_Objs[2]; 10. //+------------------------------------------------------------------+ 11. int OnInit() 12. { 13. ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0); 14. ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue); 15. ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0); 16. ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+ 26. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 27. { 28. static int p = 0; 29. MqlRates rate[1]; 30. 31. switch(id) 32. { 33. case CHARTEVENT_KEYDOWN: 34. switch ((int)lparam) 35. { 36. case def_KEY_DOWN: 37. p = (p < Bars(_Symbol, _Period) ? p + 1 : p); 38. break; 39. case def_KEY_UP: 40. p = (p > 0 ? p - 1 : p); 41. break; 42. default: 43. return; 44. } 45. Comment(StringFormat("Current bar analyzed: %d", p)); 46. CopyRates(_Symbol, _Period, p, rate.Size(), rate); 47. ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close); 48. ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close); 49. break; 50. } 51. ChartRedraw(); 52. }; 53. //+------------------------------------------------------------------+ 54. void OnDeinit(const int reason) 55. { 56. Comment(""); 57. for (uint c = 0; c < gl_Objs.Size(); c++) 58. ObjectDelete(0, gl_Objs[c]); 59. ChartRedraw(); 60. }; 61. //+------------------------------------------------------------------+
Código 05
Claro que aquí estoy haciendo las cosas de la manera más simple posible. Quien viene siguiendo y practicando lo que se muestra en los artículos sabe que podemos crear una solución mucho más elaborada, con el nivel de conocimiento actual mostrado hasta aquí. Sin embargo, lo que, de hecho, nos interesa será siempre el resultado final. Este se puede ver en la animación justo abajo.

Animación 05
Nota que el resultado es completamente diferente de lo que se vio en la animación 04. Sin embargo, la parte importante es que, si comparas el código 05 con el código 04, notarás que casi no hicimos ningún cambio en el código, a fin de corregir el problema que estaba generando el código 04, siendo el punto más llamativo la declaración que se ve en la línea nueve de este código 05. Pero, como solo esto no sería, de hecho, suficiente para resolver todos los problemas, también fue necesario otro cambio. Este se puede ver en la línea siete de este código 05.
Nota que, en este caso, cambiamos la definición de lo que sería el nombre del objeto a una macro. En esta macro, tenemos algo realmente importante ocurriendo, que es el uso de la función ObjectsTotal. Esta función de la biblioteca estándar de MQL5 nos dirá cuántos objetos existen en el gráfico o están en la cola para ser colocados en el gráfico. Al hacer esto, no importa cuántos objetos vayamos a colocar en el gráfico, todos siempre tendrán un nombre único, aunque estemos usando programas diferentes, de distintos autores, ya que, si eliminamos un objeto y, justo después, creamos otro, aunque lleguen a tener un nombre muy parecido, ambos serán, de hecho, únicos, justamente debido a esta función ObjectsTotal, que podemos ver siendo utilizada en la macro.
Algo muy simple, pero muy eficiente. En el resto, los cambios que se hicieron son muy fáciles de comprender para quien viene estudiando los artículos, y, por lo tanto, no hace falta que entremos en más detalles sobre ellos. A pesar de toda esta simplicidad, no debes olvidarte del siguiente hecho: como la macro siempre va a crear un nombre único, necesitas guardar el nombre de un objeto ya creado en algún lugar. En este caso, el lugar es justamente el array de la línea nueve. Sin esto, no podrías encontrar un objeto que ya estuviera presente en el gráfico.
Ahora, antes de terminar este artículo, quiero explicar otra cosa que vamos a utilizar muchísimo, pero muchísimo, durante varios artículos futuros. Sin embargo, para separar adecuadamente las cosas, vamos a un nuevo tema.
Nombre corto
Hasta este momento, todos los códigos mostrados son casi como un código único, y, por regla general, deben usarse uno tras otro. Sin embargo, en la práctica y en un entorno real, las cosas no siempre son tan simples y simpáticas. Por esta razón, nosotros, los programadores, necesitamos tomar algunos cuidados y saber cómo hacer ciertas cosas. Entre esas cosas, está saber cómo acceder a un indicador que esté en el gráfico. Y el motivo de esto, mi querido y estimado lector, lo verás más adelante, cuando empecemos a desarrollar algunas cosas un poco más elaboradas, por así decirlo.
Como usuario, tal vez estés pensando: pero claro que sé cómo acceder a un indicador. Basta con presionar CTRL + I. Se abrirá una ventana y, en ella, se me permitirá acceder a los indicadores presentes en el gráfico. Y, si yo deseo colocar uno en el gráfico, lo único que necesito hacer es ir al navegador, buscar el indicador deseado y listo. Después, lo arrastro al gráfico, y MetaTrader 5 hará el resto. Así de simple.
Bien, como dije, esta es la visión de un usuario. Sin embargo, como programador, tenemos medios diferentes de hacer las cosas y necesitamos una visión igualmente diferente, a fin de poder comprender cómo funcionan. En este caso, esa visión de usuario no te ayudará en nada. Todo lo contrario, tal vez incluso termine dificultándote entender ciertos detalles que veremos más adelante.
Y, como quiero que empieces a acostumbrarte y a pensar como programador, necesitamos empezar a hablar de manera un poco más profunda sobre cuál sería la visión de un programador frente a lo que podemos o no hacer en MetaTrader 5. Así como también necesitamos entender cómo MetaTrader 5 entiende lo que estamos poniendo en un código en MQL5. Esto se debe a que MQL5 fue pensado para ser nuestra forma de decirle a MetaTrader 5 lo que deseamos hacer.
Bien, a diferencia de la forma en que un usuario ve una aplicación que esté corriendo en un gráfico en MetaTrader 5, nosotros, los programadores, tenemos medios de preguntarle a MetaTrader 5 qué aplicaciones se están ejecutando en ese momento. Y, con esto, podemos, de forma automática, añadir o incluso quitar una aplicación del gráfico. La mayor parte de las veces, el problema está en identificar los indicadores que estén en el gráfico. Esto se debe a que, a diferencia de lo que muchos pueden imaginar, un indicador puede tener un nombre que el código verá totalmente distinto del nombre que tendrá el ejecutable de la aplicación.
Y este nombre, que será visto por el código, no puede, o, mejor dicho, no debería ser modificado por el usuario. Aunque tú, como programador, incluso puedas permitirlo. Sin embargo, no es aconsejable, para evitar problemas cuya naturaleza, en este momento, te será difícil entender.
Genial, entonces podemos definir un nombre para nuestra aplicación, cuando estamos trabajando con indicadores. Bien, pero ¿cómo se hace esto? Cierto, mi querido lector, esto se hace usando una función presente en la biblioteca estándar de MQL5: IndicatorSetString. Aunque parezca algo banal e incluso innecesario, el hecho de definir un nombre corto para nuestro indicador hará que diversas cosas sean mucho más simples de hacer. Pero esta misma función no sirve solo para definir un nombre para nuestro indicador. También sirve para otros propósitos aún más nobles. Pero, primero, veamos cómo hacemos para definir un nombre interno para nuestro indicador. Esto se hace usando algo parecido a lo que se ve justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. IndicatorSetString(INDICATOR_SHORTNAME, "Version Demo"); 07. 08. return INIT_SUCCEEDED; 09. }; 10. //+------------------------------------------------------------------+ 11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 12. { 13. return rates_total; 14. }; 15. //+------------------------------------------------------------------+
Código 06
No importa el nombre que el usuario, o incluso tú, le dé al ejecutable de este código 06. Al final, el nombre que nos interesa será el que se define en la línea seis. Puedes poner cualquier cosa como nombre del indicador. Sin embargo, es aconsejable que definas un nombre sugerente, que diga el propósito del indicador en pocas palabras. Esto es lo que definimos como un nombre corto.
Pero, ¿por qué definir este tipo de cosa? ¿Cuál es la utilidad o el propósito de algo así? Bien, mi querido lector, explicar esto, en este preciso momento, es un tanto complicado. Esto se debe a que, para que quedara realmente claro por qué necesitamos definir un nombre corto para nuestros indicadores, sería necesario mostrar algo que, en este momento, terminaría confundiendo y complicando tu cabeza más de lo que, de hecho, aclararía las cosas. Sin embargo, quiero que procures siempre definir un nombre corto y significativo para cualquier indicador que vayas a implementar, pues, en el futuro, esto te ahorrará el trabajo de tener que definir tales nombres, en caso de que quieras usar lo que se mostrará. De cualquier forma, es una simple línea que necesitas añadir al código y te evitará muchas frustraciones futuras.
Consideraciones finales
En este artículo, se vio cómo podríamos controlar, de forma simple, algunos objetos. Vimos cómo podemos colocar más de un objeto en un mismo gráfico, usando, para esto, una aplicación. Y, además, empezamos a ver la importancia de definir un nombre corto para todo y cualquier indicador que vayamos a implementar.
Todos estos conceptos, que aparentemente parecen cosas aisladas, deben pensarse como una entidad más global. De cualquier forma, este artículo fue solo para presentar lo que sería una manera simple de lidiar con diferentes problemas, ya que, muchas veces, cuando estamos iniciando nuestro aprendizaje, terminamos cometiendo diversos errores que, a pesar de ser simples y fáciles de evitar, destruyen por completo nuestra moral y acabamos creyendo que programar es algo para pocos, cuando, en realidad, la programación es algo que todos deberían saber hacer, para poder poner sus ideas en práctica.
Por si acaso, en el anexo, voy a dejar los códigos que se vieron aquí, para que tú practiques y estudies, a fin de no cometer los mismos errores que todos cometemos cuando estamos aprendiendo a hacer las cosas.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15987
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Utilizando redes neuronales en MetaTrader
El componente View para tablas en el paradigma MQL5 MVC: Elemento gráfico base
Particularidades del trabajo con números del tipo double en MQL4
Simulación de mercado (Parte 22): Iniciando el SQL (V)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso