English Русский 中文 Deutsch 日本語 Português
preview
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)

MetaTrader 5Trading | 30 enero 2023, 17:05
474 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, Cómo construir un EA que opere automáticamente (Parte 06): Tipos de cuentas (I), comenzamos a desarrollar una forma de garantizar que un EA automático funcione correctamente y dentro de lo especificado. Allí se creó una clase C_Manager, para que sirviera como administrador, por lo que en caso de que el EA se comportara de forma extraña o mala, seria eliminado del gráfico e impedido de continuar su ataque de locura.

En ese artículo, comencé explicando cómo puede evitar que el EA active órdenes pendientes o de mercado. Pero aunque el mecanismo que se muestra allí es lo suficientemente capaz de sostener el EA, tenemos algunos otros problemas relacionados con el tema de la interacción entre el EA y la clase C_Orders. Y este problema está principalmente en la cuenta tipo NETTING, que será uno de los temas que exploraremos en este artículo.

Pero de una forma u otra, NUNCA, NUNCA debes permitir que un EA automático funcione sin ningún tipo de supervisión.

No esperes que solo el hecho de que la programación esté bien hecha y de forma muy juiciosa sea suficiente, porque no lo es. Uno siempre debe estar al tanto de lo que está haciendo un EA automatizado, y si se sale de la línea, 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.


Las nuevas rutinas de interacción entre el EA y la clase C_Orders

Todo lo visto en artículos anteriores no tendrá ningún valor si el EA continúa pudiendo acceder a la clase C_Orders como se ve a continuación:

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                C_Orders::CreateOrder(type, Price, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                C_Orders::ToMarket(type, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+

Ten en cuenta que, incluso al tratar de limitar y controlar las cosas, el EA logra enviar órdenes de mercado y lanzar nuevas órdenes en el libro. Esto es una infracción de seguridad, porque si durante la inicialización del EA la clase C_Manager logró imponer que este siguiera ciertas reglas, ¿por qué, después de eso, permitimos que él tenga total libertad para lanzar órdenes en el libro y realizar operaciones de mercado? Tenemos que imponer algún tipo de límite aquí, incluso si este es bastante simple. Una simple prueba a menudo previene muchos tipos de problemas potenciales. Así que agreguemos algo de control en toda esta zona en forma de interacción entre el EA y el sistema de órdenes presente en la clase C_Orders.

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if ((m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if (m_bAccountHedging && (m_Position.Ticket > 0)) return;
                                tmp = C_Orders::ToMarket(type, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                                if (PositionSelectByTicket(m_Position.Ticket)) SetInfoPositions(); else m_Position.Ticket = 0;
                        }
//+------------------------------------------------------------------+

Ten en cuenta que ahora ponemos algunas reglas para que el EA no tenga tanta libertad para usar la clase C_Orders. Entendamos lo que está pasando aquí, comenzando con la función CreateOrderSi tenemos un valor en la variable que informa el ticket de la orden pendiente, se denegará una nueva orden, pero también se denegará si estamos en una cuenta HEDGING con una posición abierta. Así de simple. Pero,si estas condiciones lo permiten, podremos enviar una orden, para tener una orden pendiente en el libro, y si la solicitud es exitosa, tendremos al final el ticket de la orden que estaba colocada, y se nos impedirá enviar un nuevo pedido y una llamada posterior.

Ahora viene el gran detalle, que está en la función ToMarket, en este caso, la solicitud se coloca como orden de mercado, es decir, al mejor precio disponible, para que pueda ser ejecutada. Así que veamos las condiciones que permiten el envío de una orden de mercado. Sólo se permitirá este envío si no tenemos una posición abierta en una cuenta del tipo HEDGING. Ahora el detalle: Si es posible enviar la orden de mercado, esto se hará, pero no podemos utilizar el valor devuelto por la llamada en un primer momento. Esto se debe a que el valor puede ser diferente al valor del ticket de la posición, si estamos en una cuenta NETTING. Si pones el valor directamente en el valor del ticket de la posición, puedes perder el valor real del ticket, porque quizás en este caso el valor devuelto solo será temporal, por eso tenemos esta nueva prueba. Aquí, si estamos en una cuenta HEDGING, el valor devuelto se puede almacenar de forma segura en el ticket de la posición, pero solo se almacenará en el caso de una cuenta NETTING, si el ticket de posición es cero, de lo contrario, el valor será ignorado. Cuando se hace esto, podemos actualizar los datos de la posición o resetear el valor del ticket de la posición, pero todo esto dependerá de la prueba, que se hará aquí en este punto.


Problemas de cuenta NETTING en un EA automático

Existe un problema potencialmente dañino con el uso de un EA automático en una cuenta NETTING. En el artículo anterior, mostré un problema que podría ocurrir en la cuenta HEDGING. Pero ahora exploremos un problema diferente, sin embargo, que solo ocurre en las cuentas NETTING. Si el EA no tiene un bloqueo, en algún momento llegará a explorar esta infracción, que muchos EA tienen y de la que los usuarios no son conscientes.

El problema es el siguiente: En una cuenta de NETTING, el servidor comercial creará automáticamente el precio de posición promedio a medida que cambie el precio de oferta, en una posición de compra o venta. En caso de que esté haciendo un SHORT, de una forma u otra, el precio promedio así como el volumen de la posición cambiarán.

Para un EA, que se operará manualmente, aquí no hay problema, ya que el operador será el responsable de enviar cualquier solicitud al servidor. El problema es cuando automatizamos el EA, en este caso puede dispararse cuando empieza a explorar cierta falla o vulnerabilidad presente en su programación. Detalle: El problema no está en el servidor comercial y tampoco en la plataforma MetaTrader 5, el problema está en el EA.

Trataré de explicar de manera que todos puedan entender, pero lo ideal es que tengas algún conocimiento de cómo funciona el mercado, para que se facilite la comprensión. Pero intentaré dejar la pregunta lo más clara posible.

Cuando un EA automático envía una orden de compra o venta para abrir una posición, lo hace en función de algún tipo de gatillo. Aunque todavía no he hablado de los gatillos, pronto llegaremos allí, pero el problema no este, en algunos casos incluso puede serlo, pero en la gran mayoría el gatillo no es responsable.

Una vez abierta la posición, en una cuenta de tipo NETTING, el EA puede por una u otra razón comenzar a utilizar la siguiente estrategia: cierra una parte del volumen y abre otra. De esta manera, el precio promedio comenzará a moverse, ahora el problema puede ser que el EA comience a hacer esto de una manera salvaje, loca y alucinante. Si esto sucede, puedes, en muchos casos, perder grandes sumas de dinero en cuestión de minutos, a veces segundos, según el volumen negociado.

Y este es el problema, el EA lanza una orden y luego se queda allí sin cerrar la posición, enviando nuevas solicitudes al servidor para cambiar el precio promedio. Esto muchas veces ocurre sin que el operador, que está o debería estar supervisando el EA, pueda realmente percibirlo, ya que el volumen mostrado no cambia, pero el precio sí, y algunos operadores no notan este cambio en el precio. Muchas veces puede suceder que la posición ya esté dando una gran pérdida, sin que el operador se dé cuenta de que hay algo mal allí.

Aunque algunos EA automáticos están programados para usar este tipo de estrategia intencionalmente, algunas personas tienen este tipo de falla en su EA automático y no se dan cuenta o no lo saben. Ya que en algunos casos, y aquí viene el tema del gatillo, la persona puede estar usando un tipo de gatillo, lo que hace que el EA compre o venda, y en alguna situación de mercado muy específica el EA puede hacer exactamente lo que se describió anteriormente. Pero, si la programación ya lo prevé, el operador estará atento. Pero si este comportamiento no estaba previsto, el operador que estará supervisando el EA puede terminar asustado al ver lo que estará haciendo el EA.

Por eso, no subestimes el sistema, por mucho que confíes y creas que tu EA automático es seguro, no lo subestimes. Porque puede ocurrir que encuentre alguna falla, que no habías previsto en el momento de su programación. Pero por suerte hay una forma relativamente sencilla de superar este defecto, al menos minimizando un poco su daño. Así que veamos cómo hacer esto, al menos en términos de programación.


Limitación del volumen de operaciones en el EA

La forma más sencilla de reducir, al menos un poco, el problema descrito en el tema anterior es limitar el volumen con el que realmente puede trabajar el EA. No existe una forma 100% ideal, pero podemos intentar, en la medida de lo posible, generar alguna forma que nos brinde algo de comodidad. Y la forma más sencilla será limitar el volumen con el que puede trabajar el EA durante todo el tiempo que esté funcionando.

Presta atención a esto, no estoy diciendo que limites el volumen en una posición abierta, estoy diciendo que limites el volumen que con que él negociará durante todo el período. Es decir, puede negociar x veces el volumen mínimo, pero una vez que haya alcanzado esa cantidad, el volumen mínimo que podría negociar, no podrá abrir una nueva posición. No importa si tiene una posición equivalente a 1 volumen mínimo y que podría operar 50 veces este volumen, si ya alcanzó esta cuota de 50, no podrá abrir más posiciones ni aumentar la posición ya abierta .

El bloqueo se basa precisamente en esto. Pero tenemos un detalle aquí: No existe tal cosa como un bloqueo 100% seguro. Todo dependerá del operador que esté supervisando el EA. Si el operador, sin darse cuenta, desactiva el bloqueo, reiniciando el EA, será y debe ser responsable de cualquier falla que pueda ocurrir. Como puede haber sucedido que el EA malgastó la porción de capital, al ser eliminado del gráfico y ser colocado nuevamente en el gráfico por el operador. En este caso, no hay bloqueo que pueda mantenernos seguros ante tal comportamiento.

Para comenzar a hacer esto, necesitaremos una variable que sea estática, global y privada, dentro de la clase C_Manager, esto se puede ver en el siguiente fragmento:

static uint m_StaticLeverage;

Pero para inicializar una variable estática dentro de la clase, no podemos usar el constructor de la clase, la inicialización en este caso estará fuera del cuerpo de la clase, pero normalmente dentro del archivo fuente, en el archivo de encabezado C_Manager.mqh, luego fuera del cuerpo de la clase verás el siguiente código:

uint C_Manager::m_StaticLeverage = 0;

Cuando ves este tipo de código en un archivo, no necesitas asustarte, es solo una variable estática que se está inicializando. Para aquellos que no lo saben, esta inicialización tiene lugar incluso antes de que se haga referencia al constructor de la clase. En algunos tipos de código esto es bastante útil. Pero el hecho de que la variable se inicialice fuera del cuerpo de la clase no indica que se pueda acceder a ella fuera de la clase. Recuerda: El motivo es la encapsulación, todas las variables deben declararse como privadas. Esto es para que su clase sea lo más robusta posible, lo que evita infringir la seguridad o perder confiabilidad en el trabajo de la clase.

Pero aquí tenemos otra pregunta igualmente importante: ¿Por qué usamos una variable global privada y estática dentro de la clase C_Manager? ¿No podríamos usar una variable que al menos no fuera estática? La respuesta es NO, y la razón es que si por alguna circunstancia la plataforma MetaTrader 5 reinicia el EA, se perderán todos los datos almacenados fuera de una variable estática. Atención a esto, me refiero al hecho de que la plataforma MetaTrader 5 reinicia el EA y no al hecho de que quitas el EA y luego lo vuelves a colocar en el gráfico. Estas son situaciones diferentes. En caso de que el EA sea eliminado del gráfico y luego sea recolocado, se perderá cualquier información, incluso en variables estáticas. En estos casos, la única forma sería almacenar los datos en un archivo y luego recuperarlos leyendo el mismo archivo. Reiniciar en realidad no significa que se eliminó el EA, puede ocurrir en varias situaciones y todas implicarán la activación de un evento DeInit que llamará a la función OnDeInit dentro del código. Este restablecimiento no solo afecta al EA, también ocurre con los indicadores, por lo que los Scripts colocados en el gráfico pueden eliminarse en caso de que se active un evento DeInit. Dado que los scripts no se restablecen, no tienen esta propiedad, simplemente salen del gráfico y MetaTrader 5 no los restablece automáticamente..

Ahora necesitamos agregar una definición en el código de la clase, esto para identificar la cantidad máxima de veces que el volumen mínimo puede ser utilizado u operado por el EA, esta definición se ve a continuación:

#define def_MAX_LEVERAGE       10

Dado que ya tenemos dónde almacenar la cantidad mínima de volumen que ha negociado el EA y tenemos una definición de volumen máximo que se puede utilizar, podemos preparar una forma de contabilizar este volumen. Esta es la parte más interesante de hacer. Pero antes de eso, necesitamos hacer algunos cambios en el código que se vio en el artículo anterior, como se muestra a continuación:

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                tmp = C_Orders::ToMarket(type, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                                if (PositionSelectByTicket(m_Position.Ticket)) SetInfoPositions(); else m_Position.Ticket = 0;
                        }
//+------------------------------------------------------------------+

Al agregar estas pruebas, estoy diciendo que si el volumen contabilizado es mayor que el volumen máximo indicado, el EA no podrá realizar la operación, aquí todavía hay algunos detalles por resolver en el futuro, pero por ahora se ha de hacer así. También ten en cuenta que hay una línea que se tachó del código, porque perturbará el trabajo de contabilidad.

Bien, ahora necesitamos algunas rutinas que nos ayuden a comunicarnos con el EA, para que esta clase C_Manager pueda administrar lo que hará el EA. Para ello, comenzaremos creando una rutina muy discreta, pero sumamente necesaria, esto se puede apreciar en el siguiente código:

inline void UpdatePosition(const bool bSwap = false)
                        {
                                int ret;
                                
                                if ((m_bAccountHedging) && (m_Position.Ticket > 0) && (bSwap)) SetUserError(ERR_Unknown);
                                m_Position.Ticket = ((m_Position.Ticket == 0) && (bSwap) ? m_TicketPending : m_Position.Ticket);
                                m_TicketPending = (bSwap ? 0 : m_TicketPending);
                                if (PositionSelectByTicket(m_Position.Ticket))
                                {
                                        ret = SetInfoPositions();
                                        m_StaticLeverage += (ret > 0 ? ret : 0);
                                }
                        }

No subestimes el código anterior, aunque parece ser un código de bajo valor en términos de complejidad, es extremadamente necesario para que la clase C_Manager pueda administrar lo que está haciendo el EA. Para comprender profundamente este código, es necesario comprender cómo y cuándo el EA lo llamará, pero esto se verá más adelante, ya que el procedimiento dentro del EA es bastante diferente de lo que mucha gente hace y sigue haciendo.

Con solo mirar el código de arriba, podemos notar algunas cosas. Si estamos en una cuenta HEDGING con una posición abierta y bSwap es verdadero, esto es un error. Informamos de esto, ya que el error se procesará en otro lugar. Si la variable del ticket de la posición está vacía y bSwap es verdadero, esto significa que una orden pendiente se ha convertido en una posición. Pero si ya teníamos una posición abierta, la orden pendiente (en una cuenta NETTING) habría modificado el volumen de la posición y posiblemente el precio de entrada. Este tipo de situaciones se analizan en esta línea aquí.

Si bSwap se ha establecido como verdadero, significa que la orden pendiente ha dejado de existir, esto se ajusta aquí. Ahora hacemos la siguiente prueba: Verificamos si la posición está abierta, si es así, ejecutamos el procedimiento que actualiza sus datos, durante este procedimiento de actualización se calcula la diferencia entre el volumen antes y después de la actualización, y este volumen nos es devuelto.. Si esta información que se devolvió es positiva, esto indica que el volumen o factor de apalancamiento ha aumentado. Este tipo de cosas es muy común en las cuentas NETTING, pero en las cuentas HEDGING, el valor devuelto siempre será el valor de apalancamiento actual, y le sumaremos este valor devuelto al valor que estaba previamente en la variable estática. De esta manera, tenemos una forma de contabilizar el volumen que el EA utilizará, o ya habrá utilizado, durante el tiempo que se ejecuta.

Pero sin pensarlo demasiado, pronto te das cuenta de que necesitamos otra rutina. Servirá para quitar o cerrar una posición, y efectivamente la necesitamos, y esta rutina se puede ver a continuación:

inline void RemovePosition(void)
                        {
                                if (m_Position.Ticket == 0) return;
                                if (PositionSelectByTicket(m_Position.Ticket)) ClosePosition(m_Position.Ticket);
                                ZeroMemory(m_Position);
                        }

Aquí no hay error, es un tiro en el blanco, el EA estará cerrando la posición abierta o simplemente informando a la clase C_Manager que el servidor ha cerrado la posición, ya sea porque se han alcanzado los límites (take profit o stop loss) o porque algo provocó el cierre de la posición, no importa. Pero si el EA llama por error a esta rutina, sin que haya una posición abierta, simplemente volveremos. Si la posición está abierta, se cerrará y, al final, borraremos todos los datos presentes en la región de memoria donde se encuentran los datos de la posición.

Aunque estas dos rutinas anteriores funcionan bien, en muchas situaciones diferentes se adaptan muy poco a lo que realmente necesitamos hacer. Así que no te emociones mucho cuando veas estas rutinas, e imagina que son aptas para un EA automatizado, con un gran nivel de robustez, porque, como te acabo de decir, funcionan, eso es un hecho, pero no son adecuadas, necesitaremos otras rutinas mejores para lo que quiero mostrar cómo construir. Sin embargo, esto no se verá aquí en este artículo, y la razón es que necesitaré mostrar por qué usar otro tipo de rutina, pero para una comprensión adecuada es necesario mostrar el código de EA, y esto se verá en el próximo artículo.

Con base en todo este trabajo, por fin tenemos un sistema, en teoría, robusto y bastante fiable, hasta el punto de que por fin podemos empezar a automatizarlo, para que pueda funcionar solo siendo supervisado por un operador humano. Pero tenga en cuenta que se dijo EN TEORÍA, porque aún pueden ocurrir fallas en el sistema. Pero estos problemas ahora se probarán con la variable _LastError, por lo que podemos verificar si algo salió mal. Y si la falla es grave, como las que usan la enumeración de errores personalizada vista en el artículo anterior, debemos eliminar el EA del gráfico.

Eliminar el EA del gráfico no es una de las tareas más complicadas de realizar. De hecho, es bastante simple de hacer, basta con llamar a la función ExpertRemove. Pero esta tarea está lejos de ser nuestro mayor y real problema. El verdadero problema es qué hacer con la posición abierta y la orden pendiente, que probablemente aún existan, este es el problema.

En el peor de los casos, tú simplemente las cierra o las elimina manualmente. Pero piensa en el hecho de que puede tener 2 o más EA ejecutándose automáticamente en una cuenta de tipo HEDGING en el mismo activo, y uno de estos EA simplemente decide descarrilarse y es por eso que será eliminado del gráfico. Ahora debes observar las órdenes y posiciones que se encuentran en la caja de herramientas en la pestaña de operaciones (Figura 01), e intenta cerrar las operaciones que abrió o realizó el EA malcriado. En este caso, "malcriado" no significa que el EA no estuviera bien programado, pero creo que se entendió la idea.

Figura 01

Figura 01 - Dónde encontrar órdenes y posiciones en la plataforma MetaTrader 5

Aquí la programación nos puede ayudar un poco, pero no esperes milagros, solo nos ayuda dentro de sus posibilidades. Así que veamos cómo podemos usar la programación para ayudarnos en este caso específico, en el que un EA malcriado decidió salir, cometer sus locuras y, con eso, terminó siendo expulsado del gráfico por sus malos hábitos y mala conducta.


Uso del Destructor

El destructor es una función de la clase, que en realidad no es como el constructor llamado por el código en ningún momento, de hecho, un constructor, como su compañero el destructor, se llama solo una vez durante toda la vida de la clase. El constructor es llamado cuando nace la clase y el destructor, cuando muere. En ambas situaciones, tú, como programador, difícilmente podrás saber cuándo nacerá una clase y cuándo morirá. Normalmente quien hace esto es el compilador, pero en casos específicos, los programadores podemos saber cuándo nacerá una clase y cuándo morirá, esto usando algunas cosas en el código. Pero la llamada para darle vida al mismo puede incluso ocurrir gracias a los parámetros que usamos en los constructores, pero no se puede decir que ocurra lo mismo cuando muera.

Pero no te preocupes por eso por ahora, cuando vayamos a ver el código de EA lo entenderás mejor.

Independientemente de cuándo muera la clase, podemos decirle qué hacer cuando eso suceda. De esta forma, podemos decirle a la clase C_Manager que cuando quiera expulsar al EA del gráfico lo haga y elimine todo lo que haya hecho o dejado como rastro, en otras palabras, podemos cerrar la posición abierta y eliminar la orden pendiente que está en el libro. Pero recuerda confirmar esto mirando la caja de herramientas, figura 01, ya que podría ser que el EA fuera expulsado del gráfico, pero el destructor no pudiera llevar a cabo su tarea.

Para hacer esto, agreguemos un nuevo valor a la enumeración de errores, esto se puede ver a continuación:

class C_Manager : private C_Orders
{
        enum eErrUser {ERR_Unknown, ERR_Excommunicate};
        private :

Este valor le indicará al destructor que el EA está siendo baneado, excomulgado, expulsado de puntillas y apedreado de la tabla, y todo lo que esté bajo su responsabilidad debe ser deshecho. Para entender cómo el destructor puede recibir este valor, si en realidad no puede recibir ningún valor a través de un parámetro, veamos su código a continuación:

                ~C_Manager()
                {
                        if (_LastError == (ERR_USER_ERROR_FIRST + ERR_Excommunicate))
                        {
                                if (m_TicketPending > 0) RemoveOrderPendent(m_TicketPending);
                                if (m_Position.Ticket > 0) ClosePosition(m_Position.Ticket);
                                Print("EA was kicked off the chart for making a serious mistake.");
                        }
                }

Vean que el código es muy simple, y estaremos probando el valor de _LastError para verificar qué pudo haber sucedido. Entonces,si el valor del error es el recién agregado a la enumeración, significa que el EA está siendo expulsado del gráfico por su mal comportamiento. En este caso,si hay alguna orden pendiente, se enviará una solicitud al servidor para que se elimine la orden, así comosi hay una posición abierta, se enviará una solicitud al servidor para que se cierre la posición, y al final le informamos en el terminal lo sucedido.

Pero recuerde, esto no es seguro en absoluto, solo estamos tratando de obtener algún tipo de ayuda con la programación. Tú, como operador, debe prestar atención y eliminar cualquier orden o posición que el EA haya dejado atrás cuando fue expulsado del gráfico.  Con esto damos por concluido este tema que fue muy corto, pero imagino que ha podido pasar el mensaje. Y, antes de terminar este artículo, veamos una cosa más, pero esto se verá en el próximo tema.


Creación de un nivel de tolerancia al error

Si miraste todo el código de este artículo y del anterior, y lograste entender lo que te explico, te estarás imaginando que la clase C_Manager es muy dura con el EA, no admitiendo ningún tipo de falla, ni la más pequeña. Sí, efectivamente esto está pasando, pero podemos cambiarlo un poco, no de forma que el EA pueda cometer errores o descuidos, pero hay algunos tipos de errores y fallos que no son tan graves, y otros que ni son culpa del EA.

Uno de estos errores es cuando el servidor reporta el error TRADE_RETCODE_MARKET_CLOSED, esto se debe a que el mercado está cerrado. Este tipo de error se puede tolerar ya que no es culpa del EA. Otro es TRADE_RETCODE_CLIENT_DISABLES_AT, que se genera porque AlgoTrading está deshabilitado en la plataforma.

Hay varios tipos de errores que pueden ocurrir y no son generados por el EA, es decir, pueden generarse por varias razones, por lo que simplemente ser muy duro con el EA, culparlo por todo lo que puede estar saliendo mal, no es realmente una actitud justa. Por esto, necesitamos crear algún medio de controlar y tolerar algunos tipos de errores. Si no nos parece grave, podemos hacer que el EA sea ignorado y se quede en el gráfico, pero si efectivamente es un error grave, podemos tomar algún tipo de decisión en función del nivel de gravedad del error.

Así, dentro de la clase C_Manager, creamos una función pública, para que el EA pueda llamarla cuando tenga dudas sobre la gravedad de un posible error que se haya podido producir. Así que veamos de qué se trata esta función, su código se puede ver a continuación:

                void CheckToleranceLevel(void)
                        {
                                switch (_LastError)
                                {
                                        case ERR_SUCCESSreturn;
                                        case ERR_USER_ERROR_FIRST + ERR_Unknown:
                                                Print("A serious error has occurred in the EA system. This one cannot continue on the chart.");
                                                SetUserError(ERR_Excommunicate);
                                                ExpertRemove();
                                                break;
                                        default:
                                                Print("A low severity error has occurred in the EA system. Your code is:", _LastError);
                                                ResetLastError();
                                }
                        }

Aquí no estoy dando una solución definitiva y tampoco es la 100% adecuada. Solo estoy demostrando cómo debes proceder para crear una manera de que haya tolerancia a errores en el EA. En el código anterior, muestro 2 ejemplos en los que la tolerancia será diferente.

Existe la posibilidad de un error más grave, en cuyo caso el EA tendrá que ser expulsado del gráfico. La forma correcta de hacerlo es usando estas dos funciones, una para configurar el error de expulsión y otra que manda a quitar el EA. De esta forma, el destructor de la clase C_Manager intentará borrar o deshacer lo que estaba haciendo el EA. Y hay una segunda forma en la que el error es más ligero. En este caso, tú simplemente emites un mensaje de alerta al operador, y eliminas la indicación de error, para dejar el valor limpio, de esta manerasi la llamada se produce sin error, la función simplemente volver.

Lo ideal es que aquí promuevas el tratamiento de los errores, definiendo uno a uno cuáles se pueden tolerar y cuáles no. Otra cosa es que a pesar de no haber agregado esta llamada dentro de la clase C_Manager (es decir, la tolerancia a errores será muy alta), realmente no deberías hacer esto. Puedes agregar una llamada a la rutina anterior, en ciertos momentos, esto para evitar olvidar agregar las llamadas en el EA. Pero en un principio, esta llamada se realizará dentro del código EA en puntos muy concretos.


Conclusión

Este artículo complementa el artículo anterior, por lo que el contenido no es demasiado agotador de leer y comprender. Creo que fue suficiente para llevar a cabo esta tarea.

Sé que a muchos ya les gustaría ver el código del EA para usar lo que se vio aquí, pero las cosas no son tan simples cuando se trata de un EA automático. Entonces, en el próximo artículo, intentaré transmitir parte de mi experiencia sobre el tema, en él abordaremos las precauciones, los problemas y los riesgos involucrados en la codificación de EA. El enfoque allí será únicamente el código de EA, ya que con base en estos dos últimos artículos tendré mucho que mostrar en el próximo de esta serie. Así que estudien con calma y traten de entender cómo funciona este sistema de clases, porque la cosa será muy densa.


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

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.
DoEasy. Elementos de control (Parte 22): SplitContainer. Cambiando las propiedades del objeto creado DoEasy. Elementos de control (Parte 22): SplitContainer. Cambiando las propiedades del objeto creado
En este artículo, implementaremos la capacidad de cambiar las propiedades y el aspecto del control SplitContainer después de haberlo creado.
Cómo construir un EA que opere automáticamente (Parte 08): OnTradeTransaction Cómo construir un EA que opere automáticamente (Parte 08): OnTradeTransaction
En este artículo, te mostraré 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.
Algoritmos de optimización de la población: Enjambre de partículas (PSO) Algoritmos de optimización de la población: Enjambre de partículas (PSO)
En este artículo, analizaremos el popular algoritmo de optimización de la población «Enjambre de partículas» (PSO — particle swarm optimisation). Con anterioridad, ya discutimos características tan importantes de los algoritmos de optimización como la convergencia, la tasa de convergencia, la estabilidad, la escalabilidad, y también desarrollamos un banco de pruebas y analizamos el algoritmo RNG más simple.