Simulación de mercado: Position View (IV)
Introducción
Hola a todos y sean bienvenidos a otro artículo de la serie sobre cómo construir un sistema de repetición/simulación.
En el artículo anterior Simulación de mercado: Position View (III), mostré la importancia de que sepas cuándo y por qué configurar la propiedad ZOrder de los objetos. Hasta ahora, he sido bastante paciente y no he mostrado lo que realmente vamos a hacer. Tal vez tú, al leer los artículos anteriores, estés pensando que todo aquello es muy bonito, pero nada práctico. Pues bien, se acabó lo fácil. Ahora vamos a hacer algo que, si no se hace, no nos permitirá continuar implementando el indicador de posición. Se trata de: unir las cuatro aplicaciones que ya tenemos, para que podamos continuar implementando el indicador de posición. Sin embargo, no quiero que tú, querido lector, te preocupes por ahora por las órdenes pendientes. La forma en que trabajaremos con ellas se verá en el futuro. Así que necesitamos avanzar paso a paso.
El paso más importante en este momento no es la cuestión de las órdenes pendientes, sino hacer que las posiciones se gestionen de manera adecuada. Recuerda lo siguiente: ya tenemos un Asesor Experto que puede abrir y cerrar posiciones, a mercado. También tenemos un indicador de mouse que ya nos permite hacer estudios simples, pero que tú puedes modificar para generar estudios más elaborados. También contamos con Chart Trade, que nos permite enviar órdenes al Asesor Experto, para indicarle si debe comprar o vender, así como el volumen que se va a negociar. Pero estas tres aplicaciones necesitan otra para que podamos tener un sistema viable. Esa aplicación faltante es justamente el indicador que estamos implementando.
En el estado actual de desarrollo, el indicador de posición no pasa de ser una aplicación interesante. Pero, si lo unimos a las otras tres aplicaciones ya construidas, el conjunto empieza a volverse mucho más interesante. Bien, pero quizá estés pensando: ¿cómo haremos esto? ¿Vamos a usar la clase C_IndicatorPosition dentro del Asesor Experto? No, no lo haremos. Mantendremos el indicador de posición separado del Asesor Experto. De esta manera, podrás añadirlo a tu propio Asesor Experto.
Claro, tendrás que realizar algunos ajustes para que tu Asesor Experto pueda utilizar el Indicador de Posición. Pero, créeme, será mucho más simple hacer esto que crear varios Asesores Expertos con las mismas capacidades operativas. Cualquier cambio en el Indicador de Posición se reflejará inmediatamente en todos los Asesores Expertos que lo estén usando. A diferencia de lo que ocurriría si cada Asesor Experto tuviera su propio indicador de posición.
Esta idea, que estoy proponiendo y mostrando cómo implementar, no es nueva. En realidad, es lo que permite que programas diferentes se beneficien de componentes comunes. Algo similar a los juegos de computadora que usan DirectX. Cuando DirectX recibe una actualización, todos los juegos y programas que utilizan la biblioteca DirectX se actualizan también. Ahora, piensa si cada una de las aplicaciones tuviera que actualizarse manualmente con cada mejora en la biblioteca de DirectX. Sería completamente inviable. Por eso, no veas este modelo como un problema potencial. Velo como una solución que, con el tiempo, te permitirá tener aplicaciones cada vez más útiles y seguras. Cualquier corrección o mejora en una aplicación se reflejará en toda la cadena de aplicaciones que utilizas en MetaTrader 5. Con esto dicho, comencemos.
Recordemos cómo está actualmente el Asesor Experto
Antes de hacer cualquier cambio en el código, necesitamos ver cómo está el código principal del Asesor Experto. Esto se debe a que ya ha pasado bastante tiempo desde la última vez que lo modificamos. Así, en el código siguiente, puedes ver el estado actual de implementación del Asesor Experto.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.84" 07. #property link "https://www.mql5.com/pt/articles/12598" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. //+------------------------------------------------------------------+ 11. enum eTypeContract {MINI, FULL}; 12. //+------------------------------------------------------------------+ 13. input eTypeContract user00 = MINI; //Cross order in contract 14. //+------------------------------------------------------------------+ 15. C_Orders *Orders; 16. long GL_ID; 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. GL_ID = 0; 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (GL_ID > 0) break; else GL_ID = ChartID(); 35. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 36. EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, ""); 37. break; 38. } 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_REMOVE: 46. case REASON_INITFAILED: 47. EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, ""); 48. break; 49. } 50. 51. delete Orders; 52. } 53. //+------------------------------------------------------------------+
Código del Asesor Experto Original
¡Caramba! ¿La última vez que este código se modificó fue en el artículo 84? Sí, desde entonces, no ha recibido ninguna modificación. Pero ahora llegó el momento de volver a trabajar en él. Para ello, debes entender lo siguiente antes de cambiar nada en este código. Quiero que observes que, en la línea 13, tenemos la única forma de interacción con el usuario u operador. Esta interacción busca indicar a todas las demás aplicaciones que, de alguna forma, interactuarán con el Asesor Experto, que este estará orientado a trabajar con un contrato completo o con un minicontrato.
Para quienes no realizan operaciones en B3 (Bolsa de Brasil), esa interacción no tiene ningún sentido. Pero, independientemente de lo que estés operando, deberás tener el indicador Chart Trade en el gráfico, junto con este Asesor Experto. En cuanto el Asesor Experto se añada al gráfico, enviará un mensaje a todas las aplicaciones dentro de MetaTrader 5. Ya expliqué esto en el pasado, en esta misma secuencia de artículos. Si no sabes de qué estoy hablando, procura leer los artículos anteriores para profundizar en el tema. Entender esta cuestión de los mensajes será de suma importancia de aquí en adelante. Sin esa comprensión, no podrás seguir los próximos artículos.
Muy bien, una vez enviado este mensaje, todas las aplicaciones sabrán qué espera de ellas el Asesor Experto. Este Asesor Experto no recibirá comandos directamente del operador o usuario de ninguna manera. Solo recibirá y enviará comandos mediante mensajes. Así que no esperes ver un código lineal y todo bien ordenado. Aquí la dinámica es bastante activa.
Muy bien. Pero ¿dónde entra el indicador de posición en toda esta historia? ¿Cómo podremos usarlo si el Asesor Experto no sabe que el indicador está presente en el gráfico? En realidad, el Asesor Experto, al igual que las demás aplicaciones, no sabe de la presencia de los demás. Simplemente, todas esperan que el operador las tenga presentes en el gráfico para ejecutar correctamente sus tareas. Pero aquí hay un detalle importante. Como cada indicador de posición pertenecerá solo a una posición en particular, no podemos colocarlo en el gráfico antes de que exista una posición en ese símbolo específico. A diferencia de los otros indicadores, este indicador de posición será añadido y retirado por el Asesor Experto de forma automática. La idea es que el operador no interfiera en esto. El propio Asesor Experto se encargará de ello.
Sin embargo, no incorporaré el indicador de posición al Asesor Experto. La idea es que el indicador pueda modificarse o mejorarse de manera que todos los Asesores Expertos lo usen, sin que sea necesario recompilar los Asesores Expertos para ello. Pero, para que todo funcione en perfecta armonía, será necesario realizar algunos ajustes. Al hacerlo, tú, querido lector, deberás prestar mucha atención a lo que se explicará. De lo contrario, todo el conjunto fallará y no proporcionará los datos que deberían presentarse en el gráfico. Otro detalle es que, en este momento, no debes preocuparte por los detalles de las líneas de precio. Lo primero que hay que hacer ahora es crear una forma de interacción entre el Asesor Experto y el indicador de posición. Después nos preocuparemos por la interacción del indicador de posición con el indicador de mouse, que será el responsable de manipular las líneas de precio.
Comenzando a implementar la interacción
Bien, comencemos haciendo algunos cambios en el código principal del indicador de posición. La razón es que ahora ya no será, o mejor dicho, no deberá ser añadido al gráfico por el operador o usuario. Esto lo hará el Asesor Experto. Pero, como queremos que el Asesor Experto sepa qué indicador representa cada posición, necesitamos cambiar algunas cosas en el código del indicador, así como en el código del Asesor Experto.
Muy bien, teniendo claro lo que tendremos que hacer, vamos a empezar por el nuevo código del indicador de posición. Este puede verse a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property description "This should preferably be used together with an Expert Advisor." 05. #property description "For more details see the same article." 06. #property link "https://www.mql5.com/pt/articles/13168" 07. #property version "1.116" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #define def_ShortName "Position View" 12. //+------------------------------------------------------------------+ 13. #include <Market Replay\Order System\C_IndicatorPosition.mqh> 14. //+------------------------------------------------------------------+ 15. input ulong user00 = 0; //For Expert Advisor use 16. input color user01 = clrRoyalBlue; //Color Line Price 17. input color user02 = clrForestGreen; //Color Line Take Profit 18. input color user03 = clrFireBrick; //Color Line Stop Loss 19. //+------------------------------------------------------------------+ 20. C_IndicatorPosition *Positions = NULL; 21. //+------------------------------------------------------------------+ 22. int OnInit() 23. { 24. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 25. Positions = new C_IndicatorPosition(user01, user02, user03); 26. if (!Positions.CheckCatch(user00)) 27. { 28. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 29. return INIT_FAILED; 30. } 31. 32. return INIT_SUCCEEDED; 33. } 34. //+------------------------------------------------------------------+ 35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 36. { 37. return rates_total; 38. } 39. //+------------------------------------------------------------------+ 40. void OnDeinit(const int reason) 41. { 42. delete Positions; 43. } 44. //+------------------------------------------------------------------+
Código del indicador Position View
Observa que el código recibió pequeños cambios. Son cambios simples de entender, por lo que no merecen mayor atención. Pero debes observar atentamente que ahora, en la línea 15, se espera un dato procedente del Asesor Experto. Tú, como operador, incluso puedes introducir ese dato. Pero te aconsejo que no modifiques este dato. Puedes cambiar los colores, siempre que tengas cuidado de no dejarlos iguales, ya que, por ahora los usamos para probar qué línea horizontal representa cada color. Pero, de cualquier forma, evita en lo posible modificar el dato que deberá venir del Asesor Experto. Precisamente, este será el dato que el Asesor Experto usará para intercambiar información con el Indicador cuando ambos estén en el gráfico.
Ahora observa que, en la línea 26, pasamos un parámetro a la función de comprobación. Antes, ese parámetro no existía. Veamos el código de la clase C_IndicatorPosition, que se muestra íntegramente a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_SufixTake "Take" 05. #define def_SufixStop "Stop" 06. //+------------------------------------------------------------------+ 07. #include "..\Auxiliar\C_Terminal.mqh" 08. //+------------------------------------------------------------------+ 09. class C_IndicatorPosition : private C_Terminal 10. { 11. private : 12. struct st00 13. { 14. ulong ticket; 15. color corPrice, corTake, corStop; 16. }m_Infos; 17. //+------------------------------------------------------------------+ 18. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n") 19. { 20. if (price <= 0) return; 21. CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop)))); 22. ObjectSetDouble(GetInfoTerminal().ID, szObjName, OBJPROP_PRICE, price); 23. ObjectSetString(GetInfoTerminal().ID, szObjName, OBJPROP_TEXT, szDescription); 24. ObjectSetString(GetInfoTerminal().ID, szObjName, OBJPROP_TOOLTIP, szDescription); 25. ObjectSetInteger(GetInfoTerminal().ID, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice); 26. } 27. //+------------------------------------------------------------------+ 28. public : 29. //+------------------------------------------------------------------+ 30. C_IndicatorPosition(color corPrice, color corTake, color corStop) 31. :C_Terminal() 32. { 33. ZeroMemory(m_Infos); 34. m_Infos.corPrice = corPrice; 35. m_Infos.corTake = corTake; 36. m_Infos.corStop = corStop; 37. } 38. //+------------------------------------------------------------------+ 39. ~C_IndicatorPosition() 40. { 41. if (m_Infos.ticket != 0) 42. ObjectsDeleteAll(GetInfoTerminal().ID, IntegerToString(m_Infos.ticket)); 43. } 44. //+------------------------------------------------------------------+ 45. bool CheckCatch(ulong ticket) 46. { 47. string szName = IntegerToString(m_Infos.ticket = ticket); 48. 49. if (!PositionSelectByTicket(m_Infos.ticket)) return false; 50. if (ObjectFind(GetInfoTerminal().ID, szName) >= 0) 51. { 52. m_Infos.ticket = 0; 53. return false; 54. } 55. IndicatorSetString(INDICATOR_SHORTNAME, szName); 56. CreateLineInfos(szName, PositionGetDouble(POSITION_PRICE_OPEN), m_Infos.corPrice, "Position opening price."); 57. CreateLineInfos(szName + def_SufixTake, PositionGetDouble(POSITION_TP), m_Infos.corTake, "Take Profit point."); 58. CreateLineInfos(szName + def_SufixStop, PositionGetDouble(POSITION_SL), m_Infos.corStop, "Stop Loss point."); 59. 60. return true; 61. } 62. //+------------------------------------------------------------------+ 63. }; 64. //+------------------------------------------------------------------+ 65. #undef def_SufixTake 66. #undef def_SufixStop 67. //+------------------------------------------------------------------+
C_IndicatorPosition.mqh
Observa que algunas cosas cambiaron en este código en general. Pero nada demasiado complicado, por lo que no merece ninguna atención especial. Sin embargo, como se mencionó anteriormente, la función de comprobación ahora recibe un argumento. Así que vamos a ver qué cambió en esta función. Para ello, vayamos a la línea 45, donde está definida la función CheckCatch.
Observa que, en cuanto la función se inicia, tenemos la línea 47. En este punto, transformamos el dato que el Asesor Experto nos pasará para usarla en el nombre de los objetos que vamos a crear. Luego, en la línea 49, comprobamos si la posición existe o no. Bien, aquí vale explicar por qué estoy haciendo esta comprobación de nuevo, ya que el Asesor Experto nos informará y garantizará que la posición existe. Bien. El detalle es que no tengo intención de convertir este indicador de posición en parte integrante del Asesor Experto. Entonces, si un operador, o incluso un usuario, intenta añadir este indicador al gráfico, necesitamos confirmar que la posición existe. Observa que no estamos buscándola aquí en el indicador, como hicimos en el artículo anterior.
Una vez que se confirme la existencia de la posición, se realizará una nueva comprobación. Esta se realiza en la línea 50. Pero, en este caso, la comprobación sirve para garantizar que solo haya un indicador por posición en el gráfico. Existen otras formas de realizar esta misma comprobación. Sin embargo, aquí comprobamos la presencia de una línea con un nombre específico. Si todo sale bien, el indicador funcionará como se vio en el artículo anterior. Así, podemos pasar al código del Asesor Experto, para comprender cómo se comportará el sistema cuando el Asesor Experto se añada al gráfico. De esta manera, puedes ver el nuevo código del Asesor Experto a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.116" 07. #property link "https://www.mql5.com/pt/articles/13168" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. //+------------------------------------------------------------------+ 11. enum eTypeContract {MINI, FULL}; 12. //+------------------------------------------------------------------+ 13. input eTypeContract user00 = MINI; //Cross order in contract 14. //+------------------------------------------------------------------+ 15. C_Orders *Orders; 16. long GL_ID; 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. GL_ID = 0; 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (GL_ID > 0) break; 35. else 36. { 37. GL_ID = ChartID(); 38. for (int handle, count = PositionsTotal() - 1; count >= 0; count--) 39. { 40. handle = iCustom(_Symbol, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", PositionGetTicket(count)); 41. ChartIndicatorAdd(GL_ID, 0, handle); 42. IndicatorRelease(handle); 43. } 44. } 45. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 46. EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, ""); 47. break; 48. } 49. } 50. //+------------------------------------------------------------------+ 51. void OnDeinit(const int reason) 52. { 53. switch (reason) 54. { 55. case REASON_REMOVE: 56. case REASON_INITFAILED: 57. EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, ""); 58. break; 59. } 60. 61. delete Orders; 62. } 63. //+------------------------------------------------------------------+
Expert Advisor.mq5
Observa que el código cambió muy poco, en comparación con el código visto al comienzo de este mismo artículo. El motivo real de que haya tan pocos cambios es explicarte, querido lector y entusiasta, que no hace falta mucho para que el sistema funcione adecuadamente. Ten en cuenta que todo lo que se modificó, o mejor dicho, se agregó al código, se encuentra entre las líneas 38 y 43. Pero esto ya será suficiente para que el Asesor Experto pueda, al menos, establecer comunicación con el indicador de posición. En realidad, lo que hacemos aquí antes se hacía dentro del código del indicador. En ese código, por cada posición abierta, habría un indicador de posición colocado en el gráfico.
Sin embargo, presta atención a algo importante. Estamos capturando todas las posiciones abiertas. Pero, y esto es importante, no las estamos filtrando. Simplemente estamos añadiendo indicadores al gráfico en el que se ejecuta el Asesor Experto. Pero la posición puede corresponder a un símbolo diferente del que se espera visualizar en ese gráfico. Bien, puedes pensar que esto fue un error mío. Pero no, lo hice a propósito para mostrar que muchas veces se acaba cometiendo este error. Y, cuando se dan cuenta, están usando un mecanismo que no es fiable.
Observa que el problema no está en que usemos la variable _Symbol, disponible en MQL5, para acceder al nombre del símbolo del gráfico. El error no está allí, sino en que no estamos verificando, como se hacía en el código del indicador, si la posición devuelta por la función PositionGetTicket es una posición que pertenece o, de alguna forma, está vinculada al símbolo del gráfico. Este es el error con el que siempre debes tener cuidado. Durante las pruebas, puedes pensar que todo funciona bien en una cuenta demo. Pero, cuando pasas a la cuenta real, puedes perder dinero por un descuido durante la programación o por pruebas insuficientes en la fase de implementación. Así que, para ti, mi querido lector, que estás aprendiendo o intentando aprender algunos conceptos, ten cuidado al hacer cambios en cualquier código. Prueba siempre muy bien los cambios. Lee siempre la documentación para despejar cualquier duda.
Muy bien. Ahora has empezado a notar que algunos aspectos de la programación son un poco más complicadas de lo que pueden parecer a primera vista. Sin embargo, no hay motivo para desanimarse y dejar de estudiar programación. Todo es cuestión de saber adaptarse y encontrar el camino correcto. Pero debes estar pensando que, entonces, bastaría con comprobar si el símbolo de la posición indicada por el ticket corresponde o no al que esperamos usar. ¿Correcto? Bien, es más o menos eso.
Y digo esto precisamente porque no sé cuál es el mercado en el que estás operando. Esto ya se comentó al comienzo de este artículo. Existen casos, principalmente en B3 (Bolsa de Brasil), en los que tenemos un contrato completo y un minicontrato. También tenemos el historial del contrato vigente. Algunos operadores prefieren usar el histórico para operar. Otros, en cambio, usan el gráfico del símbolo en el que desean operar.
Observa que, aunque parezca una situación complicada y te preguntes: por qué alguien complicaría tanto las cosas al intentar operar en un gráfico diferente del que define el contrato o símbolo correcto, esto es una elección del operador. No podemos limitarlo; debemos, como programadores, satisfacer la necesidad del operador.
De esta manera, surge otra cuestión: ¿cómo podemos manejar esta situación? si no tenemos cómo saber cuál es el símbolo correcto? Sin embargo, la situación no es tan complicada, mi querido lector. Sí sabemos dónde está el dato del símbolo correcto. Está en el Indicador Chart Trade. Y es en este punto donde la situación se vuelve verdaderamente interesante. Tú, como programador, tienes dos alternativas. La primera es preguntarle a Chart Trade cuál es el símbolo correcto cuyas posiciones deben colocarse en el gráfico. Bien, puedes pensar: ¿Pero por qué necesito preguntarle esto a Chart Trade? Si el dato se muestra en un objeto de Chart Trade. Basta con leer el contenido de ese objeto.
Deberás hacerlo, mi querido lector. El dato está en un objeto de Chart Trade. Sin embargo, no estará accesible con tanta facilidad. Tendrás que crear un mensaje solo para obtener este dato de Chart Trade. Pero hacer esto complicaría mucho el código del Asesor Experto. Sin embargo, existe otra solución, considerablemente más simple. Pero debes proceder de la manera correcta. De lo contrario, terminarás creando una ruptura en la encapsulación creada y probada hace bastante tiempo. Entonces, pensando con calma y analizando el código, notarás que Chart Trade recibe el dato del símbolo que viene de la clase C_Terminal. Para ser más preciso, del procedimiento CurrentSymbol, que está en la línea 47 de la clase C_Terminal.
Es en este punto donde está el peligro, principalmente para quienes están empezando a aprender programación. Muchos principiantes intentarán acceder directamente a este procedimiento desde el Asesor Experto. Esto no podrá hacerse, ya que dicho procedimiento está en la sección protected. Así, tú piensas: Voy a poner este procedimiento en la sección public. Esto es un error. El segundo error sería: Al observar el procedimiento, notas que no devuelve ningún valor ni argumento. Entonces, decides modificarlo para que devuelva los datos del símbolo correcto. Pero, al hacer esto, romperás totalmente el procedimiento. Porque en el futuro puede cambiar para permitirnos incorporar más funcionalidades. Entonces, lo más correcto es NO tocar el procedimiento de la línea 47 de la clase C_Terminal.
Así, tú, como principiante, puedes imaginar: Entonces, no hay forma de solucionar el problema. Ya que no existe forma de saber cuál es el símbolo que se va a usar. Sin embargo, esta no es la forma correcta de plantear el problema. Sí existe una solución. Simple, elegante y económica. Ya que no traerá efectos secundarios a los códigos ya construidos y probados. Lo único que necesitas hacer es ir al constructor de la clase C_Terminal y modificarlo. El código original puede verse en el siguiente fragmento.
106. //+------------------------------------------------------------------+ 107. C_Terminal(const long id = 0, const uchar sub = 0) 108. { 109. m_Infos.ID = (id == 0 ? ChartID() : id); 110. m_Mem.AccountLock = false; 111. m_Infos.SubWin = (int) sub; 112. CurrentSymbol(false); 113. ZeroMemory(m_Mouse); 114. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
Fragmento original de C_Terminal.mqh
La modificación que debe hacerse se ve a continuación.
106. //+------------------------------------------------------------------+ 107. C_Terminal(const long id = 0, const uchar sub = 0, const bool bFull = false) 108. { 109. m_Infos.ID = (id == 0 ? ChartID() : id); 110. m_Mem.AccountLock = false; 111. m_Infos.SubWin = (int) sub; 112. CurrentSymbol(bFull); 113. ZeroMemory(m_Mouse); 114. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
Fragmento modificado de C_Terminal.mqh
¿Notaron la diferencia entre los códigos? Es extremadamente sutil, simple y elegante. Ahora podemos usar la clase C_Terminal para saber, directamente en el Asesor Experto, cuál es el nombre del símbolo. O podemos pasar el dato al indicador de posición para que él mismo realice esa tarea por nosotros. De cualquier manera, ahora el Asesor Experto podrá decirle al indicador de posición si desea mostrar contratos completos o minicontratos. Esto, usando directamente el historial del contrato. Pero puedes pensar que esto será un problema, ya que puede entrar en conflicto con lo que se indica en Chart Trade. Pero no. El dato coincidirá exactamente con lo que se encuentra en Chart Trade.
Entonces, implementemos la solución. Como no quiero hacer pruebas en el indicador, ya que su propósito es otro, vamos a probar el dato directamente en el Asesor Experto. Así, el código corregido del Asesor Experto puede verse a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.116" 07. #property link "https://www.mql5.com/pt/articles/13168" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. #include <Market Replay\Auxiliar\C_Terminal.mqh> 11. //+------------------------------------------------------------------+ 12. enum eTypeContract {MINI, FULL}; 13. //+------------------------------------------------------------------+ 14. input eTypeContract user00 = MINI; //Cross order in contract 15. //+------------------------------------------------------------------+ 16. C_Orders *Orders = NULL; 17. C_Terminal *Terminal = NULL; 18. //+------------------------------------------------------------------+ 19. int OnInit() 20. { 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (Terminal != NULL) break; 35. else 36. { 37. ulong ul; 38. Terminal = new C_Terminal(0, 0, user00); 39. for (int handle, count = PositionsTotal() - 1; count >= 0; count--) 40. { 41. ul = PositionGetTicket(count); 42. if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol) continue; 43. handle = iCustom(_Symbol, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", ul); 44. ChartIndicatorAdd((*Terminal).GetInfoTerminal().ID, 0, handle); 45. IndicatorRelease(handle); 46. } 47. } 48. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 49. if (Terminal != NULL) 50. EventChartCustom((*Terminal).GetInfoTerminal().ID, evEA_At_ChartTrade, user00, 0, ""); 51. break; 52. } 53. } 54. //+------------------------------------------------------------------+ 55. void OnDeinit(const int reason) 56. { 57. switch (reason) 58. { 59. case REASON_REMOVE: 60. case REASON_INITFAILED: 61. if (Terminal != NULL) 62. EventChartCustom((*Terminal).GetInfoTerminal().ID, evEA_At_ChartTrade, -1, 0, ""); 63. break; 64. } 65. 66. delete Orders; 67. delete Terminal; 68. } 69. //+------------------------------------------------------------------+
Expert Advisor.mq5
Esta es la belleza de la programación. Compara el código anterior con los demás códigos del Asesor Experto mostrados en este artículo. Y observa cómo la programación es una disciplina hermosa y maravillosa. Pues, usando un poco de conocimiento sobre punteros en C/C++, aquí en MQL5, fue posible eliminar una variable global y mantener solo declaraciones de clases. En realidad, serían punteros a objetos de clase. Pero está bien, MQL5 no usa punteros como C/C++. Aun así, es posible usar parte de lo que puede hacerse en C/C++. Si no entiendes lo que ocurre en este Asesor Experto, no te preocupes, pues estoy aquí para ayudarte a entender este código.
Observa que, en la línea 10, incluimos el archivo de encabezado C_Terminal.mqh, que acabamos de ajustar según nuestros intereses. En la línea 17, declaramos una variable global para poder acceder a la clase. Sin embargo, aunque no es obligatorio, quiero dejar explícito que la variable puntero se inicializa como NULL. Si no especificas esto, el compilador lo hará implícitamente. Pero conviene dejar clara la inicialización que estamos haciendo.
Bien. En cuanto se ejecuta la función OnInit, MetaTrader 5 disparará un evento CHART_CHANGE. Este será capturado por la función OnChartEvent, en la línea 28, dando inicio al manejo de eventos por parte del Asesor Experto. En la primera ejecución de esta función, el puntero Terminal tendrá el valor NULL. Por lo tanto, la línea 34 fallará, permitiendo que se ejecute la línea 35. En este punto comienza la magia. Observa que el código, en este punto, se parece mucho al código visto anteriormente en el Asesor Experto. Sin embargo, en la línea 38, llamamos al constructor de la clase C_Terminal, indicándole si usaremos uno u otro tipo de contrato.
Al hacer esto, el constructor generará el nombre correcto del símbolo. Así, en la línea 41, obtenemos el valor del ticket. Necesitamos hacer esto antes de llamar a otras funciones para obtener datos de la posición. Esto se menciona en la documentación de MQL5; estúdiala para más detalles. Después de esto, ya podemos, en la línea 42, verificar si el nombre del símbolo al que corresponde la posición es el mismo que espera el Asesor Experto. Si no es el caso, volveremos a la línea 39. De lo contrario, es decir, si el símbolo tiene el nombre esperado, hacemos la llamada al indicador de posición, tal como se hacía antes.
Ahora, quiero que observes las líneas 49 y 61. Responde: ¿por qué estoy haciendo una comprobación en estas líneas? Piensa un poco antes de leer la respuesta que daré a continuación. Bien, espero que al menos lo hayas intentado antes de leer la respuesta. El motivo por el que estas líneas existen es precisamente mejorar la fiabilidad del código del Asesor Experto. Si intentas usar una llamada a alguna función o procedimiento de la clase C_Terminal antes de que la clase se haya inicializado correctamente, la comprobación para verificar si el valor del puntero es NULL impedirá que el Asesor Experto simplemente genere un valor de error en el terminal de MetaTrader 5. Al hacer esta comprobación, garantizas que el flujo se mantenga como se espera.
Muchos programadores de C/C++, pero principalmente de C, no suelen hacer estas comprobaciones al usar punteros. Por esta razón, muchos consideran que los programas escritos en C contienen muchos errores al ejecutarse. Sin embargo, dichos errores no se deben al lenguaje en sí, sino a la forma en que se escribió el programa, o al motivo por el cual se escribió de esa manera. En algunos casos, incluso es intencional crear programas que busquen alguna vulnerabilidad en el sistema, a fin de hacer algo no previsto por el propietario del sistema.
Consideraciones finales
En este artículo, comenzamos a unir diversas aplicaciones que antes estaban completamente aisladas entre sí. Aunque Chart Trade, el Indicador de Mouse y el Asesor Experto ya tenían cierta relación, todavía no había una forma de observar directamente en el gráfico las posiciones abiertas en el servidor de trading. Muchas veces, usando un sistema de órdenes cruzadas. A partir de este momento, esto empieza a ser posible, abriendo diversas puertas a nuevas ideas e implementaciones. Aunque apenas estamos comenzando a poner los componentes en funcionamiento, ya tenemos un rumbo que seguir.
Aunque, a primera vista, este sistema sea muy primitivo y pudiera haberse presentado en un estado más avanzado de desarrollo, no consideré prudente hacerlo. Hacerlo dejaría a muchos principiantes o entusiastas sin comprender realmente lo que ocurría en el código. Ten en cuenta que los cambios ocurren poco a poco, para que todos puedan seguirlos. Este material tiene una finalidad didáctica, no la de mostrar la única forma de hacer algo en MetaTrader 5. Espero que todo este material esté ayudando a quienes realmente buscan conocimiento. En el próximo artículo, continuaremos desarrollando aún más lo que vimos aquí. Hasta pronto.
| Archivo | Descripción |
|---|---|
| Experts\Expert Advisor.mq5 | Muestra la interacción entre Chart Trade y el Asesor Experto (Es necesario usar Mouse Study para la interacción) |
| Indicators\Chart Trade.mq5 | Crea la ventana para configurar la orden que se va a enviar (Es necesario usar Mouse Study para la interacción) |
| Indicators\Market Replay.mq5 | Crea los controles para interactuar con el servicio de repetición/simulador (Es necesario usar Mouse Study para la interacción) |
| Indicators\Mouse Study.mq5 | Permite la interacción entre los controles gráficos y el usuario (Necesario tanto para operar el sistema de repetición / simulador como en el mercado real) |
| Indicators\Order Indicator.mq5 | Responsable de la indicación de órdenes de mercado, permitiendo la interacción y el control de estas |
| Indicators\Position View.mq5 | Responsable de la indicación de posiciones de mercado, permitiendo la interacción y el control de estas |
| Services\Market Replay.mq5 | Crea y mantiene el servicio de repetición y simulación de mercado (Archivo principal de todo el sistema) |
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/13168
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.
Del básico al intermedio: Acceso aleatorio (I)
Teoría de grafos: Aplicación del algoritmo de Dijkstra al trading
Simulación de mercado: Position View (V)
Símbolos personalizados MQL5: Creamos un símbolo de barras 3D
- 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