English Русский Português
preview
Desarrollo de un sistema de repetición (Parte 33): Sistema de órdenes (II)

Desarrollo de un sistema de repetición (Parte 33): Sistema de órdenes (II)

MetaTrader 5Ejemplos | 2 abril 2024, 10:02
127 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, Desarrollo de un sistema de repetición (Parte 32): Sistema de órdenes (I), comenzamos a desarrollar el sistema de órdenes a ser usado dentro de un Expert Advisor. Allí desarrollamos la clase base del sistema, con solo y únicamente las rutinas que realmente necesitamos inicialmente.

Puedes pensar, y esto no estaría del todo mal, que esas rutinas son en esencia muy básicas y no cubrirán totalmente el sistema de órdenes. Esto es cierto, pero en este primer momento vamos a desarrollar un sistema que no se utilizará directamente en el servicio de repetición/simulador. Desarrollaremos, en primer lugar y es esencial que entiendas esto, un sistema que pueda ser operado junto al servidor de negociación real, ya sea usando una cuenta demo o una cuenta real. Haremos uso masivo y extensivo de la plataforma MetaTrader 5 para proporcionarnos todo el soporte que necesitaremos en este inicio de viaje. Es extremadamente importante que el sistema primero funcione cuando la plataforma está comunicándose con el servidor. Porque cuando sólo se utiliza la repetición/simulador, no podemos seguir adaptando el sistema Expert Advisor a nuestra repetición/simulador. Deberemos hacer que la repetición/simulador se adecue a lo que hace el Expert Advisor. Por esto necesitamos que el Expert Advisor funcione primeramente en el servidor real.

El hecho de haber tomado esta decisión hace que el sistema, y consecuentemente la clase C_Orders, tenga un número reducido de procedimientos y funciones. Esto es así pensando en un sistema más amplio. Pero no te equivoques imaginando que la clase C_Orders no será capaz de suplir nuestras necesidades en este momento. Tendremos que hacer que la plataforma MetaTrader 5 nos dé el máximo soporte que necesitamos para poder operar junto al servidor de negociación. Esta cuestión de hacer que MetaTrader 5 nos dé el máximo soporte es justamente para evitar crear cosas sin necesidad. No debemos intentar reinventar la rueda. La plataforma debe explotarse al máximo para que tengamos que hacer el mínimo esfuerzo cuando vayamos a tratar con el sistema de repetición/simulación.

Pero antes de continuar hablando del sistema de órdenes, me gustaría mostrar cómo solucionar un problema. Aunque no se trata de un problema, sino solo de un inconveniente presente en MetaTrader 5.


Intentar simplificar las cosas

Quien es usuario asiduo de MetaTrader 5 debe haber notado o al menos tropezado con una cuestión que es, por un lado, bastante molesta y, por otro, cuanto menos desalentadora. Y lo que estoy diciendo se nota más por parte de personas que migran de otras plataformas al MetaTrader 5 y, al enfrentarse con su funcionamiento, se sienten molestas o desmotivadas a usarlo en algunos momentos, principalmente los que involucran estudios de gráfico.

La cuestión, y esto creo que muchos deben estar bastante descontentos, es el hecho de que cuando hacemos un estudio o algún tipo de anotación en el gráfico de un activo, se nos suele impedir, después de reiniciar la plataforma, modificar o eliminar tales objetos usados en los estudios, esto de manera simple. Porque hay algunas cosas que es necesario saber configurar en MetaTrader 5 para de hecho saber cómo manejar las cosas en él. Pero vamos a suponer que acabas de instalar la plataforma en tu computador y nunca has interactuado con ella. Entonces este será tu primer contacto con la misma. Y quiero hacer hincapié en este detalle: Esta será la primera vez que vas a interactuar con MetaTrader 5.

He aquí una cuestión curiosa: A pesar de que podemos acceder a los objetos antiguos haciendo doble clic en ellos, esto siempre y cuando no estén con el flag (casilla de verificación, vista en la figura 01) marcado como "Desactivar selección", podremos de hecho acceder al objeto.

Figura 01

Figura 01 - Casilla de verificación activada.

Pero, independientemente de esto, la cuestión no es precisamente esa. La cuestión es que muchos usuarios, al migrar de otras plataformas, tienen el hábito de hacer solo y únicamente un clic en el objeto. Y este pasa a ser seleccionado, pudiendo ser modificado o eliminado del gráfico. El hecho de que MetaTrader 5 exija un doble clic no es del todo obvio, siendo necesario tener cierta familiaridad o, al menos, cierta curiosidad por la plataforma para saber cómo proceder en estos casos.

Ahora viene un detalle. En MetaTrader 5, existe una opción que permite cambiar este comportamiento. Para tener acceso a ella, deberás presionar la combinación CTRL + O. Esto abrirá la ventana de opciones de MetaTrader 5. En esta ventana, debes ir a la pestaña Gráficos y marcar la opción: "Seleccionar objetos con un clic del ratón". Si esto se hace, podrás hacer uso de MetaTrader 5 de la misma manera que lo harías en cualquier otra plataforma. Pero, aun así, tendrías que hacer este pequeño ajuste, y aun así aún no tendrías algunas posibilidades Y esa será la intención aquí: mostrar cómo hacer esto de otra manera, pero con nuevas posibilidades.

Como programador, me encuentro en una situación en la que puedo, de alguna manera, hacer que un usuario que está migrando a MetaTrader 5 tenga una experiencia adecuada y agradable al usar la plataforma. Y lograr que tengamos que dar solo un clic es algo bastante adecuado. Sin embargo, la programación no parece tan obvia, al punto de ver códigos que muestren cómo hacer tal cosa. Quizá sea porque mucha gente piensa que ese trabajo es innecesario. Pero recuerda que podemos ampliar las posibilidades de todo esto.

Pero para resolver esta cuestión, volveremos a la clase C_Terminal y añadiremos algunas pocas líneas de código en el sistema. Esto ya será suficiente para promover una experiencia mucho más agradable por parte de nuevos usuarios. Veamos entonces lo que deberá ser de hecho añadido al código. Esto se puede apreciar en el código a continuación:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      static string st_str = "";
                                
      switch (id)
      {
         case CHARTEVENT_CHART_CHANGE:
            m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
            m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
            break;
         case CHARTEVENT_OBJECT_CLICK:
            if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
            if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
            ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
            break;
         case CHARTEVENT_OBJECT_CREATE:
            if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
            st_str = sparam;
            break;
      }
   }

Este procedimiento ya forma parte del código original de la clase C_Terminal. Pero aquí se añadieron algunas pocas líneas extras, en las que vamos a tratar dos nuevos eventos. Estos nos serán informados por la plataforma MetaTrader 5. Existe un detalle en este código arriba, pero puedo explicar el código y luego explicar el detalle. Observen que declaramos una variable local estática. Esta sirve para almacenar el nombre del objeto que estará recibiendo el foco. Esta variable se inicia como siendo una string vacía. Esto es importante para que logremos hacer las cosas sin fallos. Tan pronto como un objeto en el gráfico recibe un clic, el nombre del objeto es pasado a nuestro programa. Así como el evento, que en el caso es informado por la plataforma como un CHARTEVENT_OBJECT_CLICK, y el nombre del objeto estará en la variable sparam. Estos pequeños detalles nos ayudarán en la próxima etapa. Ahora verificamos si el nombre presente en nuestra variable local es el mismo que está siendo informado por MetaTrader 5. En el caso de la primera ejecución, serán diferentes. Si son iguales, nada va a suceder de hecho. Pero si son diferentes, el objeto que estaba bajo el foco de la plataforma perderá el foco, y esto se hace en esta línea, donde probamos y, si es necesario, eliminamos el foco del objeto.

Ahora quiero que prestes atención al siguiente hecho: Cuando un objeto tiene sus propiedades modificadas, con el fin de quedar como se muestra en la figura 01, debemos entender que el usuario está, de forma consciente, diciendo que ese objeto no debe recibir atención de manera inadvertida. Sea porque el objeto no debe ser modificado, sea porque él indicará algo, y no queremos cambiarlo de posición, o cambiar algo de ese tipo. De cualquier forma, al marcar la casilla de verificación como se muestra en la figura 01, debemos entender que el objeto debe ser ignorado. Y es justamente esto lo que esta prueba hace. Ella está verificando si el objeto debe o no ser ignorado. Si esta prueba no se hiciera, el flag que el usuario habría hecho en la propiedad del objeto, según la figura 01, sería ignorado y el objeto sería accedido, recibiendo el foco. Entonces, la forma de hacer que el objeto reciba el foco es haciendo uso exactamente de este código. Noten que en este código estamos almacenando el nombre del objeto para que cuando tenga que perder el foco, nuestro programa sepa cuál era el objeto. Algo bastante parecido ocurre cuando MetaTrader 5 dispara el evento CHARTEVENT_OBJECT_CREATE. Pero aquí existe un detalle: Diferente del evento CHARTEVENT_OBJECT_CLICK, este evento de creación de objeto solo será disparado para nuestro programa si le decimos a MetaTrader 5 que deseamos saber cuándo un objeto es creado.

Nota: De cualquier forma, MetaTrader 5 siempre disparará los eventos, pero algunos solo serán dirigidos a nuestro código si informamos esto a la plataforma.

Para decirle a MetaTrader 5 que queremos recibir notificaciones cuando un objeto sea creado en el gráfico, procedemos de una manera bastante cercana a lo que hacemos cuando queremos ser notificados cuando un objeto es eliminado. Así, es necesario agregar la siguiente línea de código:

C_Terminal()
   {
      m_Infos.ID = ChartID();
      CurrentSymbol();
      m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
      m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
      m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
      m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
      m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
      m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
      m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
      m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
      m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
      m_Infos.ChartMode     = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
      ResetLastError();
   }

Esta línea informa a MetaTrader 5 que, a partir de este momento, queremos ser notificados cuando un nuevo objeto sea creado en el gráfico. Igualmente, necesitamos informar a MetaTrader 5 cuando no queremos ser más notificados, y esto se hace usando el siguiente código:

~C_Terminal()
   {
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
   }

En el momento en que esta línea sea ejecutada, MetaTrader 5 no más notificará a nuestro código sobre eventos de creación de objetos. Este tipo de cosa es bastante importante e interesante, ya que puede ser que quieras saber cuándo el usuario añade o elimina objetos del gráfico. E intentar hacer esto "a mano" es algo bastante complicado y difícil de ser hecho. Pero con la ayuda de MetaTrader 5, es bastante fácil de hacer esto. Creo que habrás notado que podemos de hecho subyugar la plataforma para hacer que actúe según lo que esperamos. Este tipo de comportamiento, y el hecho de poder hacer esto, hace que podamos ayudar a nuevos usuarios a tener una mejor experiencia en el uso de la plataforma, lo que es bastante motivador. Es todo una cuestión de adaptación, pero esta será más rápida si tú, como programador, puedes ayudar a quien no es programador.

Bien, pero volvamos a lo que realmente vinimos a hacer aquí en este artículo. Pues todavía tenemos muchas cosas que hacer antes de que podamos realmente tocar otra cosa.


Ampliar el sistema de órdenes

El título de este tema quizás sea un poco exagerado, pero quiero traer a este nuestro sistema de órdenes algo que fue mostrado en otra serie de artículos. Así, podrás utilizar los mismos conceptos y conocimientos que fueron expuestos allí. La serie en cuestión tuvo su último artículo, hasta este momento, que puede ser accedido en este enlace: Cómo construir un EA que opere automáticamente (Parte 15) - Automatización (VII). Allí mostré cómo haces para convertir un Expert Advisor que fue construido y operado de manera manual en un Expert Advisor automático. El hecho de traer este tema en este momento se debe al hecho de que puede ser interesante para ti observar el Expert Advisor trabajando en una repetición/simulador, con el fin de poder hacer algún tipo de ajuste en él. Recuerda que, para probar y verificar la productividad del Expert Advisor, la plataforma MetaTrader 5 cuenta con el simulador de estrategias que sirve muy bien a su propósito. Aquí no estoy creando algo paralelo al simulador. La idea aquí es que puedas practicar alguna técnica, incluso cuando el mercado esté cerrado.

Entonces vamos a traer los conceptos e ideas que se vieron en la serie sobre el Expert Advisor automático para acá. Y para hacer esto, comenzaremos trayendo la clase de control de tiempo.

Un detalle importante: En esta primera fase, donde vamos a usar el Expert Advisor y desarrollarlo, haciendo que el mismo esté orientado a darnos acceso más simple al servidor de negociación, daré una rápida pincelada en las funciones que importaremos de aquella serie de artículos. Para que tengas más detalles y una explicación más profunda, lee la serie que, de cierta forma, te ayudará bastante en algunas cuestiones.


Controlar el tiempo con C_ControlOfTime

Esta clase C_ControlOfTime es una clase bastante curiosa. A pesar de tener un código bastante compacto, ella nos permite, de una forma bastante simple, hacer que el Expert Advisor tenga algún nivel de gestión basado en el horario en el cual podemos operar. Muchos operadores suelen estar bastante ansiosos en los primeros movimientos del mercado, entrando o finalizando de manera prematura posiciones. El hecho de tomar decisiones basadas en emoción en un tipo de actividad donde la emoción no es precisamente lo que necesitamos hace que muchas veces suframos pérdidas, cuando de hecho, al seguir un plan, no habríamos operado en ese momento. Una de las maneras más simples de promover este control es por medio del horario. Ya que, durante la serie sobre el Expert Advisor automático, mostré cómo implementar tal dispositivo, vamos a traerlo a este Expert Advisor de aquí. Al hacerlo, tendremos el mismo tipo de comportamiento visto allí, lo cual es muy bueno, pues nadie quiere de hecho operar fuera de una franja horaria que considere adecuada y un poco más segura para operar en el mercado. Entonces, ¿qué tal si hacemos que el Expert Advisor nos ayude en esto?

Bueno, la clase C_ControlOfTime comienza de la siguiente manera, vista abajo:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Orders.mqh"
//+------------------------------------------------------------------+
class C_ControlOfTime : protected C_Orders
{
   private :
      struct st_00
      {
         datetime Init,
                  End;
      }m_InfoCtrl[SATURDAY + 1];
//+------------------------------------------------------------------+

Aquí vamos a almacenar en una matriz el horario permitido para las operaciones, esto día por día. Tal vez la parte más curiosa de este código sea el uso de la definición SATURDAY. De hecho, para quienes no vieron la serie del Expert Advisor automático, voy a dar una rápida explicación aquí de lo que significa usar esta definición. Cuando usamos esta definición, estamos diciendo que nuestra matriz cubrirá los días de la semana. Pero existe un detalle en esta historia, que es el hecho de que necesitamos añadir una unidad al valor. Esto para que la matriz tenga la longitud adecuada, ya que empieza en CERO. Sin embargo, no podemos tener una matriz con cero elementos. Siempre tenemos que empezar con un valor mayor que cero. Por eso, sumamos una unidad. Si esto no se hiciera, tendríamos una matriz de 6 elementos en vez de 7 como de hecho se esperaría. Otro detalle es que puedes notar que la clase C_ControlOfTime está heredando la clase C_Orders. De esta forma, estaremos ampliando las capacidades de la clase C_Orders. Pero esta ampliación no será de hecho llevada al Expert Advisor, como te darás cuenta a lo largo de las explicaciones que serán dadas. El motivo puede ser mejor comprendido en la serie de artículos sobre el Expert Advisor automático.

Continuando, la próxima cosa a ser vista en el código es el constructor de la clase. Este puede ser visto justo abajo:

C_ControlOfTime(C_Terminal *arg, const ulong magic)
                :C_Orders(arg, magic)
   {
      for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) ZeroMemory(m_InfoCtrl[c0]);
   }

Aquí tenemos dos cosas bastante sencillas de entender. La primera es el hecho de que estamos inicializando la clase C_Orders antes incluso de cualquier código del constructor C_ControlOfTime se haya de hecho ejecutado. La segunda y tal vez bien curiosa es la presencia de un bucle. Pero la cuestión curiosa no es el bucle en sí, sino la forma como trabaja. Observen que estamos usando una enumeración en este bucle, y que ella comienza en SUNDAY, y la variable será incrementada hasta el SATURDAY. ¿Pero es posible esto? Sí, podemos hacer esto. Pero tenemos que tomar algunos cuidados. Básicamente, el principal cuidado es que SUNDAY debe tener un valor menor que SATURDAY. Si esto no es un hecho, tendremos problemas al ejecutar el acceso a los datos de la matriz. Afortunadamente, la enumeración comienza en SUNDAY y va progresando, día a día, hasta SATURDAY, dándonos así los días de la semana.

La principal ventaja de usar tal modo de programación es que esto eleva el nivel de nuestro código, permitiendo que el lenguaje MQL5 tenga una apariencia muy cercana al lenguaje natural. Este tipo de enfoque fue bastante aprovechado y explicado en la serie de artículos sobre el Expert Advisor automático. Además, es claro que di una pincelada aquí en esta serie también. Ve los artículos anteriores y encontrarás cómo hacer el código aún más legible. Este tipo de práctica hace que tu código pueda ser comprendido incluso por aquellos con poco conocimiento en programación. Recuerda: no programas para la máquina, programas para otros programadores.

Muy bien, ahora vamos a dar un rápido vistazo a otros dos procedimientos, comenzando con el que puede ser visto justo a continuación.

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
   {
      string szRes[], sz1[];
      bool bLocal;
                                
      if (_LastError != ERR_SUCCESS) return;
      if ((index > SATURDAY) || (index < SUNDAY)) return;
      if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
      {
         m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
         m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
         bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
         for (char c0 = 0; (c0 <= 1) && (bLocal); c0++)
            if (bLocal = (StringSplit(szRes[0], ':', sz1) == 2))
               bLocal = (StringToInteger(sz1[0]) <= 23) && (StringToInteger(sz1[1]) <= 59);
         if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
      }
      if ((_LastError != ERR_SUCCESS) || (!bLocal))
      {
         Print("Error in the declaration of the time of day: ", EnumToString(index));
         ExpertRemove();
      }
   }

Este procedimiento arriba fue explicado y diseccionado en el artículo Cómo construir un EA que opere automáticamente (Parte 10): Automatización (II), donde expliqué detalladamente cómo funciona. Si deseas más detalles sobre el mismo o en caso de que no estés comprendiendo el código, echa un vistazo a este artículo, pues valdrá mucho la pena. El próximo procedimiento, que se ve a continuación, también fue explicado en el mismo artículo. De hecho, todo el trabajo hecho aquí en la clase C_ControlOfTime puede ser visto y comprendido en el artículo mencionado arriba. La única diferencia estaba en el constructor. Pero esto se debe a lo que vamos a hacer después.

virtual const bool CtrlTimeIsPassed(void) final
   {
      datetime dt;
      MqlDateTime mdt;
                                
      TimeCurrent(mdt);
      dt = (mdt.hour * 3600) + (mdt.min * 60);
      return ((m_InfoCtrl[mdt.day_of_week].Init <= dt) && (m_InfoCtrl[mdt.day_of_week].End >= dt));
   }

Con esto explicado, y en caso de que hayas visto el artículo mencionado, te darás cuenta de que el Expert Advisor recibirá algunos parámetros extras, para que el usuario pueda configurar esta clase C_ControlOfTime. Pero necesitamos llevar esto a una nueva clase. Así como fue hecho en la serie sobre el Expert Advisor automático, usaremos una clase intermediaria. Y este es el tema para el próximo apartado.


C_Manager: La clase «administrador».

C_Manager, de hecho, es una clase bastante interesante, ya que hará que gran parte de la complejidad involucrada en el código del Expert Advisor quede aislada del mismo. Es decir, en el Expert Advisor tendremos solo algunas pocas líneas de código, y todo será gestionado y manipulado por las clases. Diferente del código que puede verse en la serie del Expert Advisor automático. Aquí vamos a empezar con algo un poco más básico, pero no por ello menos interesante de ser visto y explicado. La gran cuestión aquí es que el Expert Advisor no va a funcionar de hecho en todos los tipos de escenarios. Inicialmente, nos enfocaremos en hacer que el Expert Advisor trabaje de la manera más segura posible. Ya que luego necesitaremos hacer que pueda trabajar también en el sistema de repetición/simulación. Y esta es la parte complicada del sistema.

Para evitar que la cosa se vuelva extremadamente compleja, vamos a hacer que el Expert Advisor trabaje primero con todo y cualquier soporte que la plataforma MetaTrader 5 pueda ofrecernos. Y esto hace que el sistema de órdenes no sea de hecho tan amplio así. No pudiendo ser usado, por ejemplo, en un modo CrossOrder, es decir, tú no podrás usar el sistema de órdenes, en este primer momento, con el fin de enviar órdenes usando un activo diferente de aquel con el que se esté ejecutando Expert Advisor. Esto indicaría que estaríamos haciendo uso del sistema de órdenes cruzadas.

Pero vamos a ver el código de esta clase C_Manager en el actual estado de desarrollo. El código comienza con las siguientes líneas:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_ControlOfTime.mqh"
#include "..\System EA\Auxiliar\Study\C_Study.mqh"
//+------------------------------------------------------------------+
#define def_Prefix "Manager"
#define def_LINE_PRICE  def_Prefix + "_PRICE"
#define def_LINE_TAKE   def_Prefix + "_TAKE"
#define def_LINE_STOP   def_Prefix + "_STOP"
//+------------------------------------------------------------------+
#define def_AcessTerminal       (*Terminal)
#define def_InfoTerminal        def_AcessTerminal.GetInfoTerminal()
#define def_AcessMouse          (*Study)
#define def_InfoMouse           def_AcessMouse.GetInfoMouse()
//+------------------------------------------------------------------+

Aquí tenemos la declaración de algunas definiciones con el fin de facilitar nuestra codificación posterior. Muy probablemente, estas definiciones sufrirán algún tipo de cambio futuro, dado el hecho del sistema ser bastante complejo.

Pero vamos a ver cómo la clase se inicia. Podemos ver esto observando el siguiente fragmento:

class C_Manager : public C_ControlOfTime
{
   private :
      struct st00
      {
         double  FinanceStop,
                 FinanceTake;
         uint    Leverage;
         bool    IsDayTrade,
                 AccountHedging;                                         
      }m_Infos;
      struct st01
      {
         color   corPrice,
                 corTake,
                 corStop;
         bool    bCreate;
      }m_Objects;             
//+------------------------------------------------------------------+
      C_Terminal *Terminal;
      C_Study    *Study;

Aquí tenemos la declaración de las variables globales privadas de la clase. Para una mejor organización, usé y dividí las cosas en términos de estructuras. De esta manera, será más simple modificar y remover partes del código después. Con toda seguridad, esto sucederá en el transcurso de los próximos artículos. Por ahora, vamos a intentar hacer que el sistema funcione en su forma más simple y utilizando al máximo el auxilio de MetaTrader 5. Estoy repitiendo esto, porque no debes tomar este sistema como definitivo. Aún está siendo diseñado.

Vamos a ver el primer procedimiento presente en la clase C_Manager. Este puede ser visto en el código inmediatamente abajo:

bool CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
   {
      ulong tmp;
                                
      if (!CtrlTimeIsPassed()) return false;
      tmp = C_Orders::CreateOrder(type, Price, m_Infos.FinanceStop, m_Infos.FinanceTake, m_Infos.Leverage, m_Infos.IsDayTrade);
                                
      return tmp > 0;
   }

Lo que este procedimiento hace es enviar una orden pendiente al servidor de negociación. Pero atención, la orden solo será enviada si el sistema de control de tiempo habilita el envío. Si esto no es posible debido al hecho de estar fuera de la franja de tiempo permitida, la orden no será enviada. Observen el hecho de que necesitamos solo informar el precio al que la orden será colocada. Tanto si compramos como si vendemos, todo lo demás se rellena como indican las variables globales dentro de la clase.

La próxima rutina que tenemos, y esta también es una rutina privativa de la clase, se ve a continuación:

bool ToMarket(const ENUM_ORDER_TYPE type)
   {
      ulong tmp;
                                
      if (!CtrlTimeIsPassed()) return false;
      tmp = C_Orders::ToMarket(type, m_Infos.FinanceStop, m_Infos.FinanceTake, m_Infos.Leverage, m_Infos.IsDayTrade);
                                
      return tmp > 0;
   }

Aquí, estamos enviando una solicitud de ejecución a precio de mercado, al mejor precio disponible. En este caso, todo lo que necesitamos informar es si estaremos comprando o vendiendo. Todo el resto es completado con base en la información de las variables globales de la clase. Pero podrías estar pensando: ¿Cómo puedo inicializar estas variables globales? ¿Se hará esto dentro del código del Expert Advisor? La respuesta es un rotundo: NO. Las variables globales presentes en la clase no deben, de ninguna manera, ser accedidas sin el debido cuidado y conocimiento de la clase a la cual pertenecen.

Para inicializar tales variables, contamos con algunos medios y recursos. Por el momento, usaremos el método más simple de todos: el constructor. Hacer uso del constructor para inicializar las variables de la clase es, sin duda, el mejor camino de todos. Pero después cambiaremos esto por cuestiones de practicidad. Por ahora, esto servirá y hará lo que necesitamos que se haga. Entonces el código del constructor puede ser visto a continuación:

C_Manager(C_Terminal *arg1, C_Study *arg2, color cPrice, color cStop, color cTake, const ulong magic, const double FinanceStop, const double FinanceTake, uint Leverage, bool IsDayTrade)
          :C_ControlOfTime(arg1, magic)
   {
      string szInfo = "HEDGING";
                                
      Terminal = arg1;
      Study = arg2;
      if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (CheckPointer(Study) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (_LastError != ERR_SUCCESS) return;
      m_Infos.FinanceStop     = FinanceStop;
      m_Infos.FinanceTake     = FinanceTake;
      m_Infos.Leverage        = Leverage;
      m_Infos.IsDayTrade      = IsDayTrade;
      m_Infos.AccountHedging  = false;
      m_Objects.corPrice      = cPrice;
      m_Objects.corStop       = cStop;
      m_Objects.corTake       = cTake;
      m_Objects.bCreate       = false;
      switch ((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE))
      {
         case ACCOUNT_MARGIN_MODE_RETAIL_HEDGING: m_Infos.AccountHedging = true; break;
         case ACCOUNT_MARGIN_MODE_RETAIL_NETTING: szInfo = "NETTING";            break;
         case ACCOUNT_MARGIN_MODE_EXCHANGE      : szInfo = "EXCHANGE";           break;
      }
      Print("Detected Account ", szInfo);
   }

A pesar de parecer ser largo, este código en realidad es súper simple, dado el hecho de que estaremos básicamente inicializando todas las variables globales presentes en la clase. Toda esta inicialización es, de hecho, dada con base en los parámetros informados por el código del Expert Advisor. Pero no nos quedamos solo en esto, aprovechamos el hecho de estar inicializando el sistema y verificamos en qué tipo de cuenta se está ejecutando el Expert Advisor. Esto será importante después; por ahora, esto nos sirve solo como información de análisis, de manera que el usuario sepa qué tipo de cuenta se está utilizando. Esto es informado en la caja de herramientas de la plataforma MetaTrader 5, usando el mensaje creado en esta línea.

También tenemos un destructor; este puede ser visto abajo:

~C_Manager()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
   }

Y a pesar de que este código contiene esta línea, la misma difícilmente va a hacer algo. Pero no podemos simplemente esperar que todo suceda sin problemas. Entonces, si algún problema ocurre, este destructor va a remover los objetos que esta clase está creando.


Conclusión

Tú puedes haber notado que el conocimiento es reutilizable. No creamos de hecho algo de la nada. Estamos siempre reutilizando y perfeccionando las cosas. Por ello, las mismas van mejorando con el tiempo. Pero aún falta un procedimiento para ser explicado en la clase C_Manager. Esto en el actual estadio de desarrollo en el que se encuentra, así como el código del Expert Advisor. Pero para dar una explicación más adecuada y, principalmente, no dejar este artículo de aquí largo y agotador de leer y comprender, reservaré el próximo artículo para hablar sobre el procedimiento y el código del Expert Advisor.

Y como aún no fue totalmente explicado el código y no deseo que tú acabes utilizando algo sin de hecho saber de qué se trata, en este artículo no habrá ningún código en anexo. Lo siento, pero no creo que sea correcto hacer lo contrario.


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

Archivos adjuntos |
Anexo.zip (130.63 KB)
Creamos un asesor multidivisa sencillo utilizando MQL5 (Parte 3): Prefijos/sufijos de símbolos y sesión comercial Creamos un asesor multidivisa sencillo utilizando MQL5 (Parte 3): Prefijos/sufijos de símbolos y sesión comercial
Últimamente, he recibido comentarios de varios compañeros tráders sobre cómo usar el asesor multidivisa que estamos analizando con brókeres que utilizan prefijos y/o sufijos con nombres de símbolos, así como sobre la forma de implementar zonas horarias comerciales o sesiones comerciales en el asesor.
Redes neuronales: así de sencillo (Parte 62): Uso del transformador de decisiones en modelos jerárquicos Redes neuronales: así de sencillo (Parte 62): Uso del transformador de decisiones en modelos jerárquicos
En artículos recientes, hemos visto varios usos del método Decision Transformer, que permite analizar no solo el estado actual, sino también la trayectoria de los estados anteriores y las acciones realizadas en ellos. En este artículo, veremos una variante del uso de este método en modelos jerárquicos.
Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con Integración RestAPI (Parte 1): Como usar RestAPIs en MQL5 Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con Integración RestAPI (Parte 1): Como usar RestAPIs en MQL5
Este artículo aborda la importancia de las APIs (application programming interface) en la comunicación entre diferentes aplicaciones y sistemas de software. En él, se destaca el papel de las API a la hora de simplificar la interacción entre aplicaciones, ya que les permiten compartir datos y funcionalidades de forma eficiente.
Algoritmos de optimización de la población: Algoritmo de búsqueda de sistema cargado (Charged System Search, CSS) Algoritmos de optimización de la población: Algoritmo de búsqueda de sistema cargado (Charged System Search, CSS)
En este artículo, analizaremos otro algoritmo de optimización inspirado en la naturaleza inanimada: el algoritmo de búsqueda de sistema cargado (CSS). El objetivo de este artículo es presentar un nuevo algoritmo de optimización basado en los principios de la física y la mecánica.