
Simulación de mercado (Parte 04): Creación de la clase C_Orders (I)
Introducción
En el artículo anterior, "Simulación de mercado (Parte 82): Una cuestión de rendimiento", realizamos algunos ajustes en las clases para sortear, al menos por ahora, algunos problemas que estábamos enfrentando. Tales problemas estaban perjudicando el rendimiento general del sistema. A pesar de que, por el momento, resolvimos dichas cuestiones, ahora tenemos algo realmente bastante complicado por hacer. No en esta primera parte, pues ya abordé el tema en otros artículos más antiguos. Sin embargo, ahora estamos haciendo las cosas de una forma un poco diferente. Por esta razón, la manera en que trataremos el asunto también será algo distinta.
Sé que muchos deben estar ansiosos por ver la aplicación de reproducción/simulador ejecutando órdenes. Sin embargo, antes de hacer eso, necesitamos asegurarnos de que el sistema de órdenes esté funcionando para poder comunicarnos con el servidor de negociación real, ya sea a través de una cuenta DEMO o de una cuenta REAL. De cualquier forma, las aplicaciones —que en este caso serán el indicador Chart Trade, el indicador de Mouse y el Asesor Experto (Expert Advisor)— deberán trabajar en perfecta armonía, de modo que la comunicación con el servidor real de negociación ocurra sin mayores inconvenientes.
Además de las aplicaciones que mencioné arriba, aún será necesario crear otras cosas. No obstante, tales elementos pueden dejarse de lado por ahora, ya que su creación, tanto en la fase de idealización como en la de implementación, depende de diversos otros elementos que serán, de hecho, resueltos en esta etapa del desarrollo.
Entonces, en este artículo comenzaré a explicar cómo haremos para comunicarnos con el servidor de negociación. Sé que muchos de ustedes ya deben saber hacer esto perfectamente. En ese caso, les pido un poco de paciencia, pues no haremos las cosas de forma muy apresurada. Es necesario que comprendan y entiendan perfectamente lo que está ocurriendo en realidad. Aquí, a diferencia de lo que normalmente sucede, las cosas se harán por módulos, y cada uno de ellos es responsable de ejecutar una tarea muy específica. Si un módulo falla, todo el sistema fallará. Esto se debe a que no existe ninguna redundancia que garantice que las cosas funcionen de otra manera.
Comprender la idea
Si has estado siguiendo esta secuencia, habrás notado que en el artículo "Desarrollo de un sistema de repetición (Parte 78): Un nuevo Chart Trade (V)", en el cual comencé a mostrar cómo se realizará la interacción, el Asesor Experto, de hecho, no sabe de dónde provienen las órdenes. Sin embargo, sí sabe interpretar los mensajes recibidos. A pesar de que los medios utilizados en ese momento no permitían el uso de un sistema de órdenes cruzadas, resolvimos ese mismo problema en los artículos que vinieron después, como en "Simulación de mercado (Parte 02): Orden cruzada (II)", donde mostré cómo quedaría el sistema de mensajería que se le pasará al Asesor Experto.
Todo este mecanismo forma parte de algo más amplio. Entender este mecanismo de mensajes es, en realidad, fundamental para comprender lo que veremos a partir de este artículo. No subestimes ni ignores lo que se explicó en los artículos anteriores, pero, sobre todo, no creas que ya lo has entendido todo antes de verlo funcionando.
Si comprendiste el código del Asesor Experto presentado anteriormente, creo que no tendrás muchas dificultades para entender lo que será programado aquí. Pero, una vez más, no pienses que ya lo sabes todo con solo mirar el código. Es necesario entender cómo funciona cada detalle para tener una visión más general de todo el sistema.
Iniciar las órdenes de mercado
Como hay varias cosas que deben explicarse en el código que verás un poco más abajo en este artículo, no mostraré toda la clase de una sola vez. De la misma forma, no voy a volcar una gran cantidad de código en el artículo. Esta parte realmente necesita ser comprendida con mucho cuidado. Esto se debe a que el código que verás tratará, en efecto, con dinero. Y es con tu dinero, estimado lector. Entender cómo funciona cada detalle te permitirá sentirte más seguro y confiado con el código que será presentado. No quiero que intentes modificarlo solo porque no lo entiendes. Quiero que, en caso de que llegues a modificarlo, lo hagas porque necesitas añadir alguna funcionalidad que no está presente en él, y no porque estés acostumbrado a usar este o aquel tipo de código. Así que procura comprender el código, ya que será utilizado cuando llevemos a cabo la simulación de órdenes.
Para facilitar al máximo la explicación, comencemos haciendo lo siguiente: veremos la clase inicial, que será responsable del envío de las órdenes al servidor, ya sea este el servidor real —que es el que trataremos ahora— o el servidor simulado, que se abordará en el futuro. De cualquier modo, el código se inicia como se muestra a continuación. Este primer fragmento de código está completo.
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_MagicNumber;} 011. //+------------------------------------------------------------------+ 012. private : 013. //+------------------------------------------------------------------+ 014. MqlTradeRequest m_TradeRequest; 015. ulong m_MagicNumber; 016. bool m_bTrash; 017. //+------------------------------------------------------------------+ 018. struct stChartTrade 019. { 020. struct stEvent 021. { 022. EnumEvents ev; 023. string szSymbol, 024. szContract; 025. bool IsDayTrade; 026. ushort Leverange; 027. double PointsTake, 028. PointsStop; 029. }Data; 030. //--- 031. bool Decode(const EnumEvents ev, const string sparam) 032. { 033. string Res[]; 034. 035. if (StringSplit(sparam, '?', Res) != 7) return false; 036. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 037. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 038. 039. return true; 040. } 041. //--- 042. }m_ChartTrade; 043. //+------------------------------------------------------------------+ 044. ulong SendToPhysicalServer(void) 045. { 046. MqlTradeCheckResult TradeCheck; 047. MqlTradeResult TradeResult; 048. 049. ZeroMemory(TradeCheck); 050. ZeroMemory(TradeResult); 051. if (!OrderCheck(m_TradeRequest, TradeCheck)) 052. { 053. PrintFormat("Order System - Check Error: %d", GetLastError()); 054. return 0; 055. } 056. m_bTrash = OrderSend(m_TradeRequest, TradeResult); 057. if (TradeResult.retcode != TRADE_RETCODE_DONE) 058. { 059. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 060. return 0; 061. }; 062. 063. return TradeResult.order; 064. } 065. //+------------------------------------------------------------------+ 066. ulong ToMarket(const ENUM_ORDER_TYPE type) 067. { 068. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 069. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 070. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 071. 072. ZeroMemory(m_TradeRequest); 073. m_TradeRequest.magic = m_MagicNumber; 074. m_TradeRequest.symbol = m_ChartTrade.Data.szContract; 075. m_TradeRequest.price = NormalizeDouble(price, nDigit); 076. m_TradeRequest.action = TRADE_ACTION_DEAL; 077. m_TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 078. m_TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 079. m_TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 080. m_TradeRequest.type = type; 081. m_TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 082. m_TradeRequest.stoplimit = 0; 083. m_TradeRequest.expiration = 0; 084. m_TradeRequest.type_filling = ORDER_FILLING_RETURN; 085. m_TradeRequest.deviation = 1000; 086. m_TradeRequest.comment = "Order Generated by Experts Advisor."; 087. 088. MqlTradeRequest TradeRequest[1]; 089. 090. TradeRequest[0] = m_TradeRequest; 091. ArrayPrint(TradeRequest); 092. 093. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 094. }; 095. //+------------------------------------------------------------------+ 096. public : 097. //+------------------------------------------------------------------+ 098. C_Orders(const ulong magic) 099. :m_MagicNumber(magic) 100. { 101. } 102. //+------------------------------------------------------------------+ 103. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 104. { 105. switch (id) 106. { 107. case CHARTEVENT_CUSTOM + evChartTradeBuy : 108. case CHARTEVENT_CUSTOM + evChartTradeSell : 109. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 110. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 111. { 112. case evChartTradeBuy: 113. ToMarket(ORDER_TYPE_BUY); 114. break; 115. case evChartTradeSell: 116. ToMarket(ORDER_TYPE_SELL); 117. break; 118. case evChartTradeCloseAll: 119. break; 120. } 121. break; 122. } 123. } 124. //+------------------------------------------------------------------+ 125. }; 126. //+------------------------------------------------------------------+
Código fuente del archivo C_Replay.mqh
Muy bien, entonces vamos a entender qué hace este código. Aunque pueda parecer complicado, es bastante simple y solo hace una única cosa: envía órdenes de compra o venta a mercado, a petición de la interacción entre el usuario y el indicador Chart Trade. Pero ¿cómo logra hacer eso? Probablemente estés pensando que modificaremos el indicador Chart Trade de alguna manera. Sin embargo, si pensaste eso, entonces no comprendiste realmente cómo funciona el sistema. Los indicadores no nos permiten, en realidad, enviar órdenes al servidor de negociación. Sirven para indicar algo en el gráfico o, en el caso del indicador Chart Trade, permitir interacciones del usuario con el resto del sistema.
Así que, antes de presentar el Asesor Experto que realmente hará uso de este archivo de cabecera, vamos a retroceder un poco en el tiempo y recordar lo que ya fue explicado. De esta manera, será más sencillo comprender este archivo de cabecera.
En el artículo "Desarrollo de un sistema de repetición (Parte 78): Un nuevo Chart Trade (V)", se explicaron los mensajes que el Asesor Experto debía interceptar. Debes haber notado que hacíamos uso del procedimiento OnChartEvent para capturar tales mensajes. Dentro de ese procedimiento, hacíamos una llamada a una clase. Esa clase traducía el mensaje recibido e imprimía en el terminal un mensaje para que pudiéramos analizarla. Bien, esa era la parte fácil y tranquila, ya que no necesitábamos utilizar esos datos para comunicarnos con el servidor de negociación.
Al visualizar el código fuente del Asesor Experto, notarás que, en el procedimiento OnChartEvent, existe una llamada a DispatchMessage. Tal llamada llegará, efectivamente, a este archivo de cabecera. Más precisamente, llamará a la línea 103 de este archivo. Pero antes de ver eso, comencemos por el inicio. Es decir, veamos el constructor de la clase.
Es bastante simple y está presente en la línea 98. Cuando se llama a este constructor, debe recibir un argumento. Dicho argumento será utilizado para identificar esta clase con un número mágico. Atención a este detalle: no estoy identificando al Asesor Experto, sino a la clase. Aunque sea utilizada dentro del Asesor Experto, no estoy identificando al Asesor Experto. ¿Y por qué esta distinción? El motivo es que, en ocasiones, es interesante usar clases parecidas, pero diferentes, para realizar el mismo tipo de tarea. En este momento, esto puede no tener sentido, pero lo tendrá a medida que la explicación avance, ya que esta clase no hace uso de ciertas cosas, como se verá más adelante.
Entonces, supongamos lo siguiente: una vez que ya no existan órdenes y solamente posiciones, puede que desees que estas sean tratadas de una manera específica. Hacer eso utilizando varios Asesores Expertos distintos es algo muy propenso a errores. Además, no es posible mantener más de un Asesor Experto en el mismo gráfico. No estoy diciendo que no puedas usar más de un Asesor Experto en el mismo activo. Estoy diciendo que no es posible usar más de uno en el mismo gráfico. No confundas las cosas.
Sin embargo, abrir varios gráficos del mismo activo solo para colocar diferentes Asesores Expertos en cada uno no me parece algo muy manejable. Algunas personas logran administrar eso, pero yo lo considero muy confuso. En cambio, colocar clases ligeramente diferentes dentro de un mismo Asesor Experto, de manera que trabajen en armonía, es algo perfectamente administrable y posible de controlar. Para hacer esto de forma más sencilla, en el momento en que el constructor crea la clase, esta será identificada mediante un número mágico. Ese mismo número será utilizado luego en las órdenes y posiciones. Esto se verá más adelante.
Ese número se inicializa en la línea 99, y la variable que lo almacena está declarada en la línea 15. Observa que, en la línea 12, existe una cláusula privada que hace que todo lo comprendido entre esa línea y la línea 96 quede encapsulado dentro de la clase, incluidas las variables declaradas entre las líneas 14 y 16.
Antes de continuar, quiero llamar tu atención sobre la línea 10. Aquí tenemos una función cuyo propósito es devolver al autor de llamada el número mágico definido en esta clase. Sin embargo, el hecho de que esta línea esté ubicada entre la línea 8, donde tenemos una cláusula protegida, y la línea 12, donde está la cláusula privada, impide que esta función sea utilizada fuera del sistema de herencia. Es decir, si intentas acceder a esta función protegida desde fuera de una clase que herede de esta clase C_Orders, recibirás un mensaje de error del compilador.
Entonces, por motivos futuros, esta función se encuentra allí. No quiero tener que agregarla más adelante solo para cubrir una tarea de uso personal. Tal vez explique de qué se trata en el futuro. Pero, hasta entonces, no necesitas preocuparte por eso. Solo debes saber que esta función, en la línea 10, no puede ser accedida fuera del sistema de herencia y que sirve únicamente para devolver el número mágico de esta clase.
Ahora, para explicar de forma más clara el resto de este código, vamos a dividirlo en temas. Sin embargo, cada uno de ellos hará referencia a este mismo código.
¿Un procedimiento dentro de una estructura?
En el artículo "Desarrollo de un sistema de repetición (Parte 78): Un nuevo Chart Trade (V), mostré el sistema de traducción utilizando una clase. Aquí, estoy usando algo un poco diferente. Estoy usando una estructura. ¿Pero es posible hacer eso? Sí, y lo estoy haciendo, ya que las clases no son más que una estructura más elaborada. Sin embargo, como lo que necesitamos puede modelarse de forma más simple, decidí hacerlo dentro de una estructura. Esto puede observarse entre las líneas 18 y 42.
Ahora, presta atención: en la línea 18, estoy declarando la estructura. Si estuviera declarando una clase en su lugar, todo lo que se encuentra entre las líneas 18 y 42 sería tratado como privado. En ese caso, sería necesario añadir una cláusula pública para modificar el tipo de acceso. Eso no sería un problema. Pero piensa en lo siguiente: ¿por qué alguien crearía una clase dentro de otra? Eso no tiene sentido, además de que, muchas veces, vuelve el código mucho más confuso.
No obstante, como los datos que necesitamos están siendo recibidos en un mensaje, y dicho mensaje puede entenderse como un conjunto de variables, tal como se explicó en el artículo mencionado al inicio de este tema, ¿por qué no pensar en el mensaje como una gran cadena de variables? De esta manera, sería mucho más sencillo entender su contenido. ¿Estás de acuerdo?
Así, el mensaje es tratado como una estructura de datos, ubicada entre las líneas 20 y 29. Pero espera un momento. ¿La estructura de datos no estaría entre las líneas 18 y 42? No. La estructura del mensaje está entre las líneas 20 y 29. Sin embargo, como se explicó en un artículo anterior, el mensaje debe pensarse como una cadena (string). No obstante, si tomas esa cadena y la fragmentas con base en la longitud de la estructura de datos del mensaje, corres un gran riesgo de no entender correctamente el mensaje. Por tanto, necesitamos algo de código adicional para ayudarnos a interpretarlo.
Por esa razón, entre las líneas 31 y 40, tenemos una función. Está dentro de la estructura principal, pero no forma parte de la estructura del mensaje. ¿Por qué? Porque las cosas están separadas. Nada impide que pongas todo junto: la estructura del mensaje, las funciones y los procedimientos. Pero, cuando lo hacemos así, corremos el riesgo de tener problemas de implementación.
Ahora quiero que prestes mucha atención, porque esto es bastante confuso. Si logras entenderlo, podrás comprender por qué muchos programadores evitan usar estructuras incluso cuando podrían hacerlo. Y, cuando algunos programadores intentan utilizar estructuras, terminan convirtiendo sus programas en una bomba de tiempo. Especialmente cuando el código está escrito en C heredado.
Observa la línea 36. Esa línea representa el verdadero peligro de mezclar las cosas. Nuevamente, no hay ninguna obligación de hacerlo. Pero, la línea 36 lo deja claro, ya que inserta todos los valores deseados en la estructura de datos. Si, en lugar de una estructura de datos, tuviéramos, por ejemplo, una función o un procedimiento, ¿qué sucedería al ejecutarse la línea 36? Y lo que es peor: ¿qué ocurriría realmente al llamar a la función o procedimiento que está en la memoria y que fue sobrescrito por la línea 36? Si no tienes idea de lo que podría pasar, considérate afortunado por no haber experimentado todavía el verdadero problema y peligro del lenguaje C heredado. Pero, si lo sabes o ya lo has visto ocurrir, comprendes por qué se crearon las clases.
No entraré, de hecho, en los detalles de lo que podría pasar. Pero ten presente que un programador realmente malicioso puede hacer cosas que ni te imaginas que sean posibles. Sin embargo, volvamos a la explicación de nuestro código. La función presente en la línea 31 hace exactamente lo mismo que se hacía anteriormente. Así que no veo necesidad de explicar nuevamente lo mismo. No obstante, fíjate en la línea 110, donde se llama la función. Observa que allí no estamos llamando a la función directamente por su nombre. Es necesaria una referencia adicional. Esa referencia es justamente el nombre de la variable que contiene los datos que representan nuestra estructura de datos. Aunque pueda parecer confuso, no lo es. Olvida por un momento que estamos usando una estructura y piensa en esto como si fuera una clase. ¿No harías las cosas del mismo modo? Entonces, en realidad, no hay ninguna confusión aquí.
Sin embargo, muchos pueden imaginar que la función DECODE debería estar dentro de la clase C_Orders. Así, no tendríamos que llamarla del modo en que se hace en la línea 110. No obstante, esta función DECODE existe únicamente porque necesitamos decodificar el mensaje que contiene nuestros datos. Por eso, no tiene sentido declararla dentro de la clase C_Orders. Nuevamente, piensa siempre en separar las cosas de forma lógica, no de la manera que te resulte más conveniente. Si DECODE se declarara dentro de la clase C_Orders, cuando en el futuro quisieras usar un procedimiento con un nombre similar, pero orientado a otra estructura de datos, tendrías que crear un nombre completamente diferente para evitar conflictos. Y lo que es peor: con el tiempo, al modificar el código, tendrías dificultades para entender qué hace cada cosa. Así, las posibilidades de error aumentarían de forma incontrolable.
Hablar con el servidor
Esta parte suele ser la más confusa para los programadores principiantes. Esto se debe a que, si esta sección te resulta confusa, es porque no has comprendido bien qué es lo que realmente debes hacer. A diferencia de un operador, que observa el gráfico y ve precios y cotizaciones, tú, como programador, debes ver las cosas como si se tratara de un formulario que debe ser rellenado correctamente. Si ese formulario contiene algún error, el servidor —que deberías imaginar como si fuera la persona que te contratará— no lo aceptará.
Así como alguien para quien deseas trabajar, el servidor no tiene la obligación de entender lo que hay en el formulario. Simplemente lo revisa y, si no le agrada, será descartado. A diferencia de un empleador, el servidor sí te dirá el motivo del rechazo. Por eso, un procedimiento de comunicación bien estructurado sin duda te permitirá resolver los problemas con facilidad.
Para hacer esto, tenemos la función que se encuentra entre las líneas 44 y 64. A pesar de su inmensa simplicidad, esta función contiene todo lo que necesitamos para saber qué ocurrió. Observa que existe una secuencia lógica de pasos que deben seguirse. No puedes hacer las cosas de cualquier manera. Es necesario seguir el siguiente orden: primero, hay que poner en cero el contenido de la memoria de los datos de respuesta del servidor. Esto se hace en las líneas 49 y 50. Luego, para evitar enviar una orden que no sea aceptada por un motivo trivial, comprobamos si dicha orden tiene algún problema. Es como si revisaras el formulario antes de enviarlo al empleador. Esa revisión se realiza en la línea 51. Si hay algún error, el código de error lo indicará, y así podrás resolverlo y volver a intentarlo. Si todo parece estar correctamente en orden, enviamos nuestra orden al servidor de negociación en la línea 56.
Para evitar que el compilador nos advierta sobre la falta de verificación del valor de retorno del servidor, enviamos ese retorno a una variable que actuará básicamente como un basurero. La razón de esto es que la respuesta de la función en sí no nos interesa. Lo que realmente importa es el contenido de la estructura de respuesta. Para verificarlo, comprobamos en la línea 57 si el resultado es diferente al valor TRADE_RETCODE_DONE. Si es distinto, significa que ocurrió algún tipo de error. Entonces, para saber cuál fue el error, imprimimos el valor del código de error en el terminal. Eso se hace en la línea 59.
De cualquier forma, si ocurre un error, la función de la línea 44 devolverá el valor 0. En todos los demás casos, devolverá el valor del ticket de respuesta del servidor. Y este ticket es el que identifica nuestra orden o posición en el servidor. Esto se verá más adelante, cuando empecemos a manipular dichas posiciones u órdenes. Por ahora, apenas estamos comenzando, así que el único procedimiento realizado es el envío de una orden de negociación a precio de mercado.
Ese tipo de orden se lleva a cabo en otro punto, o mejor dicho, en otra función. Esta aparece en la línea 66. Dicha función recibirá solo un argumento. Ese argumento indica si estaremos comprando o vendiendo. Se llama en dos ubicaciones: la primera en la línea 113 y la segunda en la línea 116. Estos puntos se activan cuando un mensaje del Chart Trade nos solicita realizar alguna acción a precio de mercado. Pero si esta función en la línea 66 recibe únicamente un argumento, que indica si estaremos comprando o vendiendo, ¿cómo sabrá el servidor dónde colocar nuestro stop loss o take profit? ¿Y cómo se transmitirán las demás informaciones, como el nivel de apalancamiento y el activo, en caso de que estemos utilizando un sistema de órdenes cruzadas? ¿Cómo se transmitirán esos datos? Genial. Ahora hemos llegado a un punto interesante.
Rellenar el formulario de solicitud
Si prestaste atención a la función de comunicación con el servidor, ubicada en la línea 44, habrás notado que hacemos uso de una estructura que no está declarada dentro de esa función en las líneas 51 y 56. Dicha estructura, de hecho, está declarada en el cuerpo de la clase C_Orders. Esto se encuentra en la línea 14. Se trata de una estructura global, aunque privada para la clase. Es decir, todo el cuerpo de la clase puede acceder a esta variable, pero ningún código fuera de la clase tiene acceso a ella.
Por lo tanto, esta variable usada en la función de la línea 44 debe ser completada en algún lugar. Ese lugar es este de aquí, entre las líneas 66 y 94. El correcto llenado de la estructura representada por esta variable permitirá que el servidor ejecute exactamente lo que esperamos. Es decir, una venta a precio de mercado o una compra a mercado. Las órdenes pendientes se abordarán más adelante, al igual que la forma correcta de modificar los niveles de take profit y stop loss de una posición.
Para llenar correctamente la estructura, se necesitan algunos datos. Algunos de estos datos los proporciona el Chart Trade; otros, el propio MetaTrader 5. En cualquier caso, hay una cierta secuencia lógica que debe seguirse para hacer las cosas. El primer paso es determinar cuál es el precio actual del activo. Esto se consigue en la línea 68. Presta atención a cada detalle en esta línea, ya que cada uno de ellos es muy importante.
También necesitamos saber cuál será el volumen a negociar. Muchas personas se complican precisamente en este punto, ya que el volumen que espera el servidor es un múltiplo de otro dato. El volumen mostrado en el Chart Trade no es el volumen negociado, sino el nivel de apalancamiento. Ambas cosas son distintas, aunque están relacionadas. El nivel de apalancamiento indica cuántas veces se utilizará el volumen mínimo esperado. Así que no confundas los conceptos. También necesitamos saber cuántos dígitos decimales espera el servidor para el activo en cuestión. Esa información se encuentra en la línea 70.
Bien, ahora ya tenemos lo básico. Podemos comenzar a llenar la estructura. Lo primero que hay que hacer es limpiar los datos de la estructura. Esto se realiza en la línea 72. A partir de ahí, cada una de las siguientes líneas indica exactamente lo que el servidor deberá hacer. Esta forma de rellenado, mostrada entre las líneas 73 y 86, funcionará tanto para mercados bursátiles como extrabursátiles, así como para fórex. Lo importante aquí es saber que cada uno de estos campos tiene un significado. Malinterpretar cualquiera de ellos puede hacerte perder dinero o una oportunidad. Sin embargo, explicar el significado de cada uno de estos campos quedará para otro artículo, ya que será más fácil comprender cada uno cuando abordemos el sistema de órdenes pendientes.
De cualquier forma, la información creada se mostrará en el terminal para que puedas verificarla más adelante. Esto se hace utilizando las líneas 88 a 91. Aunque podríamos hacerlo de otro modo, usar la llamada ArrayPrint, de la biblioteca estándar de MQL5, resulta considerablemente más simple y práctico, ya que todos los campos se presentan de forma bastante clara. Por último, la función devolverá la respuesta de la solicitud, en la línea 93.
Consideraciones finales
Aunque este artículo trate únicamente de una parte del código, específicamente del envío de órdenes de mercado, el archivo de cabecera presentado es capaz, junto con el indicador Chart Trade, de ejecutar compras o ventas a precio de mercado. Pero quizá aún no hayas resuelto todas tus dudas. Eso se debe a que no expliqué el significado de cada valor en la estructura MqlTradeRequest. Pero no te preocupes, lo explicaré con calma cuando abordemos las órdenes pendientes. Por lo demás, nos vemos en el próximo artículo, donde continuaremos tratando el código fuente del Asesor Experto.
Archivo | Descripción |
---|---|
Experts\Expert Advisor.mq5 | Demuestra la interacción entre el 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 simulador de reproducción como en el mercado real) |
Services\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/12589
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.





- 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