English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
Desarrollando un EA comercial desde cero (Parte 22): Un nuevo sistema de órdenes (V)

Desarrollando un EA comercial desde cero (Parte 22): Un nuevo sistema de órdenes (V)

MetaTrader 5Trading | 23 septiembre 2022, 17:24
582 0
Daniel Jose
Daniel Jose

1.0 - Introducción

No es nada fácil implementar un nuevo sistema, muchas veces nos encontramos con problemas que dificultan mucho el proceso, cuando suceden hay que parar y volver a analizar el rumbo que se está tomando, y decidir si sigue siendo válido mantener las cosas como están o si es mejor darle una nueva cara al mismo.

Este tipo de decisiones se dan casi siempre, más aún cuando no tenemos fecha ni presupuesto para producir un determinado sistema, es decir, cuando no tenemos presión y podemos probar y ajustar las cosas para evitar en lo posible que todo sea insostenible desde el punto de vista del desarrollo y la mejora.

Los grandes programas, pero sobre todo los de comercio que se desarrollan con un presupuesto determinado o con un plazo definido suelen tener muchos fallos que luego hay que corregir, y, muchas veces, los implicados en el desarrollo del sistema no tienen tiempo para implementar o estudiar ciertas soluciones que pueden aportar muchos beneficios al sistema en que se está trabajando, con esto tenemos programas que muchas veces no están a la altura de lo que los programadores de hecho podrían haber hecho, y en otros casos, tenemos lanzamientos de versiones tras versiones con pequeñas mejoras o correcciones de fallas, no porque las fallas fueron descubiertas tiempos después del lanzamiento, sino porque antes hubo la presión para el lanzamiento.

Esto es un hecho y ocurre en toda la cadena de producción, sin embargo, cuando estamos creando, buscamos la mejor manera y, preferiblemente, la más fácil; nos podemos permitir el lujo de estudiar todas las soluciones posibles y viables. Parar y retroceder un poco, modificar y mejorar la forma en que se está desarrollando el sistema es, muchas veces, una pequeña parada con un cambio de dirección que nos ayudará mucho en el desarrollo del sistema deseado.

En el artículo Desarrollando un EA comercial desde cero (Parte 21) el sistema quedó casi listo, faltó solo la parte responsable de mover las órdenes directamente dentro del gráfico. Pero durante las pruebas que hice para conseguirlo, me di cuenta de algunas cosas extrañas en el código, había muchas partes repetidas dentro del mismo. Incluso con todo el cuidado para evitar que se produzcan estas repeticiones, me di cuenta de que ocurría. Y lo peor fue el hecho de que ciertas cosas no funcionaban de la manera correcta, como cuando se utilizaban activos muy diferentes, es decir, básicamente cuando se empezó a diseñar el EA, y sobre todo cuando empecé a documentarlo en los artículos que se están publicando aquí. Pensaba utilizarlo básicamente solo al operar con futuros en la bolsa B3, y eventualmente se estaba diseñando para ayudar en las operaciones de futuros de Dólar o Índice, los conocidos WDO y WIN. Pero a medida que el sistema se acercaba más y más al sistema que pretendía utilizar inicialmente, me di cuenta de que podía ampliarlo a otros mercados u otros activos, y fue en este momento cuando apareció un problema.


2.0 - De vuelta a la mesa de dibujo

Para entender el problema, tenemos que fijarnos en una cosa que muchos pasan por alto durante el desarrollo de un EA. ¿Cómo se definen los contratos en el sistema de comercio? Pues bien, MetaTrader 5 nos proporciona esta información, y MQL5 nos permite acceder a ella, y es necesario entender los datos para crear las matemáticas que permitan generalizar los cálculos y conseguir un sistema que sea lo más completo posible.

El sistema de órdenes que les estoy enseñando a desarrollar tiene la finalidad de permitirles operar directamente mediante el gráfico, sin necesidad de ningún otro recurso externo ni de nada que pueda llegar a crearse en el futuro. Es un intento de crear un sistema de comercio definitivo mediante el gráfico, muy similar al que se encuentra en otras plataformas, pero con el código totalmente abierto, de forma que ustedes puedan adaptar y añadir la información que deseen.

La idea es que se pueda saber lo que ocurre en una posición con solo mirarla. Aunque la idea parece muy buena, el sistema de comercio no facilita la vida a los que intentan crear un sistema que funcione bien para FOREX y para los mercados de valores, como la B3. El nivel de información disponible es suficiente para poder configurar las órdenes para uno u otro tipo de mercado, pero crear algo genérico se convirtió en un reto, casi una afrenta personal; aun así, decidí afrontar la cuestión e intentar crear un sistema que fuera genérico.

Aunque no estoy seguro de que, de hecho, pueda hacer tal cosa, les mostraré cómo pueden encontrar la información que se necesita, y, si ustedes necesitan adaptar el EA para que se ajuste al sistema local, tendrán las bases y los conocimientos necesarios para poder hacerlo, incluso si el sistema no se encarga inicialmente del modelado, ustedes podrán adaptar las cosas.

Una observación es que estos datos se pueden conseguir a través de código usando MQL5, pero para facilitar el trabajo usaré MetaTrader 5 para mostrar dónde están los datos.


2.0.1 - Activo de una empresa (ACCIÓN) negociado en la B3

Pues bien, observen las siguientes dos imágenes:

     

Los puntos resaltados son los datos que necesitamos tener para poder calcular cómo se configurará la posición.

Es importante saber esto, porque si ustedes están generando una posición con base en un valor financiero, necesitarán estos datos para calcular los valores de Stop Loss y Take Profit. En caso de no utilizar órdenes OCO, la cosa es un poco más sencilla, pero aquí voy a considerar que todas las órdenes serán órdenes o posiciones OCO.

Un detalle importante: en el caso de que el activo se negocie en el mercado fraccionario (sí en la B3 hay una diferencia entre mercados), el volumen mínimo será del 1% del valor indicado, por lo que en lugar de negociar de 100 en 100, lo haremos de 1 en 1, esto hace una pequeña diferencia en el cálculo, recuerden este detalle.


2.0.2 - Contratos de futuros en la B3

Bueno, las reglas para los contratos de futuros son diferentes a las de la renta variable, el volumen cambia según se opere entre un contrato completo y un mini contrato, e incluso de un activo a otro, por ejemplo, si vamos a negociar BOI tendremos que mirar cómo se rellenan los datos de apalancamiento, ya que es diferente a los en que me voy a centrar aquí. Bueno aquí me centraré en los mini contratos, ya que en algunos casos un contrato completo corresponderá a 25 mini contratos, pero esto varía, por lo que hay que mirar las reglas de volumen que están presentes en la B3 (Bolsa de Brasil).

De este modo, observemos siguientes las imágenes del mini dólar:

     

Estos contratos tienen fechas de vencimiento, pero esto no es importante para este artículo, ya que, en el artículo Desarrollando un EA comercial desde cero (Parte 11), expliqué cómo hacer un sistema de órdenes cruzadas, para operar con contratos de futuros directamente desde su historial, y si usted está utilizando ese método no tendrá que preocuparse de qué contrato se negocia actualmente, el propio EA lo hará por usted. Pero observen los puntos marcados, vean que son diferentes al sistema de acciones, esto muchas veces puede generar algunos problemas, pero el EA hasta ese momento sabía lidiar con esto, es cierto que era poco intuitivo, pero con el tiempo uno termina acostumbrándose a cuales deben ser los valores que hay que utilizar en el apalancamiento para negociar un determinado volumen financiero.

Pero decidí subir la vara, y en este punto comienzan las dificultades.


2.0.3 - FOREX

Mi problema era el sistema Forex, y mi EA no podía lidiar con él. Cuando logré resolver el ajuste del apalancamiento en el Forex, no podía usar el mismo código que el EA en B3, y esto me molestaba, porque para mí no tiene sentido tener dos EAs con solo una única diferencia en el código. Para entender lo que estaba sucediendo veamos las siguientes imágenes:

     

Pues bien, este es el problema, en la B3 tenemos 4 valores, pero en Forex tenemos 2 valores, faltaban 2 valores y esto hacía que los cálculos no coincidieran entre los mercados.

Este hecho hacía que el EA generara un cálculo que dificultaba mucho la comprensión de lo que realmente se estaba haciendo, porque a veces se apalancaba demasiado, a veces el volumen hacía que el sistema de comercio denegara la orden porque el volumen era demasiado bajo respecto al volumen mínimo que se esperaba, a veces no se aceptaban los puntos límite de la orden OCO porque las posiciones eran erróneas, es decir, un lío sin fin.

Al darme cuenta de esto, decidí hacer un cambio en el EA. Ahora no voy a calcular el valor como se estaba haciendo, sino que lo voy a calcular de otra manera. De hecho, ese valor empezó a ser ajustado de acuerdo al activo que estamos operando, sin importar si estamos operando en la bolsa de valores o en Forex. De este modo, el EA se adaptará a los datos y, así, realizará los cálculos de forma correcta. Pero es necesario que sepamos lo que se negocia y conozcamos los datos de la negociación, de lo contrario se apalancará más de lo deseado o menos de lo necesario. Y aunque ahora, por razones computacionales, permito el uso de valores de punto flotante en el volumen, debemos evitar hacerlo, de hecho debemos usar valores enteros, que indican el nivel de apalancamiento que se está haciendo.

Así que entiendan bien, no le están diciendo al EA qué volumen quieren operar, sino cuanto apalancamiento se está haciendo sobre el volumen mínimo requerido. De esta manera resolví el problema. Pueden comprobar esto personalmente y probar lo que dije, solo compliquen el código fuente que se adjunta al final de este artículo.

Pero para facilitar la comprensión y reducir el trabajo, en el siguiente tema haré una rápida presentación de los resultados.


3.0 - Visualización de los datos

En primer lugar vamos a ver el caso del mercado de valores, en particular el mercado de valores de Brasil (B3). Vean a continuación cómo las cosas se muestran por el EA y el MetaTrader 5.


3.0.1 - Activos de la B3

En el caso de un activo o de una acción de alguna empresa cotizada en la bolsa brasileña, el volumen mínimo negociado es de 100, por lo que al informar un grado de apalancamiento de 1 estaremos de hecho operando el valor mínimo requerido por el activo. De esta manera es más sencillo saber cuánto estamos apalancados, si queremos comprar o vender más, no necesitamos saber que estaremos operando de 100 en 100, solo indicamos el grado de apalancamiento y EA hará los cálculos para que la orden se cree de manera correcta.

Si utilizamos fracciones, solo tenemos que indicar la cantidad de fracciones que se utilizaremos en el apalancamiento, es decir, 50 significa que utilizaremos 50 fracciones, si indicamos 15 nos estaremos apalancando en 15 fracciones y así sucesivamente.

Y el resultado de esto lo vemos justo debajo en la caja de herramientas de MetaTrader 5, veamos que se ha creado una orden con el tamaño de lote mínimo. Y si analizamos los valores de take y stop, veremos que son los mismos que los informados en el gráfico, lo que significa que el EA ha funcionado aquí.


3.0.2 - Mini Dólar

Aquí el volumen es de 1 en 1, pero en el caso de los contratos completos el valor es diferente, y quiero que nos fijemos en el Chart Trade, los valores indicados como Take y Stop son los mismos de los encontrados anteriormente, pero el EA ajusta los valores de forma correcta, por lo que los valores que se deben tener en cuenta son los valores encontrados en el indicador del gráfico, y los valores del Chart Trade deben ser ignorados, pero se acercarán a los valores indicados en el gráfico.


En la caja de herramientas veríamos los siguientes datos:

De la misma manera que en el activo anterior, si ejecutamos el cálculo para verificar los niveles de stop y take, veremos que coinciden con los indicados por el EA, en los indicadores del gráfico, es decir, el EA pasó esta etapa también y sin necesidad de recompilar el código, solo cambiando el activo.


3.0.3 - Forex

Aquí la cosa es un poco más complicada, para los que no están familiarizados con Forex, los puntos parecen ser bastante extraños, pero aun así el EA se las arregla para lidiar con ellos.


Y MetaTrader 5 nos informará de esta operación pendiente de la siguiente manera:


Recordando que en forex, los niveles de apalancamiento son bastante diferentes a los mostrados anteriormente. Pero si se hacen los cálculos y se sabe operar en forex, se verá que el EA pudo proporcionar los puntos correctos, y la orden se creó perfectamente.

Todo esto está hecho sin más modificación que la que mostraré en la parte de implementación, aunque se han hecho muchos otros cambios además de los que mostraré, para conseguir realmente que el EA se adapte tanto a los mercados de acciones como a los de divisas, sin tener que ser recompilado. Pero el enfoque aquí es terminar los artículos anteriores mostrando cómo hacer el desplazamiento de los niveles de stop o de take directamente en el gráfico.


4.0 - Implementación

Bueno, empecemos por ver algunos puntos que se han cambiado en el código.

En primer lugar se eliminó el sistema de límites que se implementó hace 3 o 4 versiones. Esta fue una decisión basada en que en algunos momentos el EA no se ajustaba correctamente al sistema de apalancamiento, más aun cuando se llevó de BOLSA a FOREX.

Se ha añadido un nuevo modelo de cálculo, para que el EA pueda trabajar de la misma manera en los mercados FOREX y BOLSA. Es importante que sepamos que antes de esta versión, esto no era posible, el EA al principio de su desarrollo estaba más orientado a trabajar y ayudar en las operaciones de BOLSA, pero decidí ampliar su funcionalidad también para el mercado FOREX, ya que la forma de operar y leer el mercado es prácticamente igual.

Hay detalles en cuanto a los problemas de apalancamiento, con las cuales el EA no podía lidiar en las versiones anteriores, pero con los cambios realizados, ahora puede ser utilizado tanto en FOREX como en BOLSA, sin ningún cambio importante en su código. Esto hizo que el código sufriera muchos cambios para que el EA se ajustara al apalancamiento de FOREX y al mismo tiempo mantuviera su compatibilidad con el mercado de BOLSA.

Uno de estos cambios se ve al principio del código:

int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(user20 * Terminal.GetVolumeMinimal(), user21, user22, user23);
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        TradeView.Initilize();
                
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}

La sección resaltada no existía originalmente, pero no es lo único que ha cambiado. Sin embargo el enfoque aquí no será esta implementación, sino cómo utilizar el EA para mover los puntos de take y stop presentes en el gráfico, de forma directa sin utilizar ningún otro artificio, así que hagámoslo a partir de ahora.


4.0.1 Desplazamiento de órdenes directamente en el gráfico

Hacer esto en las versiones anteriores no era algo sencillo, por el contrario, había una serie de temas pendientes y dificultades que se fueron generando a través del tiempo, que lo hacían muy difícil. Una de las razones era que el código estaba muy disperso, lo que dificultaba implementar de manera viable el sistema de desplazamiento de órdenes directamente en el gráfico, el sistema originalmente no estaba pensado para llegar a este punto.

Bueno para que se hagan una idea del grado de cambios que han sido necesarios para que el EA pueda hacer esto, es decir, para que pueda procesar el evento de movimiento de órdenes, ya sean pendientes o posiciones, en este caso sus límites directamente en el gráfico con la ayuda del ratón. Veamos cómo era el EA antes.

El problema es que, cuando se crearon los objetos, estos sustituyeron a la clase C_HLineTrade, y esto en el artículo Desarrollo de un EA comercial desde cero (Parte 20). El sistema comenzó a tener una estructura mucho más compleja, así que para no mostrar toda la imagen de arriba de nuevo, vemos solo lo que pasó.

La flecha indica el punto de conexión donde la clase C_HLineTrade salió para entrar en nuevas clases. Bien, esto nos permitió hacer lo que se puede ver en los artículos anteriores. Pero el hecho de tener la clase C_OrderView estaba estorbando en el desarrollo, y no había manera, tenía que irse. Y eso no fue todo, lo que en realidad sucedió fue que la clase C_TradeGraphics se unió a la antigua clase C_OrderView, y estas recibieron una nueva denominación, C_IndicatorTradeView. Esta clase entonces reemplazó a ambas, pero al mismo tiempo permitió el desarrollo del sistema de movimiento de órdenes.

Y lo que voy a presentar aquí es la primera versión de este sistema, ya se está trabajando otra versión que pronto pondré en otro artículo.


4.0.1.1 - Codificación del nuevo sistema


Con la unión, el nuevo sistema tiene la configuración que se puede ver a continuación:


La región verde indica un conjunto de clases que están sueltas, es decir, el EA no se ocupará de ellas, quien se encarga de ellas es el MetaTrader 5, pero ¿cómo?  Vamos con calma, ya lo entenderemos. El EA de hecho solo creará, colocará y eliminará las clases y todos los objetos que han sido creados por estas clases, si ustedes miran dentro del código del EA, no encontrarán ninguna estructura o variable que de hecho estará haciendo referencia a los objetos que la región verde estará creando, esto nos permite crear un número ilimitado de objetos, siempre y cuando MetaTrader 5 pueda asignar memoria en el sistema operativo, el número de objetos no estará limitado por ningún tipo de estructura o variable dentro del EA.

Podrías pensar que solo un loco crearía una estructura así, entonces puedes llamarme loco, porque la he creado y funciona, y sorprendentemente no sobrecarga tanto el sistema, mucha gente me llama loco y no es de ahora, así que... Bien pero continuando, pueden notar cierta lentitud cuando están moviendo las órdenes pendientes o limitadas de una posición. El problema es que el sistema moverá las órdenes pendientes o limitadas desplazándolas en el propio servidor de operaciones, y esto, por si no lo sabes, provoca una latencia, o sea un retraso entre el movimiento y la respuesta del servidor. Ciertamente, esta es la mejor y más segura manera de operar en algunos escenarios, pero no nos permite hacer otra cosa que quiero que el EA ejecute. Así que en los próximos artículos arreglaremos esto añadiendo una nueva funcionalidad en el EA, y al mismo tiempo dejaremos el sistema más fluido, pero esto sin modificar la estructura anterior, solo manipulando el código de forma adecuada, aunque esto deja el sistema menos seguro, pero hasta entonces ¿quién sabe? puede que encuentre una buena solución a este problema.

Bien, pero veamos algunos puntos importantes en el nuevo código actual, empezando por una función poco explorada, su código se ve a continuación:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        ulong ticket;
        
        if (trans.symbol == Terminal.GetSymbol()) switch (trans.type)
        {
                case TRADE_TRANSACTION_DEAL_ADD:
                case TRADE_TRANSACTION_ORDER_ADD:
                        ticket = trans.order;
                        ticket = (ticket == 0 ? trans.position : ticket);
                        TradeView.IndicatorInfosAdd(ticket);
                        TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type)));
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                                if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order);
                                else
                                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                                if (!PositionSelectByTicket(trans.position))
                                        TradeView.RemoveIndicator(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_UPDATE:
                        TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type));
                        break;
                case TRADE_TRANSACTION_POSITION:
                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                        break;
        }
        
        
#undef def_IsBuy
}

Este código es muy interesante, porque evita que tengamos que llegar al punto de estar probando cada nueva posición que va apareciendo o que se va modificando. En realidad lo que ocurre es que el propio servidor nos informará de lo que está ocurriendo, por lo que solo tenemos que hacer que el EA responda a los eventos de forma correcta. Estudien bien esta forma de codificar y de utilizar el evento OnTradeTransaction, porque si yo utilizara el mismo modelo para análisis como en la versión anterior del código, gastaríamos mucho tiempo en ciertas pruebas y verificaciones. Pero de esta forma el propio servidor hace el trabajo pesado por nosotros, y tenemos la garantía de que los indicadores que están en el gráfico están efectivamente mostrando lo que el servidor está viendo en ese momento exacto.

Antes de entrar en los aspectos más destacados del código anterior, echemos un vistazo a otro fragmento

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        TradeView.DispatchMessage(id, lparam, dparam, sparam);
        ChartRedraw();
}

Podemos ver que todo será tratado en un solo lugar, de esta manera podemos pasar a la clase y ver un poco más de lo que sucede dentro de ella.


4.1 - La clase C_IndicatorTradeView

Esta clase hará todo el trabajo de presentación y manipulación de los datos. Ella básicamente incluye las antiguas clases C_OrderView y C_TradeGraphics, como ya he dicho, sin embargo será capaz de manipular los datos de una manera totalmente diferente. Veamos algunos puntos que merecen ser destacados en esta clase.

Comenzaremos con la función de inicialización, cuyo código se ve a continuación:

void Initilize(void)
{
        int orders = OrdersTotal();
        ulong ticket;
        bool isBuy;
        long info;
        double tp, sl;

        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_OBJECT_DESCR, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false);
        for (int c0 = 0; c0 <= orders; c0++) if ((ticket = OrderGetTicket(c0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                info = OrderGetInteger(ORDER_TYPE);
                isBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(-1, ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), OrderGetDouble(ORDER_SL), OrderGetDouble(ORDER_VOLUME_CURRENT), isBuy);
        }
        orders = PositionsTotal();
        for (int c0 = 0; c0 <= orders; c0++) if (PositionGetSymbol(c0) == Terminal.GetSymbol())
        {
                tp = PositionGetDouble(POSITION_TP);
                sl = PositionGetDouble(POSITION_SL);
                ticket = PositionGetInteger(POSITION_TICKET);
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(1, ticket, PositionGetDouble(POSITION_PRICE_OPEN), tp, sl, PositionGetDouble(POSITION_VOLUME), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
        }
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PENDING);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP);
}

Básicamente lo que estamos haciendo es crear los indicadores necesarios para trabajar y presentar lo que esté presente en la cuenta, como posiciones u órdenes pendientes. Pero las líneas resaltadas son importantes aquí, porque en caso de que no estemos usando un sistema de órdenes cruzadas, tendremos en el gráfico los puntos de las órdenes cruzadas (originales de MetaTrader 5), y si hacemos clic y arrastramos estos puntos, el EA actualizará los nuevos puntos con los cambios en los indicadores. Esto no causa demasiada molestia, pero tenemos que usar realmente el sistema que se está desarrollando, si no, ¡¿para qué desarrollarlo?!

Bueno, el siguiente código que vale la pena destacar se ve a continuación:

void UpdateInfosIndicators(char test, ulong ticket, double pr, double tp, double sl, double vol, bool isBuy)
{
        bool isPending;
                                
        isPending = (test > 0 ? false : (test < 0 ? true : (ticket == def_IndicatorTicket0 ? true : OrderSelect(ticket))));
        PositionAxlePrice(ticket, (isPending ? IT_RESULT : IT_PENDING), 0);
        PositionAxlePrice(ticket, (isPending ? IT_PENDING : IT_RESULT), pr);
        SetTextValue(ticket, (isPending ? IT_PENDING : IT_RESULT), vol);
        PositionAxlePrice(ticket, IT_TAKE, tp);
        PositionAxlePrice(ticket, IT_STOP, sl);
        SetTextValue(ticket, IT_TAKE, vol, (isBuy ? tp - pr : pr - tp));
        SetTextValue(ticket, IT_STOP, vol, (isBuy ? sl - pr : pr - sl));
}

Él recibe unos datos y actualiza todos los demás por nosotros, presentando los valores correctos solo en términos de valor financiero y los puntos donde están las órdenes. Básicamente no tenemos que preocuparnos si la orden es pendiente o es una posición, la propia rutina hará el posicionamiento de forma que lo veremos en el gráfico de forma correcta.

La siguiente rutina se ve a continuación.

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ticket = PositionGetInteger(POSITION_TICKET);
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

Ella es llamada por el evento OnTick, y es un punto crítico en términos de velocidad y carga sobre el sistema. Lo único que ella hace, además de las pruebas, es actualizar el valor para nosotros en el indicador, y esto lo hace el código resaltado, noten que el ticket de la posición es algo muy importante para nosotros.

Veamos la rutina que se destaca en el fragmento anterior y que se puede ver a continuación.

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0 / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        break;
        }
}

De esta manera los valores se presentarán en el indicador, pero ahora viene la pregunta: ¿Cómo moverlos? Pues esto lo hacen otros 3 códigos, es muy cierto que se pueden descartar y se puede utilizar el propio sistema de MetaTrader 5, que es bastante más rápido que el sistema actual del EA. Pero como ya he dicho, voy a dar preferencia por el uso del EA, ya que recibirá otras mejoras en breve.

La primera rutina responsable de los movimientos se ve a continuación, pero solamente los fragmentos necesarios para el movimiento de los puntos, sean estos los límites o la orden en sí, se colocaron allí, ya que todo el código es mucho más extenso y no es necesario para entender cómo hacer el movimiento con base en los desplazamientos del ratón.

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;

// ... Código ....

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Clique esquerdo
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT Pressionada
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL Pressionada
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        bMounting = true;
                                }
                                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                        }else if (bMounting)
                        {
                                UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick)
                                {
                                        bIsMove = false;
                                        m_TradeLine.SpotLight();
                                }
                                MoveSelection(price, mKeys);
                        }
                        break;

// ... Código ...
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Código ...

                                case EV_MOVE:
                                        if (bIsMove)
                                        {
                                                m_TradeLine.SpotLight();
                                                bIsMove = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                bIsMove = true;
                                        }
                                        break;
                        }
                        break;
        }
}

Tratemos de entender lo que está sucediendo, al final hay un video que muestra cómo hacerlo, y lo que estará sucediendo de hecho, pero tratemos de entender primero.

Cada indicador tiene un objeto que permite seleccionar el indicador, a excepción del de resultado, que es un indicador que no se puede mover. Al hacer clic en este punto, la línea del indicador cambiará, haciéndose más gruesa, cuando esto ocurra, los movimientos del ratón serán capturados y convertidos en una nueva posición para este indicador, hasta que demos un nuevo clic fuera del objeto de selección que permite el movimiento del indicador. Veamos que no hay necesidad de mantener el botón del ratón, solo debemos hacer clic una vez, arrastrar y volver a hacer clic.

Pero solo una parte del trabajo se está haciendo de hecho aquí. Tenemos otras 2 rutinas para ayudarnos, una ya se vio arriba, que es la responsable de mostrar los valores calculados, la otra es la responsable de ser el lastre que hace que el EA se vea como una babosa al usar el sistema de movimiento de ordenes o de límites, y se puede ver a continuación:

void MoveSelection(double price, uint keys)
{
        static string memStr = NULL;
        static ulong ticket = 0;
        static eIndicatorTrade it;
        eEventType ev;
        double tp, sl, pr;
        bool isPending;
                                
        string sz0 = m_TradeLine.GetObjectSelected();
        
        if (sz0 != NULL)
        {
                if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                isPending = OrderSelect(ticket);
                switch (it)
                {
                        case IT_TAKE:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                break;
                        case IT_STOP:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                break;
                        case IT_PENDING:
                                pr = macroGetPrice(IT_PENDING);
                                tp = macroGetPrice(IT_TAKE);
                                sl = macroGetPrice(IT_STOP);
                                ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                break;
                }
        };
}

Llamo lastre a esta rutina porque ES RESPONSABLE DE QUE EL SISTEMA DE POSICIONAMIENTO SEA LENTO... si no lo entienden, fíjense en los puntos resaltados, cada uno de ellos es una función que está dentro de la clase C_Router y que enviará una petición al servidor de comercio, por lo que si el servidor por una u otra razón tarda en responder, y esto siempre ocurrirá por la latencia, el sistema de posicionamiento será más o menos lento, pero si el servidor responde rápidamente, el sistema será fluido, o mejor, la cosa ocurrirá de manera más suave. Más adelante modificaremos esto, porque este sistema no nos permite hacer otra cosa, es necesario que tengamos en cuenta que así estaremos operando de una manera más segura, más aun si te gusta operar en movimientos de gran volatilidad, donde los precios pueden dislocarse muy rápidamente, pero aún así corremos el riesgo de que los límites salten, no hay otra manera, algo hay que sacrificar. Pero para los que aceptan operar sabiendo que esto es exactamente lo que habrá dentro del servidor, el EA está listo en este punto, y para los que quieren ganar fluidez en el funcionamiento del EA, aún a costa de que no se respeten los puntos, en los próximos artículos la cosa cambiará para dejar todo más interesante.

En el siguiente video muestro cómo funciona realmente, así que presten atención a los valores en el gráfico y en la caja de herramientas, donde se muestra lo que está sucediendo con la orden.



5.0 - Conclusión

Aunque ahora tenemos un EA muy interesante para operar, les aconsejo que lo usen durante un tiempo en una cuenta demo para acostumbrarse a su funcionamiento. Prometo que no habrá más grandes cambios en la forma de trabajar con él, sólo mejoras, y en un próximo artículo añadiremos algunas cosas que faltan en este EA, en detrimento de cierta seguridad que aún nos proporciona. De una manera u otra, será una gran fuente de aprendizaje de cómo funciona el sistema de comercio y cómo manipular la plataforma para tener cualquier tipo de modelado de datos que necesitemos.

No olviden una cosa, en caso de que el sistema de desplazamiento de las órdenes o los límites les resulte demasiado lento, pueden eliminar los puntos que mostré en el artículo y utilizar el propio MetaTrader 5 para mover las órdenes o los límites, y utilizar el EA como apoyo para ayudar en la lectura e interpretación de los datos. Es su elección hacer esto o no...


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

Archivos adjuntos |
Desarrollo de un EA comercial desde cero (Parte 23): Un nuevo sistema de órdenes (VI) Desarrollo de un EA comercial desde cero (Parte 23): Un nuevo sistema de órdenes (VI)
Haremos más fluido el sistema de ordenes. Aquí les mostraré cómo y dónde hacer cambios en el código para tener algo más fluido que nos permita modificar los límites de posición mucho más rápido.
El modelo de movimiento de precios y sus principales disposiciones (Parte 2):  Ecuación de evolución del campo de probabilidad del precio y aparición del paseo aleatorio observado El modelo de movimiento de precios y sus principales disposiciones (Parte 2): Ecuación de evolución del campo de probabilidad del precio y aparición del paseo aleatorio observado
En el presente artículo, hemos derivado una ecuación para la evolución del campo probabilístico de precio, hemos encontrado un criterio para acercarnos al salto de precio, y también hemos revelado la esencia de los valores de precio en los gráficos de cotización y el mecanismo para la aparición de un paseo aleatorio de dichos valores .
DoEasy. Elementos de control (Parte 10): Objetos WinForms: dando vida a la interfaz DoEasy. Elementos de control (Parte 10): Objetos WinForms: dando vida a la interfaz
Ha llegado el momento de revitalizar la interfaz gráfica de usuario, haciendo que los objetos interactúen con el usuario y otros objetos. Y para que los objetos más complejos funcionen correctamente, necesitaremos una funcionalidad que permita a los objetos interactuar entre sí y con el usuario.
Características del Wizard MQL5 que debe conocer (Parte 02): Mapas de Kohonen Características del Wizard MQL5 que debe conocer (Parte 02): Mapas de Kohonen
Gracias al Wizard, el tráder podrá ahorrar tiempo a la hora de poner en práctica sus ideas. Asimismo, podrá reducir la probabilidad de que surjan errores por duplicación de código. En lugar de perder el tiempo con el código, los tráders tendrán la posibilidad de poner en práctica su filosofía comercial.