
Desarrollo de un sistema de repetición (Parte 74): Un nuevo Chart Trade (I)
Introducción
En el artículo anterior, "Desarrollo de un sistema de repetición (Parte 73): Una comunicación inusual (II)", concluimos la segunda etapa de desarrollo de nuestra aplicación, que será la encargada de generar el sistema de repetición/simulador. Es decir, hasta el momento hemos logrado que todo el sistema se comporte de manera adecuada. O, mejor dicho, hemos conseguido que funcione de tal manera que podamos presentar algo muy cercano a la realidad del movimiento del mercado.
Sin embargo, todo lo que se ha hecho hasta ahora es solo una parte de lo que realmente necesitamos hacer. Ahora entramos en la tercera fase de desarrollo. Inicialmente, dejaremos de centrarnos en la aplicación de repetición/simulador. El objetivo será trabajar en conjunto con el servidor de trading real. Es decir, desarrollaremos las herramientas necesarias para abrir, controlar y cerrar una posición.
Algunas de estas herramientas ya se han presentado en el pasado dentro de esta misma secuencia. Sin embargo, dado que toda la aplicación de repetición/simulación ha sido objeto de diversas modificaciones con el tiempo, necesitaremos rehacer o, como mínimo, adaptar las herramientas antiguas a un nuevo modelo. La primera de las herramientas en las que trabajaremos es: Chart Trade. Esta herramienta nos ayuda a abrir o cerrar una posición realizando las operaciones a precio de mercado. Sin embargo, MetaTrader 5 ya cuenta con estos recursos en su instalación predeterminada. Dichos botones se observan en la imagen siguiente:
A pesar de que dichos botones son muy útiles y funcionan perfectamente, no nos sirven de ninguna utilidad. Esto se debe a que están diseñados para comunicarse con el servidor de trading real. Nuestro sistema de repetición/simulación no es, de hecho, un servidor. Se trata de un servicio, o mejor dicho, de un conjunto de aplicaciones que intentan simular lo que sería un servidor real.
Por esta razón, necesitamos crear nuestras propias herramientas. Así podremos utilizar las funciones que vienen predeterminadas en MetaTrader 5, pero que no podemos utilizar cuando simulamos el servidor real.
Es importante señalar que todo este trabajo que estoy realizando para mostrar cómo se hacen las cosas se debe a que la programación que se está llevando a cabo tiene como objetivo utilizar únicamente MQL5. Esto hace que el trabajo sea aún más interesante desde el punto de vista de la implementación de la aplicación. Ha resultado muy interesante la tarea de crear algo que funcione como si fuera un servidor real de trading usando solo MQL5. Sin embargo, desde el punto de vista de la programación, sería considerablemente más sencillo crear una aplicación en C/C++ que hiciera uso de sockets. Esta implementación en C/C++ debería generar todos los protocolos necesarios para que MetaTrader 5 interpretara que está en contacto con un servidor.
Aunque este tipo de enfoque simplificaría muchas cosas, ya que nos permitiría usar MetaTrader 5 en su instalación estándar, no contribuiría a comprender mejor MQL5. Si tal implementación en C/C++ se llevara a cabo, no sería necesario hacer uso del MQL5. Piénsalo: se perdería mucha experiencia valiosa en términos de conocimiento sobre programación en MQL5. Sin embargo, como acepté el desafío de crear algo tan complejo como un simulador usando únicamente MQL5, hasta el momento he logrado cumplir mi objetivo. Ahora bien, entraremos en una fase en la que haremos una pausa en el desarrollo del repetidor/simulador. Durante un tiempo, verás que las cosas funcionarán en una cuenta de demostración, pero la implementación siempre estará diseñada pensando en su uso en el repetidor/simulador.
El nacimiento de un nuevo Chart Trade
La última vez que hablamos sobre Chart Trade fue en el artículo "Desarrollo de un sistema de repetición (Parte 47): Proyecto Chart Trade (VI). Pues bien, aunque no hemos trabajado en él desde entonces, ha llegado el momento de retomarlo. Sin embargo, dado que muchas cosas han cambiado desde entonces, gran parte de ese código ya no es útil. Está completamente obsoleto y debe descartarse. No obstante, la mayoría de los conceptos vistos en aquel momento siguen siendo válidos y aplicables. Volveremos a construirlo, pero con un pequeño detalle: el código se reformulará por completo para incorporar los nuevos conceptos desarrollados hasta el momento.
Hay algo importante que debes entender en este punto: los indicadores NO PUEDEN ABRIR, MODIFICAR NI CERRAR posiciones u órdenes. Esta función es responsabilidad del EA. Sin embargo, no trataremos Chart Trade como un código de Expert Advisor. Lo configuraremos como un indicador que se comunicará con un EA para poder llevar a cabo su tarea.
Si has comprendido los últimos artículos de esta secuencia, habrás notado que la comunicación entre los indicadores y el servicio se realizaba mediante eventos personalizados. El uso de estos eventos es, sin duda, la forma más práctica de intercambiar datos entre los programas de la aplicación de repetición/simulador. Una vez más, aunque estemos haciendo una pausa en el desarrollo del repetidor/simulador, todo seguirá diseñándose pensando en él. Por lo tanto, lo primero que debemos hacer es crear algunos nuevos valores de eventos personalizados. A continuación, se muestra el nuevo archivo Defines.mqh:
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. evHideMouse, //Hide mouse price line 31. evShowMouse, //Show mouse price line 32. evHideBarTime, //Hide bar time 33. evShowBarTime, //Show bar time 34. evHideDailyVar, //Hide daily variation 35. evShowDailyVar, //Show daily variation 36. evHidePriceVar, //Hide instantaneous variation 37. evShowPriceVar, //Show instantaneous variation 38. evCtrlReplayInit, //Initialize replay control 39. evChartTradeBuy, //Market buy event 40. evChartTradeSell, //Market sales event 41. evChartTradeCloseAll //Event to close positions 42. }; 43. //+------------------------------------------------------------------+
Código fuente del archivo Defines.mqh
Observa que los únicos cambios que se produjeron fueron la aparición de las siguientes líneas: 39, 40 y 41. Estas líneas definen los eventos que se dispararán para que el indicador Chart Trade pueda indicar al EA que debe realizar una acción. En consecuencia, es el Expert Advisor quien realmente ejecuta las acciones de apertura y cierre de posiciones. Es importante señalar que aquí no estamos hablando de órdenes, sino de posiciones. La necesidad del Chart Trade radica en reemplazar el sistema estándar del MetaTrader 5, que permite abrir o cerrar una posición, ya sea incrementando el tamaño de la operación o realizando cierres parciales. El indicador Chart Trade cumplirá exactamente esa función y, además, nos permitirá aplicar estos mismos conceptos en el repetidor/simulador. En este caso, las operaciones siempre se realizarán a precio de mercado. Más adelante trabajaremos en otro sistema que permitirá el uso de órdenes pendientes, pero por ahora avanzaremos paso a paso. El concepto más sencillo de implementar es la ejecución a precio de mercado.
Con esto aclarado, necesitamos volver a hacer funcionar el antiguo indicador presentado en el artículo mencionado al inicio de este tema. No pensarías que íbamos a reescribir toda la programación del indicador desde cero, ¿verdad? En ningún momento planteé algo así. Sería una completa y absoluta insensatez.
A continuación, veremos el código del indicador, que ha sido modificado, y los motivos que hay detrás de estos cambios, que se explicarán más adelante. Comencemos revisando el archivo de cabecera C_AdjustTemplate.mqh. Puede visualizarse íntegramente a continuación.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade" 007. #define def_BTN_BUY def_PATH_BTN + "\\BUY.bmp" 008. #define def_BTN_SELL def_PATH_BTN + "\\SELL.bmp" 009. #define def_BTN_DT def_PATH_BTN + "\\DT.bmp" 010. #define def_BTN_SW def_PATH_BTN + "\\SW.bmp" 011. #define def_BTN_MAX def_PATH_BTN + "\\MAX.bmp" 012. #define def_BTN_MIN def_PATH_BTN + "\\MIN.bmp" 013. #define def_IDE_RAD def_PATH_BTN + "\\IDE_RAD.tpl" 014. #define def_IDE_RAD "Files\\Chart Trade\\IDE_RAD.tpl" 015. //+------------------------------------------------------------------+ 016. #resource "\\" + def_BTN_BUY 017. #resource "\\" + def_BTN_SELL 018. #resource "\\" + def_BTN_DT 019. #resource "\\" + def_BTN_SW 020. #resource "\\" + def_BTN_MAX 021. #resource "\\" + def_BTN_MIN 022. #resource "\\" + def_IDE_RAD as string IdeRad; 023. //+------------------------------------------------------------------+ 024. class C_AdjustTemplate 025. { 026. private : 027. string m_szName[], 028. m_szFind[], 029. m_szReplace[], 030. m_szFileName; 031. int m_maxIndex, 032. m_FileIn, 033. m_FileOut; 034. bool m_bFirst; 035. //+------------------------------------------------------------------+ 036. public : 037. //+------------------------------------------------------------------+ 038. C_AdjustTemplate(const string szFile, const bool bFirst = false) 039. :m_maxIndex(0), 040. m_szFileName(szFile), 041. m_bFirst(bFirst), 042. m_FileIn(INVALID_HANDLE), 043. m_FileOut(INVALID_HANDLE) 044. { 045. ResetLastError(); 046. if (m_bFirst) 047. { 048. int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE); 049. FileWriteString(handle, IdeRad); 050. FileClose(handle); 051. } 052. if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess); 053. if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess); 054. } 055. //+------------------------------------------------------------------+ 056. ~C_AdjustTemplate() 057. { 058. FileClose(m_FileIn); 059. FileClose(m_FileOut); 060. FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE); 061. ArrayResize(m_szName, 0); 062. ArrayResize(m_szFind, 0); 063. ArrayResize(m_szReplace, 0); 064. } 065. //+------------------------------------------------------------------+ 066. void Add(const string szName, const string szFind, const string szReplace) 067. { 068. m_maxIndex++; 069. ArrayResize(m_szName, m_maxIndex); 070. ArrayResize(m_szFind, m_maxIndex); 071. ArrayResize(m_szReplace, m_maxIndex); 072. m_szName[m_maxIndex - 1] = szName; 073. m_szFind[m_maxIndex - 1] = szFind; 074. m_szReplace[m_maxIndex - 1] = szReplace; 075. } 076. //+------------------------------------------------------------------+ 077. string Get(const string szName, const string szFind) 078. { 079. for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0]; 080. 081. return NULL; 082. } 083. //+------------------------------------------------------------------+ 084. void Execute(void) 085. bool Execute(void) 086. { 087. string sz0, tmp, res[]; 088. int count0 = 0, i0; 089. 090. if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS)) 091. if ((m_FileIn == INVALID_HANDLE) || (m_FileOut == INVALID_HANDLE)) return false; 092. while (!FileIsEnding(m_FileIn)) 093. { 094. sz0 = FileReadString(m_FileIn); 095. if (sz0 == "<object>") count0 = 1; 096. if (sz0 == "</object>") count0 = 0; 097. if (count0 > 0) if (StringSplit(sz0, '=', res) > 1) 098. { 099. if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off"))) 100. sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1]; 101. sz0 = res[0] + "=\\Indicators\\Chart Trade.ex5::" + def_PATH_BTN + res[1]; 102. i0 = (count0 == 1 ? 0 : i0); 103. for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0); 104. for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0])) 105. { 106. if (StringLen(m_szReplace[c0])) sz0 = m_szFind[c0] + "=" + m_szReplace[c0]; 107. else m_szReplace[c0] = res[1]; 108. } 109. } 110. if (FileWriteString(m_FileOut, sz0 + "\r\n") < 2) return false; 111. }; 112. 113. return true; 114. } 115. //+------------------------------------------------------------------+ 116. }; 117. //+------------------------------------------------------------------+ 118. #undef def_BTN_BUY 119. #undef def_BTN_SELL 120. #undef def_BTN_DT 121. #undef def_BTN_SW 122. #undef def_BTN_MAX 123. #undef def_BTN_MIN 124. #undef def_IDE_RAD 125. #undef def_PATH_BTN 126. //+------------------------------------------------------------------+
Código fuente del archivo C_AdjustTemplate.mqh
Observa que hay líneas que han sido eliminadas en este código. Vamos a ver cuál es el motivo de estos cambios. La línea 14 se ha modificado; ahora utilizamos el código mostrado en la línea 13. Este cambio implica que la ubicación del archivo de la plantilla se ha trasladado a una nueva ubicación. No te preocupes, en el anexo tendrás acceso a estos archivos, si es que aún no los tienes. Todo lo que necesitas hacer es mantener la misma estructura del anexo. De esta forma, los recursos declarados aquí podrán encontrarse sin problemas. Muy bien, además de este cambio, se han eliminado otras líneas.
La línea 45 se eliminó por el motivo explicado en los artículos anteriores. Es decir, a veces la variable _LastError contiene un valor que no indica necesariamente un error en la ejecución del código, sino que se debe a otra razón. Por esta razón, no me preocuparé por los valores que pueda contener la variable _LastError, salvo en casos excepcionales.
Veamos ahora el caso de la línea 90: ¿por qué se eliminó? El motivo es que la variable _LastError puede contener un valor distinto a ERR_SUCCESS que no fue causado por nuestra aplicación. Sin embargo, precisamente para evitar posibles conflictos de interés, la situación ha cambiado aquí.
En este punto, lo que antes era un procedimiento se ha convertido en una función. Vayamos a la línea 84. Verás que está eliminada y, en su lugar, está la línea 85. Pero, ¿por qué se ha realizado este cambio? El motivo es que podemos evitar ciertos errores y no necesitamos utilizar de forma masiva y directa la variable _LastError.
Es importante que comprendas que este indicador tiene unos niveles de exigencia bastante altos. A diferencia de las órdenes pendientes, donde un pequeño error puede ser tolerado en ocasiones (y digo «en ocasiones» por razones que exploraremos en el futuro), en este indicador no podemos tolerar ciertos errores. Esto se debe a que aquí estamos trabajando con un sistema de mercado.
Además, existe un agravante aún más grave que analizaremos en otro momento. Por esta razón, debemos garantizar que los datos contenidos en el archivo de la plantilla sean correctos.
De esta forma, si las líneas 91 y 110 indican que ha ocurrido un fallo, debemos reportarlo al autor de la llamada, que se encuentra en otro lugar. Sin embargo, si todo está en orden, el archivo de la plantilla se modificará y esto se indicará como un éxito en la línea 113. Así de sencillo.
Antes de terminar esta explicación sobre este archivo de cabecera, quiero que prestes atención al contenido de la línea 101. Observa que se ha eliminado la línea 100 y en su lugar se encuentra la línea 101. Lo que quiero destacar aquí es la cadena de texto (string). Esta cadena indica exactamente la ubicación esperada para el indicador Chart Trade. Es decir, la ubicación ha cambiado.
Ahora debes colocar el indicador exactamente donde se indica en la cadena, con el nombre especificado. Esto se debe a que, cuando se utiliza esta cadena, en realidad se está accediendo a los archivos de mapa de bits (bitmaps) declarados como recursos dentro del indicador. Si cambias algo aquí, ya sea en el código, en el nombre del indicador o en su ubicación, MetaTrader 5 no podrá acceder a los archivos de mapa de bits, por lo que el indicador Chart Trade no se mostrará correctamente en el gráfico.
Aunque normalmente muestro todos los archivos de cabecera antes de presentar el código principal, esta vez lo haré de manera diferente. Vamos a omitir un archivo de cabecera, que es básicamente el principal de este indicador, y veremos directamente el código fuente del archivo del indicador. Este se presenta íntegramente a continuación.
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.74" 06. #property icon "/Images/Market Replay/Icons/Indicators.ico" 07. #property link "https://www.mql5.com/pt/articles/12413" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. #property indicator_buffers 1 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 13. //+------------------------------------------------------------------+ 14. #define def_ShortName "Indicator Chart Trade" 15. //+------------------------------------------------------------------+ 16. C_ChartFloatingRAD *chart = NULL; 17. //+------------------------------------------------------------------+ 18. input long user00 = 0; //ID 19. input ushort user01 = 1; //Leverage 20. input double user02 = 100.1; //Finance Take 21. input double user03 = 75.4; //Finance Stop 22. //+------------------------------------------------------------------+ 23. double m_Buff[]; 24. //+------------------------------------------------------------------+ 25. int OnInit() 26. { 27. bool bErr; 28. 29. chart = new C_ChartFloatingRAD(user00, "Indicator Chart Trade", new C_Mouse(user00, "Indicator Mouse Study"), user01, user02, user03); 30. chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03); 31. 32. if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED; 33. 34. if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError); 35. 36. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 37. ArrayInitialize(m_Buff, EMPTY_VALUE); 38. 39. return (bErr ? INIT_FAILED : INIT_SUCCEEDED); 40. return INIT_SUCCEEDED; 41. } 42. //+------------------------------------------------------------------+ 43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 44. { 45. (*chart).MountBuffer(m_Buff, rates_total); 46. 47. return rates_total; 48. } 49. //+------------------------------------------------------------------+ 50. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 51. { 52. if (_LastError < ERR_USER_ERROR_FIRST) 53. (*chart).DispatchMessage(id, lparam, dparam, sparam); 54. (*chart).MountBuffer(m_Buff); 55. 56. ChartRedraw(); 57. } 58. //+------------------------------------------------------------------+ 59. void OnDeinit(const int reason) 60. { 61. switch (reason) 62. { 63. case REASON_INITFAILED: 64. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 65. break; 66. case REASON_CHARTCHANGE: 67. (*chart).SaveState(); 68. break; 69. } 70. 71. delete chart; 72. } 73. //+------------------------------------------------------------------+
Código fuente del Indicador Chart Trade
A primera vista, puede parecer complicado debido a las líneas eliminadas. El motivo de que estén eliminadas tantas líneas es precisamente que se trata de una modificación del código original.
Si no lo has visto, puedes consultar el código original en el artículo "Desarrollo de un sistema de repetición (Parte 47): Proyecto Chart Trade (VI). Sin embargo, no intentes utilizar el código de este artículo. Este código funciona de manera completamente diferente. Aunque pueda parecer complicado, en realidad es muy sencillo. La razón de esta simplicidad es que toda la complejidad se ha trasladado al archivo de cabecera. Ahora bien, veamos cómo funciona este código, ya que es absolutamente necesario para entender el archivo de cabecera que exploraremos después.
Vamos por partes, como diría Jack El Destripador. Básicamente, lo que más ocurre aquí son eliminaciones en lugar de modificaciones propiamente dichas. La primera cosa que eliminamos es la línea 10, como puedes ver porque está removida.
Vaya, ahora todo se complica de verdad. Al eliminar del indicador la propiedad que define la cantidad de buffers que estamos utilizando, le indicamos al compilador que no usaremos ningún buffer. Esto se debe a que el valor predeterminado de esta propiedad es cero. Es decir, el indicador no tendrá ningún dato almacenado en un buffer interno al que pueda acceder otro programa mediante CopyBuffer.
Por esta razón, muchos podrían decir que la situación se complica aún más. Pero si todavía no entiendes o no comprendes lo que puede causar la ausencia de un buffer en un indicador, significa que aún no has comprendido realmente cómo funcionan las cosas en MetaTrader 5 y en MQL5. Intentaré explicártelo si eres un aspirante que desea aprender a programar en MQL5.
Un indicador sirve básicamente para ayudarnos en alguna tarea, ya sea un cálculo, una indicación o, como en este caso, para presentar una interfaz de interacción con el usuario. En cualquiera de estos casos, pero especialmente en el último, podríamos usar otros tipos de programas, como, por ejemplo, scripts. El problema de los scripts es que, cuando se cambia el marco temporal, el script se elimina y no regresa automáticamente, por lo que es necesario que tú o el usuario lo vuelvan a colocar en el gráfico. En algunos casos, esto es un inconveniente que resolvemos utilizando otros métodos, como un indicador.
Sin embargo, los indicadores no son adecuados para temporizar. Esto se debe a que, al agregar un temporizador a un indicador, este afectará de hecho a todos los demás indicadores presentes en el gráfico. Por lo tanto, cuando necesitamos temporizar algo y no queremos o no deseamos incluir el código en un script, lo incluimos en un EA o en un servicio. La elección del lugar dependerá exclusivamente del propósito de la temporización.
Dicho esto, este Chart Trade podría colocarse en un servicio o en un Expert Advisor. No necesariamente tendría que estar en un indicador. Sin embargo, si se quisiera que Chart Trade se ejecutara en todos y cada uno de los gráficos, podría ser interesante colocarlo en un servicio. En ese caso, el propio servicio se encargaría de mostrar los objetos del Chart Trade en el gráfico correspondiente.
El problema de este enfoque radica precisamente en los eventos. Los servicios no cuentan con ningún manejador de eventos. Básicamente, son scripts que no están vinculados a ningún gráfico. Por lo tanto, si queremos implementar Chart Trade mediante un servicio, tendríamos que crear un indicador. Este indicador solo serviría para gestionar eventos y que el servicio esté al tanto de las posibles acciones realizadas en Chart Trade, como cuando el usuario hace clic en uno de los botones o activa algún evento relacionado con Chart Trade.
Observa cómo algo relativamente simple puede complicarse en muchas ocasiones. Ahora, ¿qué pasaría si usáramos Chart Trade dentro de un Expert Advisor? Parece factible. De hecho, debo concordar en parte, ya que efectivamente es posible integrar Chart Trade dentro de un Expert Advisor, pero no es viable.
¿Y por qué sería inviable? La razón es sencilla: diversificación. Reflexionemos un poco: si colocas Chart Trade dentro de un Expert Advisor, este solo estará disponible en ese Expert Advisor. Si deseas crear otro Expert Advisor por el motivo que sea, deberás añadir todo el código del Chart Trade a este nuevo Expert Advisor. Bien, pero podrías pensar en hacerlo fácilmente utilizando la inclusión mostrada en la línea 12, que sin duda funcionaría perfectamente. Sin embargo, ¿qué pasaría si decides mejorar algo en el código del Chart Trade? Tendrías que recompilar todos los Expert Advisors para actualizar el código. Esto supondría mucho trabajo, mientras que la solución anterior sería más práctica.
De acuerdo, pero podrías considerar incluir Chart Trade dentro de un Expert Advisor sin tener que recompilar todos cuando se realicen mejoras en él. Sí, también podrías pensar en hacerlo. Para ello, bastaría con usar algo parecido a una DLL. Sin embargo, este enfoque complicaría aún más la programación y no ofrecería resultados realmente interesantes. Por esta razón, se decidió colocar Chart Trade dentro de un indicador. Sin embargo, a diferencia de lo que se hacía antes, ahora no tendremos un buffer que realmente contenga los datos para ser leídos. Esto se manejará de otra forma, pero lo veremos más adelante. Continuemos con la explicación del código, ya que aún quedan aspectos por comprender.
Ahora quiero llamar tu atención a la línea 14. El hecho de que esta definición se realice después de la inclusión que ocurre en la línea 12 es muy importante. A diferencia de las variables globales, una definición que se coloque antes o después de una inclusión te permite controlar cómo ocurrirán las cosas o, al menos, probar ciertos aspectos dentro o fuera del código presente en los archivos de cabecera. Así que presta atención a este detalle.
Dado que la definición se realiza después de la inclusión del archivo de cabecera C_ChartFloatingRAD.mqh, no será visible ni utilizable dentro de este archivo. Sin embargo, si esa misma definición, presente en la línea 14, se colocara antes de la inclusión en la línea 12, entonces sería visible y utilizable por el código del archivo de cabecera C_ChartFloatingRAD.mqh. En otras palabras, el orden de los factores afecta al resultado cuando se trata de programación. Continuemos, ya que aún quedan cosas que revisar y que pueden resultarte interesantes.
La línea 18 se ha eliminado, ya que no es necesario utilizar este medio. Ahora será responsabilidad del usuario colocar el indicador Chart Trade en el gráfico, por lo que no es necesario especificar cuál es el ID del gráfico. Por otro lado, la línea 23 fue eliminada, pero no debido a la eliminación de la línea 10, ya que son cosas diferentes. Como expliqué anteriormente, la línea 10 sirve para definir si existe y, en caso afirmativo, cuántos buffers pueden ser accesibles externamente al indicador mediante CopyBuffer. Sin embargo, esto no impide que el indicador pueda tener buffers internamente. No obstante, dado que no son necesarios, la línea 23 fue eliminada. Lo mismo ocurre con las líneas 36 y 37, ya que al no utilizar ningún buffer, estas líneas también son innecesarias.
En este código OnInit se pueden observar muchas líneas eliminadas. Gran parte de ellas se deben a cambios que se explicarán en detalle en el próximo artículo, donde se abordará el código del archivo de cabecera C_ChartFloatingRAD.mqh. De entre todas las líneas eliminadas, solo tres realmente tienen una función activa. Veamos esas pocas líneas, comenzando por la 30. En esta línea se inicia la clase C_ChartFloatingRAD. Esto se realiza mediante el constructor mediante la llamada al operador new. Sin embargo, como ya debes saber, los constructores no devuelven ningún valor. La manera de verificar si el código del constructor ha fallado es utilizando otros métodos.
En programas más modulares en términos de estructura de llamadas, el constructor, cuyo propósito real es inicializar correctamente algunas variables y datos dentro de la clase, podría limitarse a eso. Sin embargo, dado que no hay una prohibición estricta, sino más bien una convención, podemos permitir que el constructor realice algo más que inicializar. Es aquí donde pueden surgir complicaciones. Sin un mecanismo para informar si el constructor ha fallado, no podremos resolver problemas de inicialización.
Por fortuna, MQL5 permite hacerlo. Es cierto que no es la forma más adecuada, pero funciona. En la línea 32 verificamos el valor de la variable _LastError. Si contiene un valor definido por nosotros, indica que la inicialización ha fallado. No estoy seguro de si mantendré esta estructura tal cual, pero podría hacerlo gracias a la línea 52. Sin embargo, llegaremos a eso en breve. Si en la línea 32 se detecta que la inicialización ha fallado, hacemos que la función OnInit retorne una constante de error a MetaTrader 5. Al recibir esta constante, MetaTrader 5 llamará en algún momento a OnDeInit, que eliminará automáticamente el indicador del gráfico. Si todo funciona correctamente, OnInit retornará una constante que indicará éxito, lo cual se realiza en la línea 40.
Ahora, observa las líneas 45 y 54. Ambas se utilizaban para configurar el buffer. Sin embargo, dado que ya no utilizamos ningún buffer, estas líneas quedaron obsoletas y fueron eliminadas, como puede verse claramente al estar resaltadas. No obstante, presta atención al test de la línea 52. Actualmente, este test no tiene sentido. Sin embargo, sin él, la llamada de la línea 53 fallará en algún momento. La razón por la que esta llamada en la línea 53 fallaría sin el test de la línea 52 solo podrá entenderse en el próximo artículo, donde se explicará en detalle la clase C_ChartFloatingRAD.
Para concluir, observemos el último elemento presente en este código: el procedimiento OnDeInit. Este procedimiento se llama automáticamente cuando se debe eliminar el indicador del gráfico. Sin embargo, aquí encontramos algo muy inusual para un OnDeInit. Me refiero a la línea 64. ¿Por qué motivo se incluye esta línea en el código? A primera vista, no tiene ningún sentido. De hecho, no lo tiene, pero esto se debe a que estás observando este código sin conocer el de la clase C_ChartFloatingRAD. Si te limitas únicamente a este fragmento de código, el motivo único para que REASON_INITFAILED sea la constante utilizada en la llamada a OnDeInit es la línea 32. Sin embargo, hay otros motivos en el código de la clase C_ChartFloatingRAD. Como deseo centralizar la ejecución de ciertas acciones, surge la línea 64. Esta línea elimina exactamente el indicador Chart Trade del gráfico. Pero el verdadero motivo se explicará en el próximo artículo.
Consideraciones finales
En este artículo, presenté parte de los cambios que ha sufrido el indicador Chart Trade. No intentes comprender ni utilizar el código mostrado sin antes revisar el código que se presentará en el próximo artículo. Si no entiendes y no conoces el código de la clase C_ChartFloatingRAD, podrías cometer errores significativos. Ten un poco de paciencia y espera al próximo artículo, ya que el contenido que se explicará realmente valdrá la pena. Esto se debe a que es en esa clase donde realmente se ha implementado el indicador Chart Trade.
De todos modos, si decides compilar este código, no olvides que la información utilizada en el archivo de cabecera C_AdjustTemplate.mqh se encuentra en el anexo. Sin embargo, puedes crear tus propias imágenes y modelos de plantilla para usar en Chart Trade. Solo asegúrate de que sean compatibles con lo esperado en la clase C_AdjustTemplate. Eso es todo, y no olvides leer el próximo artículo de esta serie.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/12413





- 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