English Русский 中文 Deutsch 日本語 Português
preview
Cómo construir un EA que opere automáticamente (Parte 08): OnTradeTransaction

Cómo construir un EA que opere automáticamente (Parte 08): OnTradeTransaction

MetaTrader 5Trading | 1 febrero 2023, 08:53
906 2
Daniel Jose
Daniel Jose

Introducción

En los artículos anteriores, Cómo construir un EA que opere automáticamente (Parte 06): Tipos de cuentas (I) y Cómo construir un EA que opere automáticamente (Parte 07): Tipos de cuentas (II), toda la explicación se enfocó en intentar mostrar que debemos tener cierto cuidado al desarrollar un EA que opere de manera automática.

Antes de empezar a ver realmente cómo el código del EA debe trabajar para que sea automatizado, es necesario comprender cómo se comunica con el servidor de negociación. Para ello, véase la figura 01:

Figura 01

Figura 01 - Flujo de mensajes


La figura 01 muestra el flujo de mensajes para que el EA pueda enviar órdenes o peticiones al servidor comercial. Fíjate en la dirección de las flechas.

El único momento en que las flechas son bidireccionales es cuando la clase C_Orders envía un pedido al servidor, utilizando la función OrderSend, porque, así, ella recibirá una respuesta del servidor a través de una estructura. Aparte de este momento, todos los demás puntos son direccionales. Pero aquí estoy mostrando sólo el flujo para enviar órdenes de mercado o para colocar órdenes en el libro. Como tal, el sistema es extremadamente sencillo.

En el caso de un EA 100% automatizado, todavía necesitaremos algunas cosas más aquí. Y para un EA con una automatización mínima, todavía tendremos que añadir algunos detalles, pero todo sucederá entre el EA y la clase C_Manager. En ningún otro momento tendremos que añadir código. Aunque en un EA 100% automatizado tendremos que eliminar la clase C_Mouse, ya que no servirá de nada para un EA 100% automático. Es muy importante que entiendas este flujo de mensajes, porque sin entenderlo no podrás seguir el resto del artículo.


Cómo adicionar las rutinas de control y accesibilidad

El mayor problema es que muchos de los que utilizan el lenguaje MQL5 para crear los EA no hacen uso de algunas características que el lenguaje promueve. Tal vez por ignorancia o por alguna otra razón, esto no importa. Pero si vas a utilizar MQL5 de una forma más completa, con el fin de mejorar la robustez y fiabilidad de tu código, sinceramente tienes que pensar en utilizar algunos de los recursos que este lenguaje pone a tu disposición.

Lo primero que haremos será añadir 3 nuevos procedimientos a la clase C_Manager. Estos servirán para que la clase libere el EA o conozca lo que la EA tiene previsto hacer. La primera de estas rutinas se ve a continuación:

inline void EraseTicketPendig(const ulong ticket)
                        {
                                m_TicketPending = (ticket == m_TicketPending ? 0 : m_TicketPending);
                        }

Esta rutina elimina el valor del ticket pendiente, cuando el ticket informado es igual al ticket pendiente. Por lo general, esto no suele ocurrir. Una orden pendiente no será eliminada por el EA, esta eliminación suele ocurrir por interferencia del operador o usuario del EA, lo cual no es aconsejable. Pero si el EA se da cuenta de que una orden pendiente que había colocado en el libro ha sido retirada por el usuario, debe informar a la clase C_Manager para que permita al EA colocar, si es necesario, una nueva orden pendiente en el libro.

La siguiente función a añadir se ve en el siguiente código:

                void PedingToPosition(void)
                        {
                                ResetLastError();
                                if ((m_bAccountHedging) && (m_Position.Ticket > 0)) SetUserError(ERR_Unknown);
                                        else m_Position.Ticket = (m_Position.Ticket == 0 ? m_TicketPending : m_Position.Ticket);
                                m_TicketPending = 0;
                                if (_LastError != ERR_SUCCESS) UpdatePosition(m_Position.Ticket);
                                CheckToleranceLevel();
                        }

Este código sirve para que el EA informe a la clase C_Manager que una orden pendiente acaba de convertirse en una posición o ha realizado algún cambio en la posición. Ten en cuenta que cuando esta función se ejecuta el ticket de la orden pendiente será borrado por la clase C_Manager, lo que le permite al EA colocar una nueva orden pendiente. Pero las cosas sólo continuarán si no se ha generado ningún error crítico, que será analizado por esta función, que vimos en el artículo anterior. Pero esta función, de hecho, no funciona sola, necesita otra función, que se ve a continuación:

                void UpdatePosition(const ulong ticket)
                        {
                                int ret;
                                
                                if ((ticket == 0) || (ticket != m_Position.Ticket)) return;
                                if (PositionSelectByTicket(m_Position.Ticket))
                                {
                                        ret = SetInfoPositions();
                                        m_StaticLeverage += (ret > 0 ? ret : 0);
                                }else ZeroMemory(m_Position);
                                ResetLastError();
                        }

Todavía faltan otras 2 funciones en la clase C_Manager. Pero como son funciones de automatización, no voy a entrar en eso todavía.

Pero ahora en una forma mucho más completa, finalmente tenemos la clase C_Manager y EA siendo amigables entre sí. Hasta el punto de que ambos puedan trabajar y asegurarse de que no son agresivos ni antipáticos. De esta forma, el flujo de mensajes entre el EA y la clase C_Manager pasa a ser el siguiente, como se ve en la Figura 02:

Figura 02

Figura 02 - Flujo de mensajes con las nuevas rutinas implementadas


Puede que este flujo de mensajes te resulte demasiado complicado o que no sea funcional en absoluto. Pero es exactamente lo que se está implementando.

Si bien al ver la figura 02 debes estar pensando que el código del EA debe ser muy complicado, es mucho más simple de lo que muchos consideran un código necesario para utilizar realmente en un EA. Más aún en uno que se automatice. Recuerden lo siguiente: en realidad el EA no genera ninguna operación. Es sólo un medio o herramienta para comunicarse con el servidor comercial. Así que en realidad sólo reacciona a los gatillos que se le aplican.

Usando este entendimiento como base, vamos a dar un paseo a través del código del EA en la situación actual antes de llegar a la automatización. Pero, para los que no lo vieron, el código del EA no sufrió grandes cambios desde el último artículo en el que había aparecido, que era Aprendiendo a construir un EA que opere de forma automatizada (Parte 05): Gatillos manuales (II). Los únicos cambios que se produjeron realmente se ven justo debajo:

int OnInit()
{
        manager = new C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08);
        mouse = new C_Mouse(user05, user06, user07, user03, user02, user01);
        (*manager).CheckToleranceLevel();

        return INIT_SUCCEEDED;
}

Básicamente era necesario añadir sólo y únicamente esta nueva línea. Así, se trataba de comprobar si se había producido algún error más grave o crítico, pero ahora viene una pregunta: ¿Cómo hacer que el EA le diga a la clase C_Manager lo que va a pasar con respecto al sistema de órdenes? En efecto, muchas personas se quedarían sin saber cómo actuar en esta situación, tratando de averiguar cómo descubrir lo que se está haciendo en el servidor comercial. Aquí es donde reside el peligro.

La primera cosa que realmente tienes que entender es que la plataforma MetaTrader 5 así como el lenguaje MQL5 no son herramientas convencionales, es decir, tú no vas a crear realmente un programa que necesite quedarse buscando la información. Esto se debe a que el sistema se basa en eventos y no en procesos. En una programación basada en eventos, no hay que pensar en pasos, hay que hacerlo de otra manera.

Para que te hagas una idea de lo diferente que tiene que ser la forma de pensar, piensa en lo siguiente: Si estás conduciendo, básicamente tu intención es llegar a un destino determinado. Pero por el camino tendrás que resolver algunas cosas que sucederán de formas aparentemente inconexas. Pero todos influirán en tu dirección, como: Frenar, acelerar, tener que cambiar la trayectoria por algún imprevisto que haya ocurrido. Todos estos son acontecimientos que tú sabías que podían ocurrir. Pero no tenías ni idea de cuándo ocurrirían.

La programación basada en eventos es esto, tienes acceso a algunos eventos que son proporcionados por el lenguaje específico para un trabajo específico. Todo lo que tienes que hacer es crear alguna lógica que pueda resolver los problemas que genera un determinado evento para tener algún tipo de resultado útil.

En MQL5, tenemos algunos eventos que podemos y otros que debemos manejar para cada tipo de situación. Mucha gente se pierde tratando de entender la lógica que hay detrás, pero no es complicado, sino todo lo contrario. Una vez que lo entiendes, la programación se vuelve mucho más sencilla. Esto se debe a que el propio lenguaje te proporciona los medios para hacer frente a cualquier problema.

Este es el primer punto: Utiliza inicialmente el lenguaje MQL5 para resolver los problemas. Si de algún modo no es suficiente, añada funciones específicas. Usa otro lenguaje como C/C++ o incluso Python, para ayudarle, pero primero tratar de utilizar MQL5.

Segundo punto: No debes intentar capturar la información. Vengan de donde vengan. Debes simplemente, y siempre que sea posible, utilizar y responder a los eventos que la plataforma MetaTrader 5 va a generar.

Tercer punto: No utilices funciones ni intentes utilizar eventos que no sean realmente necesarios en el código. Utiliza exactamente lo que necesites, ni más ni menos. Y procura utilizar siempre el evento adecuado para el trabajo adecuado.

Con base en estos 3 puntos, podemos hacer 3 tipos de elecciones para que el EA se comunique con la clase C_Manager, o cualquier otra clase, que necesite recibir los datos proporcionados por la plataforma MetaTrader 5. La primera elección es utilizar el activador de eventos en cada nuevo ticket recibido. Este evento llamará a la función OnTick, sinceramente desaconsejo el uso de esta función. La razón se verá en otra ocasión.

La otra forma es utilizar un evento de reloj, que activa la función OnTime. Pero éste no es muy adecuado para lo que queremos en este momento. Esto se debe a que tendríamos que seguir comprobando, en cada activación de evento de reloj, la lista de órdenes o posiciones. Esto no es eficaz en absoluto, lo que vuelve al EA un peso muerto para la plataforma MetaTrader 5.

La última alternativa es utilizar el evento Trade que activa la función OnTrade. Este evento se activa cada vez que se produce un cambio en el sistema de órdenes. Ya sea por el lanzamiento de nuevas órdenes o por cambios en las posiciones. Pero la función OnTrade no es muy adecuada en algunos casos. Aunque en otros nos ahorrará hacer ciertas tareas y, por lo tanto, simplificará mucho las cosas. En lugar de utilizar la función OnTrade, utilizaremos la función OnTradeTransaction.


¿Qué es OnTradeTransaction y para qué sirve?

Esta es quizás la función de manejo de eventos más complicada que existe dentro del lenguaje MQL5.  Así que utiliza este artículo como una buena fuente de investigación sobre la misma, Intentaré explicar y transmitir en la medida de lo posible lo que he entendido y aprendido sobre cómo utilizar esta función.

Para que sea más fácil de explicar. Al menos en esta fase inicial. Veamos a continuación el código de esta función que está presente en este EA:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
        switch (trans.type)
        {
                case TRADE_TRANSACTION_POSITION:
                        manager.UpdatePosition(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                        if (trans.order == trans.position) (*manager).PendingToPosition();
                        else (*manager).UpdatePosition(trans.position);
                        break;
                case TRADE_TRANSACTION_REQUEST: if ((request.symbol == _Symbol) && (result.retcode == TRADE_RETCODE_DONE) && (request.magic == def_MAGIC_NUMBER)) switch (request.action)
                        {
                                case TRADE_ACTION_DEAL:
                                        (*manager).UpdatePosition(request.order);
                                        break;
                                case TRADE_ACTION_SLTP:
                                        (*manager).UpdatePosition(trans.position);
                                        break;
                                case TRADE_ACTION_REMOVE:
                                        (*manager).EraseTicketPending(request.order);
                                        break;
                        }
                        break;
        }
}

Sé que este código parece bastante extraño a mucha gente, especialmente a aquellos acostumbrados a utilizar otros métodos, para saber qué está pasando con sus órdenes y posiciones. Pero te garantizo que si realmente entiendes cómo funciona esta función OnTradeTransaction, la adoptarás en todos tus EAs, porque es, de hecho, una ayuda oportuna.

Pero, para explicar cómo funciona, evitaré hablar en términos de datos de archivos de registro en la medida de lo posible. Porque, si intentas ver una lógica siguiendo archivos o patrones encontrados en archivos de registro, podrías volverte loco. Esto se debe a que, en ocasiones, los datos no tendrán ningún tipo de patrón. La razón esta función es un manejador de eventos. Estos eventos se originan en el servidor de negociación. Así que olvídate de los archivos de registro. Y nos centraremos en la gestión de los eventos que se envían desde el servidor comercial, independientemente del orden en que aparezcan.

Básicamente, aquí siempre estaremos mirando dentro de 3 estructuras y el contenido de las mismas. El servidor comercial rellena estas estructuras. Tienes que entender que todo lo que se haga aquí será un tratamiento de lo que nos ha informado el servidor. Las constantes de tráding que se están probando aquí son las que realmente necesitamos en el EA. Puede que necesites más constantes dependiendo de lo que estés planeando. Para saber cuáles serán, basta con consultar el siguiente punto de la documentación: Tipos de operaciones. Ahí puedes ver que tenemos 11 enumeraciones diferentes, de nuevo cada una de ellas es para algo específico.

Nótese que en varios puntos estoy utilizando una variable, que estará haciendo referencia a la estructura MqlTradeTransaction. Esta estructura es bastante compleja, y digo completa en términos de lo que ha visto y entendido el servidor. Pero, para nosotros, esto depende de cada tipo de cosa que realmente queramos probar, analizar y conocer. Lo que nos interesa es el campo type de esta estructura, por eso tenemos este sistema de conmutación a partir de aquí. En este código, sólo estamos tratando con 3 tipos de transacciones realizadas por el servidor: TRADE_TRANSACTION_REQUEST, TRADE_TRANSACTION_ORDER_DELETE y TRADE_TRANSACTION_POSITION. Por eso se utilizan aquí.

Ya que es bastante difícil explicar cualquiera de los tipos de transacción sin tener un ejemplo para ello, empecemos por TRADE_TRANSACTION_POSITION, puesto que sólo tiene una línea, como puede verse en el siguiente fragmento:

                case TRADE_TRANSACTION_POSITION:
                        manager.UpdatePosition(trans.position);
                        break;

Este evento ocurrirá siempre cuando algo haya ocurrido en una posición abierta. Pero no cualquier posición, la posición que ha sufrido alguna modificación es informada por el servidor, y se la pasamos a la clase C_Manager, para que si es la posición que está observando el EA esta se actualice. Si no, se ignorará. Esto nos ahorra mucho tiempo intentando buscar qué posición se ha cambiado realmente.

El siguiente en la lista es TRADE_TRANSACTION_ORDER_DELETE. Tiene un código que para muchos puede resultar bastante confuso y sin ninguna lógica. Esto puede verse en el fragmento siguiente:

                case TRADE_TRANSACTION_ORDER_DELETE:
                        if (trans.order == trans.position) (*manager).PendingToPosition();
                        else (*manager).UpdatePosition(trans.position);
                        break;

Cuando una orden se convierte en una posición, esta orden dispara un evento con el cual el servidor informa que la orden ha sido eliminada. Ocurre lo mismo y se activa el mismo evento cuando se cierra una posición. La diferencia entre un evento que informa cuando una orden se ha convertido en una posición y una posición se ha cerrado está en el valor informado por el servidor.

Cuando la orden se convierte en posición, tendrá el mismo valor informado en el ticket de posición, así le indicamos a la clase C_Manager que una orden se ha convertido en posición. Cuando se cierra la posición, estos valores serán diferentes, pero también puede ocurrir que se tenga una posición abierta y se haya agregado una orden, o reducido el volumen abierto, en ambos casos el valor informado en trans.order y trans.position será diferente. En este caso, hacemos una petición de actualización a la clase C_Manager.

En algunos casos, este evento puede ir acompañado de un evento TRADE_TRANSACTION_POSITION. Pero no siempre es así. Para reforzar la explicación, separemos aún más las cosas, porque entender este código es muy importante.

Entendamos primero el caso en que trans.order es igual a trans.postion. Y sí, pueden ser iguales. Así que no imagines que siempre serán diferentes. Cuando son iguales, el servidor activa una enumeración TRADE_TRANSACTION_ORDER_DELETE. Ésta no viene sola, va acompañada de otras enumeraciones. No tenemos que ocuparnos de todas ellas, sólo de ésta en concreto. Pues lo que nos estará diciendo el servidor es que una orden acaba de convertirse en posición. En ese momento se cerrará la orden y se abrirá una posición con el mismo valor de ticket que la orden que se cerró.

Pero puede ocurrir que el servidor no nos envíe una enumeración TRADE_TRANSACTION_POSITION. Aunque en un primer momento te quedes esperando esta enumeración, el servidor simplemente puede no haberla disparado. Pero lo más seguro es que dispare la de eliminación. Los valores indicados serán iguales, estrictamente iguales. En este caso tú sabes que es una orden que estaba en el libro y que se ha convertido en una posición. Pero en el caso de una orden de mercado, las cosas funcionan un poco diferente, veremos este caso más adelante.

Ahora, en caso de que trans.order sea diferente de trans.position, el servidor también disparará otras enumeraciones. Pero, del mismo modo, no te quedes contando con que aquella en concreto llegue. Puede ocurrir que el servidor no la dispare. Pero activará la que estoy usando. En este caso, esto indica que la posición acaba de cerrarse, por la razón que sea que no estoy analizando aquí. De cualquier forma se te estará informando sobre las estructuras recibidas por el evento TradeTransaction. Por eso es tan interesante este manejador de eventos. Porque no tienes que salir a buscar información. Están ahí, sólo hay que ir a la estructura adecuada y leer lo que se informa. Así comprenderás por qué las pruebas se hacen como se hacen.

Normalmente en programas que no utilizan este manejador de eventos, el programador crea un bucle que recorrerá todas las posiciones abiertas u órdenes pendientes. Y buscará saber cuál se ha ejecutado o cerrado. Esto es pura pérdida de tiempo, pues hace que el EA se ocupe de cosas completamente inútiles que se pueden capturar más fácilmente. Esto se debe a que el servidor comercial ya ha hecho todo el trabajo pesado por nosotros, informándonos qué orden pendiente se ha cerrado, qué posición se ha abierto o qué posición se ha cerrado. Y tú ahí, haciendo bucles para conocer esta información.

Ahora llegamos a la parte que, por cierto, será más larga de explicar y, aun así, no abarcaré todos los casos. La razón es la misma que en el caso anterior. No es sencillo explicar cada caso sin tener un ejemplo para ello. Pero aun así, lo que se explicará aquí ya ayudará a mucha gente. Para mayor facilidad, captaremos sólo y únicamente el fragmento que estudiaremos ahora. Esto puede verse justo debajo:

                case TRADE_TRANSACTION_REQUEST: if ((request.symbol == _Symbol) && (result.retcode == TRADE_RETCODE_DONE) && (request.magic == def_MAGIC_NUMBER)) switch (request.action)
                        {
                                case TRADE_ACTION_DEAL:
                                        (*manager).UpdatePosition(request.order);
                                        break;
                                case TRADE_ACTION_SLTP:
                                        (*manager).UpdatePosition(trans.position);
                                        break;
                                case TRADE_ACTION_REMOVE:
                                        (*manager).EraseTicketPending(request.order);
                                        break;
                        }
                        break;

Esta enumeración, TRADE_TRANSACTION_REQUEST, se activa en casi todos los casos. Es bastante raro que no se dispare. Así que muchas de las cosas que podemos hacer en términos de pruebas se pueden hacer dentro de ella. Pero como se trata de una enumeración que el servidor dispara bastante necesitamos filtrar las cosas aún más en ella.

Pero si sirve de consuelo, el servidor suele activar esta enumeración tras algún requerimiento realizado por EA o la plataforma. Es cuando se hace algo relacionado con el sistema de órdenes. Pero no cuentes con esto siempre, ya que a veces el servidor simplemente activa esta enumeración. A veces lo hace sin motivo aparente, por lo que hay que filtrar lo que está siendo informado.

En primer lugar, filtramos el activo, tú puedes utilizar cualquiera de las estructuras para averiguar esto, pero yo prefiero usar esta aquí. A continuación, comprobamos si el pedido ha sido aceptado por el servidor, para ello, utilizamos esta prueba de aquí. Y para terminar, probamos el número mágico para filtrar aún más las cosas. Ahora viene la parte en la que mucha gente acaba enredándose. Esto se debe a que no saben cómo rellenar el resto del código.

Cuando se utiliza un switch para hacer la comprobación del tipo de acción, no estamos y no vamos a analizar la acción realizada en el servidor. Esto no es lo que haremos en realidad. De hecho, vamos a hacer exactamente una comprobación reiterativa de lo que ha sido enviado al servidor ya sea por el EA o la plataforma. Hay 6 tipos de acciones, y son bastante clásicas. Así que vamos a ellas que son una enumeración llamada ENUM_TRADE_REQUEST_ACTIONS. Para hacerlo más fácil, consulta la siguiente tabla, que es la misma que puede verse en la documentación. Pero la he tomado prestada, para ayudar con la explicación, y voy a poner la descripción de una manera ligeramente diferente a la que se encuentra en la documentación.

Tipo de acción Descripción de la acción
TRADE_ACTION_DEAL Coloca una orden de operación para que se ejecute a precio de mercado
TRADE_ACTION_PENDING Coloca una orden en el libro para que se ejecute dentro de los parámetros indicados
TRADE_ACTION_SLTP Modifica los valores de stop loss y take profit de una posición
TRADE_ACTION_MODIFY Modifica los parámetros de una orden pendiente que esté en el libro
TRADE_ACTION_REMOVE Elimina una orden pendiente que aún está en el libro
TRADE_ACTION_CLOSE_BY Cierra una posición

Tabla 01

Si de verdad está acompañado lo que se está programando desde el principio de esta serie de artículos, pero no prestas suficiente atención, la programación a ser realizada tendrá que revisar dónde el campo Tipo de acción, presente en la tabla 01, se utilizó en nuestro código. Y no estoy hablando del manejador de eventos OnTradeTransaction, porque esto no cuenta. Además, estos enumeradores ya se han utilizado. ¡¿Dónde?! En la clase C_Orders.

Abre el código fuente y en la clase C_Orders, mira los siguientes procedimientos: CreateOrder, ToMarket, ModifyPricePointsClosePosition. De esta manera, verás en cada uno de ellos, con la excepción del procedimiento ClosePosition, donde no estoy usando la enumeración TRADE_ACTION_CLOSE_BY.

¿Y por qué es esto importante para nosotros aquí en el manejador de eventos OnTradeTransaction? La razón es que estas enumeraciones serán las mismas que se verán y podrán verse cuando analicemos a qué tipo de acción se refiere la enumeración TRADE_TRANSACTION_REQUEST, por eso en el código del manejador del evento OnTradeTransaction se ven las enumeraciones TRADE_ACTION_DEAL y TRADE_ACTION_SLTP, así como TRADE_ACTION_REMOVE, porque son a las que el EA debe prestar atención.

Pero, ¿y las demás? Para nuestro propósito aquí, que será crear un EA automático, las demás no tienen importancia, sirven para otro tipo de cosas. Si quieres ver una aplicación de estos otros tipos, puedes consultar este artículo: Desarrollo de un EA comercial desde cero (Parte 25): Dotando de robustez al sistema (II), en el que muestro posibilidades de uso de las otras enumeraciones. 

Muy bien, ya que he explicado de dónde vienen esos cases, entendamos qué hace cada uno de ellos, empezando por el que se ve a continuación:

        case TRADE_ACTION_DEAL:
                (*manager).UpdatePosition(request.order);
                break;

Cuando se ejecuta una orden de mercado se llama a este enumerador, pero no sólo en este case, hay un segundo case en el que también se llama. Cuando hablé de la enumeración TRADE_TRANSACTION_ORDER_DELETE, dije que había un case en el que los valores de trans.order y trans.postion podían ser iguales, y este es el segundo case, en el que esta enumeración TRADE_ACTION_DEAL es activada por el servidor comercial. Así que ahora podemos añadir el valor del ticket informado como posición abierta. Pero ten en cuenta que si algo sucede, como que una posición diferente sigue abierta, se producirá un error, y esto hará que EA termine su ejecución, esto no se ve aquí sino en el código UpdatePosition

El siguiente código de la secuencia se ve en el siguiente fragmento:

        case TRADE_ACTION_SLTP:
                (*manager).UpdatePosition(trans.position);
                break;

Este enumerador se activará cuando se produzca el cambio de los valores límite, los conocidos take profit y stop loss, él simplemente actualizará los valores stop y take. Pero, esta versión es muy básica, hay maneras de hacerlo un poco más interesante, pero por ahora es lo suficientemente bueno. Y la última enumeración que tenemos en el código se ve justo en el fragmento de abajo:

        case TRADE_ACTION_REMOVE:
                (*manager).EraseTicketPending(request.order);
                break;

Este fragmento se disparará cuando se elimine una orden, requiriendo que la clase C_Manager sea alertada para que el EA pueda enviar con éxito una orden pendiente. Normalmente, una orden pendiente no se retira del libro. Pero puede ser que el usuario u operador lo haya hecho. Si se retira la orden, accidental o deliberadamente, del libro, y el EA no lo comunica a la clase C_Manager, se le impedirá al EA enviar otra orden pendiente.


Conclusión

En este artículo, te mostré cómo puedes utilizar el sistema de manejo de eventos para poder procesar con más agilidad y de mejor manera las cuestiones relacionadas con el sistema de órdenes, para que el EA sea más rápido. Así, éste no tendrá que estar buscando información todo el tiempo. Es cierto que aquí todavía estamos tratando con un EA que no cuenta con ningún nivel de automatización. Pero pronto añadiremos una automatización, aunque sea básica, en el sistema, para tener un break even y trailing stop gestionado por el EA.

En el archivo adjunto, tendrás el código del EA que se ha visto y explicado en estos 3 últimos artículos. Así que estudia este código detenidamente. Entiende cómo funciona realmente, ya que esto te ayudará mucho en los siguientes pasos.


Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/11248

Archivos adjuntos |
pperea21
pperea21 | 23 abr. 2023 en 21:03

Hola, estoy siguiendo esta serie de artículos. Mi respeto. Es realmente útil.

Estoy probando el resultado y después de crear un pedido lo elimino. Pero EA ya no permite crear ningún pedido nuevo.

Este fragmento de código:

 case TRADE_TRANSACTION_ORDER_DELETE :
                         if (trans.order == trans.position) (*manager).PendingToPosition();
                         else (*manager).UpdatePosition(trans.position); // ORDER?
                         break ;

Utiliza trans.position que, en caso de eliminación de pedidos, siempre es 0. Entonces, ese es el valor que se pasa a UpdatePosition. Su primera condición lo envía directamente a declaración de devolución.

Intenté pasar el boleto trans.order pero el código lo ignora porque m_position tiene cero datos, por lo que el boleto es 0 (! = m_Position.Ticket):

Después de este. Cuando trato de crear un nuevo pedido, m_ticketPending tiene el valor de primer pedido, por lo que no se crea.

EraseTicketPending no se llama porque cuando llega TRADE_ACTION_REMOVE , esta condición

 if ((request.symbol == _Symbol ) && (result.retcode == TRADE_RETCODE_DONE ) && (request.magic == def_MAGIC_NUMBER))

siempre es falso, ya que request.magic es 0.

Investigaré si esto se soluciona en próximos capítulos.

Saludos

pperea21
pperea21 | 23 abr. 2023 en 21:18

Supongo que encontré el problema.

Cuando la eliminación del pedido se realiza desde la interfaz de usuario de MT5, el número mágico es 0. Por lo tanto, he modificado la condición para

 if ((request.symbol == _Symbol ) && (result.retcode == TRADE_RETCODE_DONE ) && ((request.magic == def_MAGIC_NUMBER) || request.magic == 0 ))

y parece funcionar como se esperaba.

Redes neuronales: así de sencillo (Parte 31): Algoritmos evolutivos Redes neuronales: así de sencillo (Parte 31): Algoritmos evolutivos
En el artículo anterior, comenzamos a analizar los métodos de optimización sin gradiente, y también nos familiarizamos con el algoritmo genético. Hoy continuaremos con el tema iniciado, y estudiaremos otra clase de algoritmos evolutivos.
DoEasy. Controles (Parte 23): mejorando los objetos WinForms TabControl y SplitContainer DoEasy. Controles (Parte 23): mejorando los objetos WinForms TabControl y SplitContainer
En este artículo, añadiremos los nuevos eventos de ratón respecto a los límites de los espacios de trabajo WinForms, y también corregiremos algunos errores en los controles TabControl y SplitContainer.
Aprendiendo a diseñar un sistema de trading con Fractals Aprendiendo a diseñar un sistema de trading con Fractals
Bienvenidos a un nuevo artículo de nuestra serie destinada a la creación de sistemas comerciales basados en indicadores técnicos populares. Hoy analizaremos otra herramienta técnica, el indicador Fractals, y desarrollaremos sistemas comerciales basados en este para operar en el terminal MetaTrader 5.
Cómo construir un EA que opere automáticamente (Parte 07): Tipos de cuentas (II) Cómo construir un EA que opere automáticamente (Parte 07): Tipos de cuentas (II)
Aprenda a crear un EA que opere automáticamente de forma sencilla y segura. Uno siempre debe estar al tanto de lo que está haciendo un EA automatizado, y si se descarrila, eliminarlo lo más rápido posible del gráfico, para poner fin a lo que él estaba haciendo y evitar que las cosas se salgan de control.