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

Cómo construir un EA que opere automáticamente (Parte 02): Inicio de la codificación

MetaTrader 5Trading | 9 enero 2023, 16:44
1 905 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, Cómo construir un EA que opere automáticamente (Parte 01): Conceptos y estructuras, he presentado los primeros pasos que usted necesita entender, antes incluso de empezar a construir un EA que opera de forma automática. Allí mostré cuáles son los conceptos que usted debe tener en cuenta y cuál debe ser la estructura que debe crear.

En el artículo anterior no hablamos de la codificación. Me interesaba intentar nivelar los conocimientos de la gente, sobre todo de los principiantes o aficionados, pero que no tienen conocimientos prácticos de programación. De esta forma, pretendía acercar a estas personas al mundo de MQL5, mostrando cómo crear un EA capaz de operar de forma totalmente automatizada. Si no ha leído el artículo anterior, le sugiero que lo haga, porque es muy importante que entienda el contexto en el que va a trabajar cuando cree un EA automatizado.

Bueno, empecemos a codificar. Partamos del sistema base, que es el sistema de órdenes. Este será el punto de partida para cualquier EA que decida crear.


Planificación

Es cierto que la biblioteca estándar de MetaTrader 5 proporciona una forma cómoda y adecuada de trabajar con un sistema de órdenes. Sin embargo, en este artículo quiero ir más allá y mostrar lo que se esconde detrás de las cortinas de la biblioteca TRADE de MetaTrader 5. Mi objetivo no es desalentar el uso de la biblioteca, sino arrojar luz sobre lo que sucede detrás de ella. Para ello vamos a desarrollar nuestra propia biblioteca de envío de órdenes. No tendrá todas las características presentes en la biblioteca estándar de MetaTrader 5, sino sólo las básicas para crear y mantener un sistema de órdenes funcional, robusto y fiable.

Dadas estas palabras, tendremos que utilizar una construcción específica. Pido disculpas a los que se inician en la programación, pero cuesta esfuerzo seguir la explicación. Haré todo lo posible por hacerlo lo más sencillo posible, para que puedan seguir y entender las ideas y los conceptos. Sin embargo, no puedo negar el hecho de que sin esfuerzo sólo verá la cosa se construye y no tendrá éxito en MQL5 y siempre se quedará fuera. Así que empecemos.


Creación de la clase C_Orders

Para crear la clase, primero tenemos que crear un archivo que contendrá el código para nuestra clase. Para ello, en la ventana del navegador MetaEditor, vaya a la carpeta "Include" y haga clic con el botón derecho del ratón en dicha carpeta. Seleccione "Nuevo archivo" y siga las instrucciones de la imagen siguiente:

Figura 1

Figura 01 - Añadimos un archivo de inclusión

Figura 02

Figura 02 - Así se creará el archivo que necesitamos


Para hacer lo que se muestra en la figura 01 y 02, se creará un archivo que se abrirá con el MetaEditor. Su contenido puede verse a continuación. Antes de continuar, me gustaría explicar algo rápidamente. Observe la figura 01 y vea que es posible crear directamente una clase. Se preguntará por qué no lo hacemos. De hecho, es una pregunta válida. La razón es que, si creamos la clase utilizando el punto mostrado en la figura 01, no se crea completamente desde cero. Ya vendrá con alguna información o formato predefinido, pero queremos que la clase se cree desde cero. Ahora, volvamos al código.

#property copyright "Daniel Jose"
#property link      ""
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Todas las líneas en gris claro son comentarios (como ejemplo de código), que pueden borrarse si se desea. Sin embargo, lo que nos interesa empieza a partir de aquí. Una vez abierto este archivo, empezaremos a añadirle código. Este código nos ayudará a trabajar con nuestro sistema de órdenes.

Para hacer nuestro código seguro, fiable y robusto, vamos a utilizar una herramienta que MQL5 trajo de C++: las clases. Si no sabe lo que son las clases, le recomiendo que eche un vistazo al tema. No tiene que ir directamente a la documentación de C++ sobre clases; de hecho, puede empezar leyendo sobre clases en la documentación de MQL5, que le dará un buen punto de partida. Además, el contenido de la documentación de MQL5 será más fácil de entender que toda la confusión que C++ puede causar a las personas que nunca han oído hablar de las clases.

En general, una clase es sin duda la forma más segura y eficaz de aislar el código de otras partes del mismo. Esto hace que la clase no sea vista como código, sino como un tipo de datos especial en el que es posible hacer mucho más que utilizar tipos primitivos, como integer, double, boolean, entre otros. En otras palabras, para el programa, la clase es una herramienta multifuncional. No necesita saber cómo se creó la clase ni qué contiene. Sólo necesita saber cómo usarla. Piense en una clase como en una herramienta eléctrica. Usted no necesita saber cómo se construyó o qué componentes tiene. Todo lo que necesita saber es cómo enchufarla y utilizarla. Su funcionamiento no influirá en su uso. Esa sería una definición simple de una clase.

Bueno, sigamos adelante. Lo primero que hacemos es generar las siguientes líneas:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
class C_Orders
{
        private :
        public  :
};
//+------------------------------------------------------------------+

Estas líneas son la parte más básica de nuestra clase. Normalmente, utilizamos el mismo nombre de la clase que el nombre del archivo en el que se encuentra, pero esto no es obligatorio. Sin embargo, puede ayudar a encontrar la clase más tarde. Dentro de la clase, señalo dos palabras reservadas, que indican el nivel de intercambio de la información presente entre ellas. Si usted no añade estas palabras, todos los elementos de la clase serán tratados como públicos, lo que significa que cualquiera puede leer, escribir o acceder a ellos. Esto socava algunas de las principales razones por las que utilizamos clases, como la seguridad y la robustez, ya que cualquier fragmento de código puede acceder a lo que hay dentro de la clase y modificarlo.

En resumen: todo lo que está entre la declaración de la palabra private y la palabra public puede ser accedido sólo y únicamente dentro de la clase. Aquí usted puede utilizar variables globales y no se puede acceder a ellas fuera del código de la clase. Se puede acceder a cualquier cosa declarada después de la palabra public en cualquier parte del código, independientemente de si forma parte de la clase o no. Cualquiera puede acceder a lo que haya allí.

Después de crear este archivo, como se muestra arriba, podemos añadirlo a nuestro EA. Entonces, nuestro EA tendrá este aspecto:

#property copyright "Daniel Jose"
#property version   "1.00"
#property link      "https://www.mql5.com/pt/articles/11223"
//+------------------------------------------------------------------+
#include <Generic Auto Trader\C_Orders.mqh>
//+------------------------------------------------------------------+
int OnInit()
{
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+

En este punto incluimos nuestra clase en el EA usando la directiva de compilación Include. Cuando utilizamos esta directiva, el compilador entiende que, a partir de ese momento, el archivo de cabecera C_Orders.mqh presente en el directorio include de la carpeta Generic Auto Trader debe ser incluido en el sistema y compilado. Hay algunos trucos para esto, pero no voy a entrar en detalles aquí, porque dejaría a las personas menos experimentadas completamente perdidas tratando de entender estos detalles. Pero lo que ocurre es exactamente lo que he descrito.


Definición de las primeras funciones de la clase C_Orders

Contrariamente a lo que mucha gente piensa, un programador no empieza tecleando códigos sin parar. Al contrario, piensa y estudia lo que hay que hacer antes de empezar nada.

Por lo tanto, usted que está empezando también debería hacer lo mismo. Pensar antes de intentar añadir cualquier línea de código. Pensemos por un momento: ¿qué es lo que realmente necesitamos tener en la clase C_Orders para tener el menor trabajo posible y aprovechar al máximo lo que la plataforma MetaTrader 5 nos puede ofrecer?

Lo único que se me ocurre es una forma de enviar órdenes. No necesitamos medios para mover, eliminar o cerrar posiciones, ya que la plataforma MetaTrader 5 nos proporciona estas capacidades. Cuando colocamos una orden en el gráfico, ésta aparece en la pestaña 'Trading' de la caja de herramientas, por lo que no tenemos que preocuparnos demasiado.

Otra cosa es que, al menos al principio, no necesitemos un mecanismo para mostrar órdenes o posiciones en el gráfico, ya que la propia plataforma ya lo hace por nosotros. Por lo tanto, lo que realmente necesitamos es una manera de enviar órdenes directamente desde el EA, sin tener que pasar por el sistema de tickets de la plataforma MetaTrader 5.

Partiendo de ese pensamiento, podemos empezar a programar lo que realmente vamos a necesitar en este primer momento, es decir, una función o procedimiento que permita al EA enviar órdenes. Es en este punto donde muchas personas, especialmente las que tienen pocos conocimientos o experiencia en programación, empiezan a cometer errores.

Como ya hemos definido que vamos a utilizar MetaTrader 5 como gestor de órdenes y posiciones, utilizando la caja de herramientas de la plataforma, tenemos que empezar a desarrollar el sistema de envío de órdenes al servidor, ya que es la parte más básica de todas. Sin embargo, para ello, necesitamos saber algunas cosas sobre el activo que el EA está vigilando, para evitar estar constantemente buscando esta información a través de llamadas a procedimientos de la librería estándar MQL5. Por lo tanto, vamos a empezar por definir algunas variables globales dentro de nuestra clase.

class C_Orders
{
        private :
//+------------------------------------------------------------------+
                MqlTradeRequest m_TradeRequest;
                struct st00
                {
                        int     nDigits;
                        double  VolMinimal,
                                VolStep,
                                PointPerTick,
                                ValuePerPoint,
                                AdjustToTrade;
                        bool    PlotLast;
                }m_Infos;

Estas variables almacenarán los datos que necesitamos y serán visibles en toda la clase. Sin embargo, es importante no intentar acceder a ellas fuera de la clase, ya que se declaran como privadas después de la cláusula private. Esto significa que sólo se puede acceder a ellas o verlas dentro de la clase.

Ahora, necesitamos inicializar estas variables de alguna manera. Hay varias formas de hacerlo, pero a veces podemos olvidarnos de inicializarlas. Por lo tanto, podemos utilizar un constructor de clase para asegurarnos de que estas variables siempre se inicializarán. Véase el ejemplo siguiente:

                C_Orders()
                        {
                                m_Infos.nDigits         = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
                                m_Infos.VolMinimal      = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
                                m_Infos.VolStep         = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
                                m_Infos.PointPerTick    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
                                m_Infos.ValuePerPoint   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
                                m_Infos.AdjustToTrade   = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
                                m_Infos.PlotLast        = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);
                        };

Los constructores de clase son una forma segura de inicializar una clase antes de trabajar con ella. En este ejemplo, estamos utilizando un constructor por defecto, es decir, no recibe ningún parámetro. En otros casos, el constructor puede tomar parámetros para inicializar algo dentro de la clase.

Observe que cada una de las variables se inicializa de una manera específica, lo que garantiza que siempre estarán inicializadas cuando se acceda a ellas para realizar pruebas o cálculos específicos. Por eso es importante preferir el uso de constructores de clase para inicializar las variables globales e internas de la clase antes que cualquier otra acción en el código.

Como estamos utilizando un constructor, también necesitamos declarar un destructor para la clase. Puede ser tan sencillo como se muestra a continuación:

                ~C_Orders() { }

Observe la sintaxis. El nombre del constructor y del destructor es el mismo que el nombre de la clase. Sin embargo, la declaración del destructor va precedida de una virgulilla (~). Además, es importante recordar que tanto el constructor como el destructor no devuelven ningún tipo de valor. Intentarlo se considera un error e impedirá la compilación del código.

Si necesita devolver algún valor durante la inicialización o finalización de la clase, debe utilizar una llamada de procedimiento convencional. Para ello no se pueden utilizar constructores ni destructores.

Ahora bien, ¿en qué puede ayudar realmente esta codificación mediante constructores y destructores? Como se mencionó anteriormente, es común tratar de acceder o utilizar una variable global de la clase sin que haya sido inicializada. Esto puede dar lugar a resultados extraños y errores en el programa, incluso para programadores experimentados. Al utilizar constructores para inicializar las variables globales de la clase, nos aseguramos de que siempre estarán disponibles cuando las necesitemos.

Por lo tanto, no adquiera el mal hábito de programar con programación orientada a objetos sin hacer uso de constructores y destructores. Esto puede causar muchos problemas y llevar horas intentando comprender por qué el programa no funciona correctamente.

Antes de continuar, me gustaría llamar su atención sobre un detalle presente en el constructor de la clase, destacado a continuación:

        m_Infos.PlotLast = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);

Contrariamente a lo que mucha gente piensa, existen diferencias entre los mercados, pero no del tipo que usted puede estar acostumbrado a imaginar. La diferencia radica en cómo se grafica el activo. La línea anterior determina el tipo de gráfico de barras que utiliza el activo.

Pero, ¿por qué es importante? Si desea crear un EA que funcione en diferentes mercados o activos, debe saber que pueden tener diferentes sistemas de representación. Básicamente, hay dos tipos principales:

  • Representación gráfica BID, que puede verse en el mercado de DIVISAS;
  • Representación gráfica LAST, que suele verse en el mercado de ACCIONES.

Aunque tienen el mismo aspecto, estos dos tipos de representación son diferentes desde el punto de vista de un EA y del servidor de negociación. Esto se hace más evidente cuando el EA comienza a enviar órdenes pendientes en lugar de órdenes de mercado, que se ejecutan al mejor precio. En este caso, es importante crear una clase que pueda enviar este tipo de órdenes, que justamente permanecerán en la profundidad del mercado.

Pero, ¿por qué necesita el EA saber qué sistema de representación gráfica se utiliza? El motivo es simple. Cuando el activo utiliza el sistema de representación BID, el valor del precio LAST es siempre cero. Esto puede impedir que el EA envíe algunos tipos de órdenes al servidor, ya que no sabe cuál es el tipo correcto de orden que debe enviar. Si el EA consigue enviar la orden de todos modos y el servidor la acepta, la orden se ejecutará incorrectamente debido a que se ha llenado el tipo de orden incorrecto.

Este problema se produce cuando se intenta utilizar un EA creado para el mercado de ACCIONES en un mercado de DIVISAS. Pero lo contrario también es cierto: si el sistema de representación gráfica es de tipo LAST, pero el EA fue creado y construido para trabajar en el mercado de divisas, donde el sistema de representación gráfica es de tipo BID, el EA puede no enviar órdenes en el punto apropiado, ya que el precio LAST puede variar mientras que el BID y ASK permanecen estáticos.

Sin embargo, la diferencia entre el BID y el ASK siempre será superior a cero en el mercado de acciones, lo que significa que siempre habrá un spread entre ellos. El problema es que al crear la orden que se enviará al servidor, el EA puede crearla incorrectamente, especialmente si no observa los valores BID y ASK. Esto puede ser fatal para un EA que busque operar dentro del spread existente, ya que puede costar mucho dinero.

Por este motivo, el EA comprueba sobre qué tipo de mercado o sistema de representación gráfica está trabajando para que pueda ser utilizado o transferido de un mercado de DIVISAS a un mercado de ACCIONES o viceversa sin tener que ser modificado o compilado de nuevo.

Hemos observado cómo los pequeños detalles son importantes. Un simple detalle puede comprometerlo todo. Veamos ahora cómo enviamos una orden pendiente al servidor comercial.


Envío de una orden pendiente al servidor

Al estudiar la documentación de MQL5 sobre el funcionamiento del sistema comercial, encontramos la función OrderSend. Es importante recordar que no es necesario utilizar esta función directamente, ya que es posible enviar órdenes al servidor utilizando la librería estándar de MetaTrader 5, a través de la clase CTrade. Sin embargo, aquí quiero mostrar cómo funciona esta función entre bastidores.

Para enviar solicitudes de operaciones al servidor, simplemente utilizamos la función OrderSend. Sin embargo, hay que tener cuidado al utilizar directamente esta función. Así que, para asegurarnos de que sólo se utiliza dentro de la clase, lo pondremos en la cláusula private. De esta manera, podemos separar toda la complejidad de llenar la solicitud en las funciones públicas de la clase, que se pueden acceder por el EA.

Tenemos que ser muy cuidadosos y realizar las pruebas necesarias para saber cuándo algo ha ido mal y cuándo ha ido bien. Todos los requisitos pasarán por la función OrderSend, por lo que es conveniente crear un procedimiento sólo para ella, como se muestra en el código siguiente:

                ulong ToServer(void)
                        {
                                MqlTradeCheckResult TradeCheck;
                                MqlTradeResult      TradeResult;
                                bool bTmp;
                                
                                ResetLastError();
                                ZeroMemory(TradeCheck);
                                ZeroMemory(TradeResult);
                                bTmp = OrderCheck(m_TradeRequest, TradeCheck);
                                if (_LastError == ERR_SUCCESS) bTmp = OrderSend(m_TradeRequest, TradeResult);
                                if (_LastError != ERR_SUCCESS) MessageBox(StringFormat("Error Number: %d", GetLastError()), "Order System", MB_OK);
                
                                return (_LastError == ERR_SUCCESS ? TradeResult.order : 0);
                        }

No hay razón para asustarse por este código. En definitiva, garantiza el envío y nos informa de si el envío se ha realizado correctamente o no. En caso de fallo, se nos avisa inmediatamente. Examinemos con calma cómo realiza este trabajo. En primer lugar, ponemos a cero la variable interna de error para que cualquier error generado pueda ser analizado en cualquier punto del EA.

Recuerde probar siempre el retorno de los procedimientos más críticos. Una vez hecho esto, ponemos a cero las estructuras internas de la función y solicitamos una comprobación de la orden. Este es el punto importante: en lugar de verificar si la comprobación ha generado algún error para saber qué error se ha producido, comprobamos el valor de la variable de error. Si indica un valor distinto del esperado, no se realizará el envío al servidor. Si la comprobación no ha generado ningún error, la solicitud se enviará al servidor comercial.

Un detalle fundamental: independientemente de quién haya generado el error, si se produce, aparecerá un mensaje en el gráfico con el número de error. Si todo va perfectamente, el mensaje no aparecerá. Esto significa que, utilizando el procedimiento anterior, siempre tendremos el mismo tipo de tratamiento, independientemente del tipo de petición que estemos haciendo al servidor. Si se necesita más información o comprobaciones, basta con añadirlos a este procedimiento para garantizar que el sistema sea lo más estable y fiable posible.

La función devuelve dos valores posibles: el ticket de la orden, que siempre devuelve el servidor en caso de éxito, o el valor cero, que indica un error. Para saber cuál fue el error, basta con comprobar el valor de la variable _LastError. En un EA automatizado, este mensaje probablemente no estaría en este procedimiento, sino en algún otro lugar o como en el log. Depende de la finalidad del EA. Aquí, la idea es mostrar cómo crear un EA, así que decidí dejar el mensaje aquí para mostrar de dónde se origina.

Ahora, otro problema que tenemos que resolver es el siguiente: no se puede enviar ningún precio al servidor, porque la orden puede ser denegada. Se debe introducir el precio correcto. Algunos dicen que basta con normalizar el valor, pero eso no funciona en la mayoría de los casos. Se debe realizar un pequeño cálculo para introducir el precio correcto que se utilizará en la orden. Para ello utilizaremos la siguiente función:

inline double AdjustPrice(const double value)
                        {
                                return MathRound(value / m_Infos.PointPerTick) * m_Infos.PointPerTick;
                        }

Este sencillo cálculo corregirá el precio para que, independientemente del valor introducido, se corrija a un valor adecuado y el servidor acepte el precio indicado.

Genial, una cosa menos de la que preocuparse. Sin embargo, la mera creación de la función anterior no garantiza que nuestra clase cree o envíe una orden al servidor. Para ello, necesitamos rellenar la estructura MqlTradeRequest, que usted habrá visto en el código anterior y a la que se accede a través de la variable global privada m_TradeRequest. Se debe rellenar esta estructura correctamente.

Cada uno de los requisitos requiere un tipo específico de llenado, pero aquí sólo queremos enviar una solicitud para que el servidor pueda añadir una orden pendiente al activo. Cuando esto sucede, la plataforma MetaTrader 5 mostrará una orden pendiente en el gráfico y la caja de herramientas mostrará la orden pendiente.

Rellenar esta estructura es una de las principales causas de los problemas encontrados en los EAs, ya sean manuales o automatizados. Por lo tanto, utilizaremos nuestra clase para crear una abstracción y facilitar la correcta y adecuado rellenado de los valores, para que nuestra petición sea aceptada por el servidor comercial.

Pero antes de crear la función, hay que pensar un poco. Tenemos que responder a algunas preguntas:

  • ¿Qué tipo de operación vamos a realizar (compra o venta)?
  • ¿A qué precio queremos negociar?
  • ¿Cuál es el volumen que negociaremos?
  • ¿Cuál es el plazo deseado para la operación?
  • ¿Qué tipo de orden utilizaremos: buy limit, buy stop, sell limit o sell stop (no utilizaremos sell stop limit o buy stop limit)?
  • ¿En qué mercado operaremos: divisas o acciones?
  • ¿Qué stop financiero estamos dispuestos a hacer?
  • ¿Cuál es Take Profit que pretendemos alcanzar?
  • Vea que hay varias preguntas y algunas de ellas no se pueden utilizar directamente en la estructura requerida por el servidor. Así que hacemos una abstracción para crear un lenguaje o modelado más práctico, para que nuestro EA pueda funcionar sin problemas. Dejamos que la clase se encargue de la complejidad de hacer los ajustes y correcciones. Así, nace la siguiente función:

                    ulong CreateOrder(const ENUM_ORDER_TYPE type, double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
                            {
                                            double  bid, ask, Desloc;                      
    					
                                    	Price = AdjustPrice(Price);
                                            bid = SymbolInfoDouble(_Symbol, (m_Infos.PlotLast ? SYMBOL_LAST : SYMBOL_BID));
                                            ask = (m_Infos.PlotLast ? bid : SymbolInfoDouble(_Symbol, SYMBOL_ASK));
                                            ZeroMemory(m_TradeRequest);
                                            m_TradeRequest.action           = TRADE_ACTION_PENDING;
                                            m_TradeRequest.symbol           = _Symbol;
                                            m_TradeRequest.volume           = NormalizeDouble(m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1)), m_Infos.nDigits);
                                            m_TradeRequest.type             = (type == ORDER_TYPE_BUY ? (ask >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : 
                                                                                                        (bid < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));
                                            m_TradeRequest.price            = NormalizeDouble(Price, m_Infos.nDigits);
                                            Desloc = FinanceToPoints(FinanceStop, Leverage);
                                            m_TradeRequest.sl               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), m_Infos.nDigits);
                                            Desloc = FinanceToPoints(FinanceTake, Leverage);
                                            m_TradeRequest.tp               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), m_Infos.nDigits);
                                            m_TradeRequest.type_time        = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
                                            m_TradeRequest.type_filling     = ORDER_FILLING_RETURN;
                                            m_TradeRequest.deviation        = 1000;
                                            m_TradeRequest.comment          = "Order Generated by Experts Advisor.";
                                    
                                            return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
                    };
    

    Esta simple función de arriba es maravillosa, porque toma los datos que queremos operar y los transforma en algo que el servidor comercial espera recibir, independientemente de si estamos trabajando con un activo del mercado de DIVISAS o de ACCIONES.

    Para utilizar esta función, basta con indicarle si desea comprar o vender, el precio deseado, el stop financiero aceptable, el Take Profit deseado, el nivel de apalancamiento deseado y si la operación es diaria o de largo plazo. La función realizará todos los cálculos y ajustes necesarios para que la orden se cree correctamente para el servidor. Al utilizar la función anterior, es importante recordar algunos detalles para evitar problemas a la hora de crear ordenes por parte del servidor.

    Ahora bien, cuando informamos el valor cero para el Take Profit o el Stop Loss, la función no genera esos valores. Por lo tanto, es necesario tener cuidado al informar cero, porque esto puede ser interpretado por el servidor como una orden sin límite de ganancia o de pérdida. Además, debemos evitar informar valores negativos para el Stop Loss cuando estamos comprando y para el Take Profit cuando estamos vendiendo, porque esos valores no tienen sentido y la función los ignora. Al trabajar sólo con valores positivos, podemos garantizar que la creación de ordenes se produce de forma correcta por parte del servidor.

    Al ver esta función, puede que piense que es complicada y difícil de usar, pero cuando la vea en acción, puede que cambie de opinión. Antes de ver la función en acción, vamos a entender la función FinanceToPoints, que se menciona en el procedimiento anterior. A continuación, se muestra el código de la función FinanceToPoints:

    inline double FinanceToPoints(const double Finance, const uint Leverage)
                            {
                                    double volume = m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1));
                                    
                                    return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
                            };
    

    Intentemos entender lo que ocurre aquí, ya que puede ser un poco complicado. Si no comprende esta función, puede tener dificultades para entender lo que ocurre cuando la clase envía la orden al servidor y éste coloca los límites de Take Profit y Stop Loss en posiciones desplazadas respecto al precio de entrada. Por lo tanto, es importante comprender esta función para entender cómo funciona el sistema.

    Cualquier activo tiene información que puede utilizarse para convertir un valor financiero en un valor en puntos. Para entenderlo, véanse las imágenes a continuación:

    Figura 05  Figura 06  Figura 07


    Las cifras anteriores muestran los puntos resaltados, que se utilizan en el cálculo para convertir un valor financiero en puntos. En el caso de las DIVISAS, el tamaño y el valor del ticket no aparecen, pero son 1,0 para el valor del ticket y 0,00001 para el tamaño (número de puntos) por ticket. No olvide estos valores.

    Ahora considere lo siguiente: el valor financiero es el resultado de dividir el volumen negociado por el número de puntos, multiplicado por el valor de cada punto. Por ejemplo, suponga que negocia cualquier activo con un volumen mínimo de 100 y apalancado a 2x, es decir, el doble del volumen mínimo. En este caso, el volumen que negociará será 100 x 2, es decir, 200. Por ahora, no se preocupe por este valor, continuemos. Para averiguar el valor de cada punto, podemos realizar el siguiente cálculo:

    El valor de un punto es igual al valor por punto dividido por el número de puntos. Mucha gente asume que el número de puntos es 1, pero esto es un error. En las imágenes anteriores, puede ver que el número de puntos puede variar. Para divisas, es 0,00001. Así que no de por sentado que un valor es el más adecuado sin comprobarlo. Haga que el programa capture el valor correcto y utilice ese valor. Por ejemplo, supongamos que el número de puntos es 0,01 y el valor de cada punto es 0,01. En este caso, dividiendo uno por otro se obtendría un valor de 1. De nuevo, este valor puede cambiar. Para divisas, sería 0,00001 y para el dólar negociado en B3 (Bolsa de Brasil) es 10.

    Ahora, entendamos una cosa más. Para hacerlo más fácil, supongamos que el usuario desea realizar una operación con un activo con un volumen mínimo de 100 y que se apalancará 3 veces. El tamaño del ticket es 0,01 y el valor del ticket es 0,01. Sin embargo, quiere asumir un riesgo financiero de 250. ¿Cuántos puntos tiene que colocar en relación al precio de entrada para que el valor coincida con estos 250? Esto es exactamente lo que hace el procedimiento anterior. Calcula el valor y lo ajusta para que haya un desplazamiento positivo o negativo en la cantidad de puntos, según el caso, de modo que el valor financiero sea 250. Esto daría 2,5 o 250 puntos, en este caso.

    En este ejemplo, parece sencillo, pero intente hacer esto rápidamente mientras está operando en el mercado, con la volatilidad aumentando la tensión y usted necesitando decidir allí mismo qué tamaño de posición tomar en relación a un valor financiero dado, para no arriesgar mucho más de lo que está dispuesto a perder y no arrepentirse de haber puesto menos de lo que podría. En ese momento, usted se da cuenta de lo importante que es tener a su lado un EA bien programado.

    Con esta clase, ya puede permitir que el EA envíe una orden al servidor para que se configure correctamente. Sin embargo, para ver cómo se hace, vamos a crear una forma de probar nuestro sistema de órdenes.


    Una manera de probar el sistema de órdenes

    Me gustaría enfatizar que el objetivo de esta secuencia no es crear un EA que pueda ser operado manualmente. La idea es mostrar cómo un aspirante a programador puede crear un EA capaz de operar de forma automatizada con el menor esfuerzo posible. Si usted quiere aprender a crear un EA para operar manualmente, le recomiendo la serie de artículos "Desarrollo de un EA comercial desde cero". Allí le muestro cómo puede desarrollar un EA propio para operar manualmente, aunque esa serie de artículos puede quedar obsoleta y más adelante usted entenderá por qué. Por ahora, centrémonos en nuestro objetivo principal.

    Por lo tanto, para probar el sistema de órdenes del EA y comprobar si está enviando órdenes al servidor comercial, la forma más sencilla y adecuada es utilizar algunos trucos. Veamos cómo hacerlo. Normalmente, añadiría una línea horizontal en el gráfico, pero en este caso, sólo queremos comprobar si el sistema de órdenes está funcionando. Por lo tanto, para realizar la prueba de forma más sencilla, vamos a utilizar lo siguiente:

    #property copyright "Daniel Jose"
    #property description "This one is an automatic Expert Advisor"
    #property description "for demonstration. To understand how to"
    #property description "develop yours in order to use a particular"
    #property description "operational, see the articles where there"
    #property description "is an explanation of how to proceed."
    #property version   "1.03"
    #property link      "https://www.mql5.com/pt/articles/11223"
    //+------------------------------------------------------------------+
    #include <Generic Auto Trader\C_Orders.mqh>
    //+------------------------------------------------------------------+
    C_Orders *orders;
    ulong m_ticket;
    //+------------------------------------------------------------------+
    input int       user01   = 1;           //Fator de alavancagem
    input int       user02   = 100;         //Take Profit ( FINANCEIRO )
    input int       user03   = 75;          //Stop Loss ( FINANCEIRO )
    input bool      user04   = true;        //Day Trade ?
    input double    user05   = 84.00;       //Preço de entrada...
    //+------------------------------------------------------------------+
    int OnInit()
    {
            orders = new C_Orders();
            
            return INIT_SUCCEEDED;
    }
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
            delete orders;
    }
    //+------------------------------------------------------------------+
    void OnTick()
    {
    }
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
    {
    #define KEY_UP          38
    #define KEY_DOWN        40
    
            switch (id)
            {
                    case CHARTEVENT_KEYDOWN:
                            switch ((int)lparam)
                            {
                                    case KEY_UP:
                                            m_ticket = orders.CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
                                            break;
                                    case KEY_DOWN:
                                            m_ticket = orders.CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
                                            break;
                            }
                            break;
            }
    #undef KEY_DOWN
    #undef KEY_UP
    }
    //+------------------------------------------------------------------+
    

    A pesar de su simplicidad, este código nos permitirá probar el sistema de órdenes. Para ello, sólo tiene que seguir los siguientes pasos para posicionar una orden con el precio deseado:

    1. Indique el precio al que debe realizarse la orden. Recuerde que no debe utilizar el precio actual;
    2. Utilice la flecha hacia arriba si cree que el precio subirá o utilice la flecha hacia abajo si cree que el precio bajará;
    3. Para ello, verifique la pestaña "Trading" de la caja de herramientas. Debería aparecer una orden con las condiciones especificadas por el EA.

    En esta región, los datos se rellenarán como se indica y el EA podrá interactuar con el usuario. Observe cómo la clase se inicializa a través del constructor y se llama en el destructor. De esta manera, podemos estar seguros de que la clase siempre se inicializará antes de que hagamos cualquier llamada a uno de sus procedimientos internos.


    Conclusión

    Aunque este EA es muy sencillo, podrá enviar y probar el sistema de órdenes. Sin embargo, recuerde no utilizar el precio actual, porque de esta forma no podrá ver si el sistema está colocando las órdenes en la posición correcta y con los valores adecuados.

    En el siguiente vídeo puede ver una demostración de cómo debe funcionar el sistema y cómo está funcionando. En el archivo adjunto usted encontrará el código completo visto en este artículo para que pueda experimentar y estudiar cómo funciona.

    Pero aún estamos al principio. En el próximo artículo, las cosas se pondrán un poco más interesantes. Sin embargo, es importante que en esta fase comprenda bien cómo se colocan las órdenes en la profundidad del mercado.


    Vídeo de demostración


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

Archivos adjuntos |
Redes neuronales: así de sencillo (Parte 30): Algoritmos genéticos Redes neuronales: así de sencillo (Parte 30): Algoritmos genéticos
En el artículo de hoy, hablaremos de un método de aprendizaje ligeramente distinto. Podríamos decir que lo hemos tomado de la teoría de la evolución de Darwin. Probablemente resulte menos controlable que los métodos anteriormente mencionados, pero también nos permite entrenar modelos indiferenciados.
Cómo construir un EA que opere automáticamente (Parte 01): Conceptos y estructuras Cómo construir un EA que opere automáticamente (Parte 01): Conceptos y estructuras
Aprenda a crear un EA que opere automáticamente de forma sencilla y segura.
Aprendiendo a diseñar un sistema de trading con Accelerator Oscillator Aprendiendo a diseñar un sistema de trading con Accelerator Oscillator
Aquí tenemos un nuevo artículo de nuestra serie dedicada a la creación de sistemas comerciales basados en indicadores técnicos populares. Esta vez analizaremos el indicador Accelerator Oscillator: aprenderemos a utilizarlo y a crear sistemas comerciales basados en él.
Tablero de cotizaciones: Versión mejorada Tablero de cotizaciones: Versión mejorada
¿Qué tal si animamos la versión básica del tablero? Lo primero que vamos a hacer es modificar el tablero para añadir una imagen, ya sea el logotipo del activo o cualquier otra imagen, para facilitar una rápida identificación del activo que estamos viendo.