Simulación de mercado (Parte 05): Creación de la clase C_Orders (II)
Introducción
En el artículo anterior, «Simulación de mercado (Parte 04): Creación de la clase C_Orders (I)», me centré básicamente en explicar cómo sería el código para enviar órdenes de tráding al mercado. El objetivo de toda esa explicación era mostrar cómo se podría estructurar el código de la clase para decodificar la información recibida del indicador Chart Trade.
Quienes tienen ciertos conocimientos podrían implementarlo, aunque no hayan visto el código fuente del Asesor Experto. Aunque parezca que el código mostrado en el artículo anterior no puede ejecutar operaciones en el servidor de tráding real, no es del todo cierto. Ese código no sería suficiente para usarlo con cuentas del tipo HEDGING.
En las cuentas del tipo NETTING ya es posible abrir y cerrar una posición. Pero, el funcionamiento es un poco diferente para las cuentas HEDGING.
Si intentas cerrar una posición de compra utilizando el botón de venta, en realidad abrirás una posición de venta. Esto ocurre en cuentas del tipo HEDGING. En una cuenta del tipo NETTING, realizar lo descrito cerraría la posición de compra o, al menos, produciría algún tipo de efecto, como:
- Esto supondría un cambio de posición, que se produciría si vendieras un volumen mayor que el de la posición de compra. En este caso, dejarías de estar comprado y pasarías a tener una posición de venta. El volumen de la posición vendida sería la diferencia entre el volumen de la posición comprada y el volumen enviado para el cambio de posición.
- Realizar un cierre parcial. Esto se lograría si el volumen vendido fuera inferior al de la posición de compra. En este caso, se cerraría una parte del volumen comprado y quedaría una posición de compra abierta desde la perspectiva del mercado.
Todo esto es bastante interesante y se explicó con todo detalle en una serie de artículos que publiqué hace algún tiempo. Dicha serie tenía como objetivo explicar con todo detalle cómo crear un asesor experto que pudiera utilizarse de forma automática. La serie constaba de quince artículos, en los que se explicaban los aspectos y precauciones necesarios para desarrollar dicho asesor experto en cada uno de ellos. Se partía de un modelo que funcionaba manualmente hasta conseguir que funcionara de forma totalmente automática. Quien tenga interés en saber más, puede buscar los artículos sobre el tema. El primero de ellos está disponible aquí: «Cómo construir un EA que opere automáticamente (Parte 01): Conceptos y estructuras».
Gran parte del código que se verá aquí proviene de esa serie, pero el enfoque será diferente, ya que el objetivo principal es otro. Es decir, no queremos desarrollar un sistema de envío de órdenes ni comunicarnos con un servidor de tráding real. Lo que queremos, y efectivamente haremos, es desarrollar un servidor simulado. Pero antes de hacerlo, necesitamos implementar algunas cosas. Dado que lo ideal es poder utilizar aplicaciones comunes entre nuestro sistema de repetición/simulador y el servidor de tráding real, tanto en cuentas de demostración como en cuentas reales, primero implementaremos el asesor experto para que pueda comunicarse con el servidor real. Una vez concluya dicha implementación, desarrollaremos un simulador del servidor de tráding.
El motivo de hacerlo así es limitar el tipo de llamadas que necesitaremos utilizar. Al menos de momento, no necesito simular todas las llamadas que el servidor real efectivamente responde, ya que la mayoría de las veces no son necesarias. Aunque tal vez cambie de opinión con el tiempo, por ahora lo haremos así. Dicho esto, podemos comenzar con el primer tema del artículo.
Tratar el mensaje para cerrar las posiciones
Bien. Si no has comprendido el contenido del artículo anterior, te sugiero que regreses a él y lo entiendas primero, ya que aquí solo continuaremos lo que se vio allí. No obstante, como hubo una pequeña mejora en la estructuración del código, podrás verlo todo nuevamente más abajo. No obstante, no se revisarán aquí las partes que se explicaron en el artículo anterior, ya que la reestructuración realizada no invalida en absoluto las explicaciones dadas en él. Así que vayamos al código en sí.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Defines.mqh" 005. //+------------------------------------------------------------------+ 006. class C_Orders 007. { 008. protected: 009. //+------------------------------------------------------------------+ 010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; } 011. //+------------------------------------------------------------------+ 012. bool ClosePosition(const ulong ticket) 013. { 014. bool IsBuy; 015. string szContract; 016. 017. if (!PositionSelectByTicket(ticket)) return false; 018. IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; 019. szContract = PositionGetString(POSITION_SYMBOL); 020. ZeroMemory(m_Base.TradeRequest); 021. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 022. m_Base.TradeRequest.type = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); 023. m_Base.TradeRequest.price = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS)); 024. m_Base.TradeRequest.position = ticket; 025. m_Base.TradeRequest.symbol = szContract; 026. m_Base.TradeRequest.volume = PositionGetDouble(POSITION_VOLUME); 027. m_Base.TradeRequest.deviation = 1000; 028. 029. return SendToPhysicalServer() != 0; 030. }; 031. //+------------------------------------------------------------------+ 032. private : 033. //+------------------------------------------------------------------+ 034. struct stBase 035. { 036. MqlTradeRequest TradeRequest; 037. ulong MagicNumber; 038. bool bTrash; 039. }m_Base; 040. //+------------------------------------------------------------------+ 041. struct stChartTrade 042. { 043. struct stEvent 044. { 045. EnumEvents ev; 046. string szSymbol, 047. szContract; 048. bool IsDayTrade; 049. ushort Leverange; 050. double PointsTake, 051. PointsStop; 052. }Data; 053. //--- 054. bool Decode(const EnumEvents ev, const string sparam) 055. { 056. string Res[]; 057. 058. if (StringSplit(sparam, '?', Res) != 7) return false; 059. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 060. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 061. else return false; 062. 063. return true; 064. } 065. //--- 066. }m_ChartTrade; 067. //+------------------------------------------------------------------+ 068. ulong SendToPhysicalServer(void) 069. { 070. MqlTradeCheckResult TradeCheck; 071. MqlTradeResult TradeResult; 072. 073. ZeroMemory(TradeCheck); 074. ZeroMemory(TradeResult); 075. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 076. { 077. PrintFormat("Order System - Check Error: %d", GetLastError()); 078. return 0; 079. } 080. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 081. if (TradeResult.retcode != TRADE_RETCODE_DONE) 082. { 083. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 084. return 0; 085. }; 086. 087. return TradeResult.order; 088. } 089. //+------------------------------------------------------------------+ 090. ulong ToMarket(const ENUM_ORDER_TYPE type) 091. { 092. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 093. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 094. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 095. 096. ZeroMemory(m_Base.TradeRequest); 097. m_Base.TradeRequest.magic = m_Base.MagicNumber; 098. m_Base.TradeRequest.symbol = m_ChartTrade.Data.szContract; 099. m_Base.TradeRequest.price = NormalizeDouble(price, nDigit); 100. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 101. m_Base.TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 102. m_Base.TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 103. m_Base.TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 104. m_Base.TradeRequest.type = type; 105. m_Base.TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 106. m_Base.TradeRequest.stoplimit = 0; 107. m_Base.TradeRequest.expiration = 0; 108. m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN; 109. m_Base.TradeRequest.deviation = 1000; 110. m_Base.TradeRequest.comment = "Order Generated by Experts Advisor."; 111. 112. MqlTradeRequest TradeRequest[1]; 113. 114. TradeRequest[0] = m_Base.TradeRequest; 115. ArrayPrint(TradeRequest); 116. 117. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 118. }; 119. //+------------------------------------------------------------------+ 120. void CloseAllsPosition(void) 121. { 122. for (int count = PositionsTotal() - 1; count >= 0; count--) 123. { 124. if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue; 125. if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue; 126. ClosePosition(PositionGetInteger(POSITION_TICKET)); 127. } 128. }; 129. //+------------------------------------------------------------------+ 130. public : 131. //+------------------------------------------------------------------+ 132. C_Orders(const ulong magic) 133. { 134. m_Base.MagicNumber = magic; 135. } 136. //+------------------------------------------------------------------+ 137. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 138. { 139. switch (id) 140. { 141. case CHARTEVENT_CUSTOM + evChartTradeBuy : 142. case CHARTEVENT_CUSTOM + evChartTradeSell : 143. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 144. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 145. { 146. case evChartTradeBuy: 147. ToMarket(ORDER_TYPE_BUY); 148. break; 149. case evChartTradeSell: 150. ToMarket(ORDER_TYPE_SELL); 151. break; 152. case evChartTradeCloseAll: 153. CloseAllsPosition(); 154. break; 155. } 156. break; 157. } 158. } 159. //+------------------------------------------------------------------+ 160. }; 161. //+------------------------------------------------------------------+
Código del archivo de cabecera C_Orders.mqh
Y ahí está. El código que se muestra arriba responde adecuadamente a todos los mensajes que el indicador Chart Trade envía al asesor experto para comunicarse con el servidor de tráding. A partir de este momento, todos los botones y controles del indicador Chart Trade se vuelven funcionales. Puedes utilizar este código en cualquier asesor experto, siempre y cuando realices las configuraciones pertinentes, que se explicarán más adelante en este artículo. Esa es precisamente la idea. Podrás usar los módulos que componen Chart Trade y el indicador de Mouse, y hacer que tu asesor experto sea compatible con los módulos ya compilados de Chart Trade y del indicador de Mouse utilizando este código. Es decir, programa una vez, comprueba que funciona y no lo modifiques más. Pasa a la siguiente etapa. Esa es la idea.
Pero para quienes están empezando y no logran comprender este código, veamos qué hay de nuevo aquí. Recordemos que el cambio mencionado en la estructuración se produjo en la línea 34, donde agrupé las variables privadas de la clase dentro de una estructura. Pero esto, como se dijo, no cambia en absoluto lo explicado en el artículo anterior.
Lo primero que podrías pensar es: «¿Cómo sabe esta clase qué posición debe cerrarse cuando pulsamos el botón de cierre de posiciones en el indicador Chart Trade?». La respuesta es que la clase no sabe qué posición debe cerrarse. Tal vez pienses que es una tontería, ya que al observar los botones, el que cierra las posiciones está declarado como CLOSE ALL POSITIONS. Por lo tanto, se podría suponer que se cerrarán todas las posiciones, ¿no es así? Incorrecto. Porque no es exactamente eso lo que hará esta clase. Al menos, no sin que el código mostrado anteriormente sufra algunas modificaciones.
Es muy probable que ahora mismo estés algo confundido con todo esto. Si al pulsar el botón CLOSE ALL POSITIONS no se cierran todas las posiciones, ¿por qué muestra ese mensaje? Para entenderlo, será necesario retroceder un poco en el tiempo y analizar cómo funciona realmente el código de la clase.
En los artículos sobre el sistema de órdenes cruzadas:
- Simulación de mercado (Parte 01): Orden cruzada (I)
- Simulación de mercado (Parte 02): Orden cruzada (II)
Les expliqué cómo y por qué a veces necesitamos utilizar un tipo específico de símbolo: el histórico. En él se explicó que tendríamos que hacer que el indicador Chart Trade le dijera al asesor experto cuál sería el símbolo real que debería negociarse. Esto ocurre porque el histórico no es, de hecho, un símbolo negociable, por lo que no es posible enviar órdenes de tráding a través de él. Muchos de estos símbolos históricos existen debido a los contratos. En esos dos artículos, expliqué esta cuestión de los contratos. Sin embargo, aquí el problema se agrava enormemente, ya que se puede estar utilizando el gráfico de un símbolo histórico y pedir al asesor experto que cambie del contrato completo al minicontrato en cualquier momento. Esto hará que el indicador Chart Trade cambie el símbolo que se va a negociar.
Ahora llega el verdadero problema. Si durante un tiempo has utilizado el gráfico histórico del dólar, por ejemplo, y le has pedido al asesor experto que anote que se van a realizar operaciones en el contrato completo de dólar y luego cambias de idea y le pides que utilice el minicontrato, el asesor experto emitirá un mensaje al indicador Chart Trade para informar del cambio de contrato. Hasta ahí, todo correcto. El problema comienza cuando tienes una posición abierta en el contrato completo. Bueno, si le indicas al asesor experto que deseas cambiar, por cualquier motivo, al minicontrato, aun teniendo una posición abierta en el contrato completo, el asesor experto simplemente obedecerá. Sin embargo, al hacerlo, Chart Trade también cambiará. Ahora tenemos un problema. Y debes entender este problema para poder adecuar el código a lo que necesitas hacer.
Si en este momento Chart Trade indica que el contrato a negociar es el minicontrato, ¿qué ocurre si se piden cerrar todas las posiciones abiertas? Recuerda que se mantiene una posición abierta en el contrato completo. El indicador Chart Trade generará un mensaje para el asesor experto. Como se ha visto en artículos anteriores, este mensaje contendrá todos los datos necesarios para que el asesor experto sepa qué hacer. Sin embargo, el asesor experto no sabrá qué hacer. Esto se debe a que el asesor experto se encuentra en un gráfico cuyo contenido no se puede negociar, es decir, un gráfico histórico del contrato. Para que el asesor experto sepa qué hacer, se interceptará el mensaje en la línea 153 del código. Cabe recordar que, para que esto ocurra, el mensaje ya habrá sido traducido. Dicha traducción se ha realizado sin problemas. Así, el código se dirigirá a la línea 120. Ahora comienza el verdadero trabajo.
Muy bien, en la línea 122 tenemos un bucle. Este bucle leerá todas las posiciones abiertas. Absolutamente todas, sin excepción. Pero ¿por qué estamos haciendo esto?, ¿por qué leer todas las posiciones abiertas, incluso aquellas que no tienen nada que ver con el contrato de dólar de nuestro ejemplo? El motivo es que no estamos almacenando el ticket que el servidor nos proporcionó cuando se abrió la posición. Por esta razón, necesitamos buscar ese ticket de nuevo.
Ahora bien, la otra cuestión es: Entonces, ¿por qué no almacenamos esos tickets? Esto simplificaría el trabajo. De hecho, lo simplificaría. Sin embargo, no podemos almacenarlos dentro del asesor experto, ya que si ocurre algo en el gráfico relacionado con él, perderíamos la información correspondiente. Tendríamos que buscarlos de nuevo. Esto sucederá siempre que se modifique el marco temporal del gráfico o el tipo de contrato.
De momento, lo haremos así. Sin embargo, si lo hacemos así, no podemos llamar simplemente al procedimiento que cierra la posición, porque recorreríamos todas las posiciones, incluso las que no están relacionadas con el mensaje del Chart Trade.
De esta manera, podemos filtrar los resultados. El primer filtro está en la línea 124, donde verificamos el nombre del símbolo de esa posición y lo comparamos con el nombre que nos ha proporcionado Chart Trade. Ahora, presta atención. En nuestro ejemplo, existe una posición abierta en el contrato completo. Sin embargo, el mensaje de Chart Trade indica que el símbolo es el minicontrato. Por tanto, esta verificación fallará, ya que el nombre del símbolo no coincide. Lo mismo sucedería si estuviéramos en la situación contraria, es decir, si tuviéramos una posición abierta en el minicontrato y usáramos Chart Trade para cerrar las posiciones en el contrato completo. Esta comprobación evitaría la confusión.
Ahora es necesario hacer una observación. Aunque parezca complicar las cosas, esta verificación es necesaria. Incluso si estás utilizando el histórico del contrato para operar con el minicontrato o el contrato completo mediante orden cruzada, no debes eliminar esta verificación de la línea 124 del código, aunque en este momento quieras cerrar cualquier posición relacionada con el contrato de dólar. El motivo es sencillo: sin esta comprobación, incluso las posiciones de otros símbolos podrían cerrarse cuando uses Chart Trade. Esto se debe a que se recorrerán todas las posiciones abiertas.
Sin embargo, si la verificación de la línea 124 tiene éxito, obtendremos una nueva comprobación. Esta tiene como objetivo aislar y filtrar otro aspecto. En el artículo anterior, mencioné que esta clase representaría una entidad única debido al número mágico. Así, un mismo asesor experto podría contener diversas estrategias de tráding diferentes e independientes entre sí. Dicha separación se realizaría mediante el número mágico.
A pesar de todo, para cuentas del tipo NETTING esto no tendría sentido, ya que, en este tipo de cuenta, el servidor de tráding realiza un precio promedio de la posición. Entonces, solo tendrías una única posición abierta por símbolo.
Sin embargo, la situación cambia completamente en el caso de las cuentas del tipo HEDGING. En este caso, podemos tener varias posiciones abiertas al mismo tiempo, incluso una de venta y otra de compra, en el mismo activo. Y es precisamente en estos casos donde tiene sentido que esta clase se diferencie de las demás presentes en el mismo asesor experto. Pero, para no complicar las cosas, no entraré en detalles sobre cómo hacerlo. Solo necesitas saber que esto es posible y está previsto en esta clase.
Además de que el asesor experto haya abierto posiciones marcadas con un número mágico que indica quién lo hizo, existe otra posibilidad: el operador también podría haber abierto manualmente alguna posición. En este caso concreto, la situación puede volverse problemática. Esto se debe a que el operador puede no querer que su posición se cierre al usar Chart Trade. El asesor experto comparará el número mágico de la posición con el de la clase y, si no coinciden, ignorará la posición, que permanecerá abierta. Por lo tanto, si el operador abrió una posición manualmente, el asesor experto la ignorará y permanecerá abierta, por lo que deberá cerrarla manualmente.
¿Ves cómo van ocurriendo las cosas? Aunque este comportamiento puede modificarse, todo el sistema está diseñado para trabajar con cuentas del tipo HEDGING y de modo que no interfiera en lo que no le corresponde. Si ambas verificaciones tienen éxito, se ejecutará la línea 126, que capturará el ticket de la posición y llamará al procedimiento para cerrar la posición indicada. Este procedimiento se halla en la línea 12. Fíjate que está dentro de una cláusula protegida, es decir, que podrá utilizarse mediante herencia, pero no se podrá acceder a ella directamente fuera del sistema de herencia. En resumen, no será posible llamar a este procedimiento directamente desde el código del asesor experto ni desde cualquier otro código.
Aquí tenemos algunas cosas curiosas que quizás decida eliminar del código en el futuro. Se trata de lo siguiente: cuando se llama a este código en la línea 12, ya se conocen algunos datos, como el nombre del símbolo de la posición y el ticket del activo. Sin embargo, aquí, en la línea 17, volvemos a verificar si el ticket corresponde realmente a una posición abierta. ¿Por qué estoy haciendo esto de nuevo aquí? El motivo es que, en este momento exacto, no sé cómo se comunicará con este procedimiento otra herramienta que aún está por crear. No obstante, es necesario usar la función de biblioteca PositionSelectByTicket para poder actualizar algunas informaciones que necesitaremos obtener del servidor. Esta actualización también se realizó cuando, en la línea 124, llamamos a PositionGetSymbol. Es decir, estamos duplicando algunas cosas. Pero, por ahora, esto no es un problema.
En cualquier caso, necesitamos actualizar la información. Cuando hayamos actualizado la información y el ticket corresponda a una posición, en la línea 18 verificaremos si la posición es de compra o de venta. Esto es importante para nosotros. A continuación, en la línea 19, capturamos el nombre del símbolo de la posición. Este tipo de cosas podría modificarse en el futuro, cuando se implemente la otra herramienta. Al consultar solo este código de esta clase, esta información sería totalmente innecesaria, ya que el procedimiento de la línea 120 podría proporcionárnosla. No sé cómo será este procedimiento ClosePosition en el futuro. A continuación, pasamos a la fase de completar el formulario, es decir, la estructura que se enviará al servidor.
A diferencia de lo que ocurrió en el procedimiento ToMarket, visto en el artículo anterior, donde se dijo que la explicación de cada campo se vería después, aquí puedo explicar los campos principales. Es importante saber lo que representa cada uno porque lo necesitaremos en el futuro. En la línea 21, le indicamos al servidor qué acción realizar. Ahora muchos se estarán preguntando: «¿Por qué no usas la acción TRADE_ACTION_CLOSE_BY, si la idea aquí es cerrar la posición?» Esta es una pregunta interesante y la razón principal es la cuenta de tipo NETTING. Para entenderlo, es necesario observar el valor que se indica en la línea 26, donde le indicamos al servidor el volumen que se va a utilizar. Si se usara la acción TRADE_ACTION_CLOSE_BY, el servidor simplemente cerraría la posición.
De lo contrario, nos veríamos obligados a crear toda una lógica y también un código para hacer algo distinto a lo que nos permite la cuenta de tipo NETTING. Esa cosa es la parcial, donde cerramos parte de nuestra posición. No incluyo aquí la lógica para hacer parciales ni la lógica para cambiar de posición. Se trata de una cuestión de decisión respecto a lo que se implementará. Como podemos usar este sistema en cualquier tipo de cuenta, debemos priorizar algunas cosas en detrimento de otras. Y dado que la cuenta NETTING permite cosas que no son posibles en la cuenta HEDGING, y viceversa, intento cubrir ambos tipos de cuenta. Por eso, esta explicación es importante.
Todos los demás campos tienen como objetivo decirle al servidor si vamos a vender o comprar, esto en la línea 22. El precio en el que se debe realizar la operación, esto en la línea 23. El ticket de la posición a usar, esto en la línea 24. El nombre del símbolo, esto en la línea 25. Un detalle: este nombre debe coincidir con el nombre presente en el ticket, de lo contrario, se producirá un error. Además del volumen, que tiene mucho que ver con lo que se hará, esto en la línea 26. Y, por último, la derivación, que sería un desvío en el precio informado, esto en la línea 27. Toda esta información le indicará al servidor que ejecute el requerimiento enviado al mercado, es decir, realizará la operación al mejor precio posible, modificando así el volumen, o la dirección esperada del movimiento, o cerrando la posición.
¿Han notado que, a diferencia de la acción TRADE_ACTION_CLOSE_BY, que solo cierra la posición, aquí podemos hacer muchas más cosas? Por eso, estoy utilizando la acción TRADE_ACTION_DEAL. Pero solo sirve cuando ya se tiene una posición abierta. No sirve para cambiar los puntos de stop loss o take profit. Esto se realiza mediante otra acción que se verá en el futuro. Por ahora, vamos a utilizar el sistema de MetaTrader 5. Aunque sea posible negociar a través del histórico del contrato futuro, no se verán las líneas de precio de ese contrato. Estarán presentes en el contrato indicado en la posición. No obstante, esto se modificará en el futuro.
Muy bien, falta una última cosa por ver: el código del asesor experto. Este 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.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 fuente del EA
Ya ha visto casi todo este código en el artículo Simulación de mercado (Parte 02): Cross Order (II), por lo que solo se han realizado pequeños cambios para poder utilizar la clase C_Orders. Dichos cambios son bastante simples. Sin embargo, si no tienes mucha experiencia programando en C, algunas cosas aquí podrían parecerte extrañas. Pero no es nada grave, solo son diferentes. El primer cambio está en la línea nueve, donde incluimos el archivo de encabezado visto en este artículo. Otro cambio es la adición de la línea 15, donde declaramos la clase con un puntero.
En la línea 21 inicializamos la clase mediante el operador NEW. Pero tal vez notes algo extraño en esta línea. El constructor de la clase está declarado para recibir un valor de 64 bits, pero vemos algo extraño: 0xC0DEDAFE78514269, donde hay letras y no solo números. ¿Qué es esto? En realidad, no es nada raro. Se trata de un valor hexadecimal. Fíjate en que al principio del valor hay un 0x; esto le indica al compilador que el valor que sigue es hexadecimal. Esto resulta muy útil en diversas situaciones. Aunque, cuando lo veas como el número mágico de la posición, verás algo completamente diferente, ya que ese valor es el valor decimal que representa este código hexadecimal. Dejaré el valor decimal para que lo veas después. No lo revelaré aquí.
Y, como última modificación, tenemos la línea 30. Esta línea transferirá los datos para que la clase pueda realizar su trabajo. De este modo, el asesor experto podrá manejar los mensajes proporcionados por el indicador Chart Trade. Como se puede observar, con muy poco código se ha conseguido algo admirable e interesante, ya que todo el sistema está dividido en módulos. De esta forma, cuando se mejora un módulo, no se afecta directamente a otro, lo que permite que el código crezca de manera más segura y con mucha más agilidad. En comparación con un sistema monolítico, en el que todo estaría contenido en un único archivo gigantesco, el trabajo de desarrollo y mejora resulta mucho más tedioso.
Consideraciones finales
En estos dos últimos artículos, presenté el asesor experto junto con el código de la clase responsable de enviar órdenes al mercado. Este sistema puede funcionar perfectamente en cualquier tipo de cuenta, ya sea NETTING o HEDGING, por lo que se puede utilizar el mismo asesor experto tanto en mercados de Forex como en mercados de acciones o de mostrador. Esto hace que todo sea mucho más sencillo y accesible.
Sin embargo, este asesor experto aún no está terminado. Queda mucho por hacer antes de que se pueda implementar su función principal. Y su papel principal es: simular el servidor de tráding para usarlo en la repetición/simulador.
Sin embargo, antes de hacer esto, tendremos que hacer algunas cosas más. Así que no te pierdas el próximo artículo, en el que empezaré a explicar algo que ya es posible hacer, incluso con este asesor experto aún sin terminar. Quiero empezar a explicarlo ahora para simplificar las cosas. Si lo dejo para después, la explicación podría volverse demasiado complicada. Y, si esto ocurre, querido lector, quizá no puedas entender todos los detalles.
| Archivo | Descripción |
|---|---|
| Experts\Expert Advisor.mq5 | Demuestra la interacción entre Chart Trade y el Asesor Experto (Es necesario el Mouse Study para la interacción) |
| Indicators\Chart Trade.mq5 | Crea la ventana para configurar la orden a ser enviada (es necesario el Mouse Study para la interacción) |
| Indicators\Market Replay.mq5 | Crea los controles para la interacción con el servicio de reproducción/simulador (es necesario el 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 como en el mercado real) |
| Servicios\Market Replay.mq5 | Crea y mantiene el servicio de reproducció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/12598
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.
Simulación de mercado (Parte 06): Transfiriendo información desde MetaTrader 5 hacia Excel
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 8): Panel de métricas
Desarrollamos un asesor experto multidivisas (Parte 22): Inicio de la transición a la sustitución dinámica de ajustes
Algoritmo de búsqueda circular — Circle Search Algorithm (CSA)
- 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