Descargar MetaTrader 5

Recetas MQL5 - órdenes ОСО

18 septiembre 2015, 17:02
Dennis Kirichenko
0
431

Introducción

En el artículo, se hablará del trabajo con el tipo de conexión de órdenes OCO. Este mecanismo ha sido implementado en ciertos terminales comerciales que compiten con MetaTrader 5. Partiendo del ejemplo de creación de un asesor que dispone de un panel para el procesamiento de órdenes ОСО, persigo dos objetivos. Por una parte, deseo arrojar luz sobre las posibilidades de la Biblioteca Estándar, por otra, ampliar el instrumental del trader.


1. Esencia de las órdenes ОСО

Las órdenes ОСО (one-cancels-the-other order) son la unión de una pareja de órdenes pendientes.

Están unidas entre sí por una función de cancelación mutua: si se activa la primera, entonces no se debe activar la segunda, y al contrario.

Fig. 1. Conexión de órdenes OCO

Fig. 1. Conexión de órdenes OCO

En la fig. 1 se muestra un esquema sencillo de la interdependencia entre órdenes. En él se muestra una definición esencial: la conexión existe mientras existan ambas órdenes. Desde el punto de la lógica de cualquier [una] orden de la pareja, existen condiciones imprencisdibles, pero insuficientes para la existencia de la unión.

En ciertas fuentes se hace notar que en la conexión, obligatoriamente una de las órdenes es de límite, en otras, de stop, además, las órdenes deberán tener una dirección (compra o venta). Mi punto de vista es que semejante limitación no posibilita la creación de estrategias comerciales flexibles. Propongo estudiar en la conexión diferentes órdenes OCO y, lo más importante, intentar programar esta conexión.


2. Programando las conexiones de órdenes

Para programar tareas relacionadas con el control sobre órdenes OCO, a mi entender, viene muy bien el instrumental de POO.

En los siguientes apartados veremos las nuevas clases de tipos de datos que nos servirán para nuestros objetivos. En primer lugar, tenemos la clase CiOcoObject.


2.1. Clase CiOcoObject

Y bien, hay que pensar algún objeto de programación que sea responsable de la gestión y el control de las dos órdenes conectadas.

Crearemos un nuevo objeto de manera tradicional, utilizando como pilar la clase básica familiar para MQL5 CObject, .

Entonces, la nueva clase puede tener el aspecto siguiente:

//+------------------------------------------------------------------+
//| Class CiOcoObject                                                |
//| Purpose: a class for OCO-orders                                  |            
//+------------------------------------------------------------------+
class CiOcoObject : public CObject
  {
   //--- === Data members === --- 
private:
   //--- incidencias de la conexión
   ulong             m_order_tickets[2];
   //--- bandera de la inicialización
   bool              m_is_init;
   //--- id
   uint              m_id;

   //--- === Methods === --- 
public:
   //--- constructor/destructor
   void              CiOcoObject(void){m_is_init=false;};
   void             ~CiOcoObject(void){};
   //--- constructor de copia
   void              CiOcoObject(const CiOcoObject &_src_oco);
   //--- operador de asignación
   void              operator=(const CiOcoObject &_src_oco);

   //--- inicialización/desinicialización
   bool              Init(const SOrderProperties &_orders[],const uint _bunch_cnt=1);
   bool              Deinit(void);
   //--- obtener id
   uint              Id(void) const {return m_id;};

private:
   //--- tipos de órdenes
   ENUM_ORDER_TYPE   BaseOrderType(const ENUM_ORDER_TYPE _ord_type);
   ENUM_BASE_PENDING_TYPE PendingType(const ENUM_PENDING_ORDER_TYPE _pend_type);
   //--- establecer id
   void              Id(const uint _id){m_id=_id;};
  };

Cada conexión de las órdenes OCO tendrá su propio identificador. Su valor se crea mediante el llamamiento al generador de cifras aleatorias (al objeto de clase CRandom).

Desde el punto de vista del interfaz, resultan interesantes los métodos de inicialización y desinicialización de la conexión. El primero crea (inicializa) la conexión, y el segundo la elimina (desinicializa).

Como argumento, el método CiOcoObject::Init() adopta la matriz de estructuras del tipo SOrderProperties. Este tipo de estructuras supone en sí mismo las propiedades de la orden que entra en la conexión, es decir, la orden OCO.


2.2 Estructura SOrderProperties

Veamos los componentes de los campos de la estructura designada más arriba:

//+------------------------------------------------------------------+
//| Estructura de propiedades de la orden                                         |
//+------------------------------------------------------------------+
struct SOrderProperties
  {
   string            symbol;           // símbolo
   ENUM_PENDING_ORDER_TYPE order_type; // tipo de orden
   double            volume;           // volumen de la orden
   uint              price_offset;     // retroceso para el precio de ejecución, en pips
   uint              limit_offset;     // retroceso para el precio límite, en pips
   uint              sl;               // stop loss, en pips
   uint              tp;               // take profit, en pips
   ENUM_ORDER_TYPE_TIME type_time;     // tipo según expiración
   datetime          expiration;       // expiración
   string            comment;          // comentarios
  };

Y bien, para que el método de inicialización funcione, hay que rellenar antes la matriz de las estructuras de los dos elementos. Dicho de fomra más sencilla, hay que indicar al programa qué órdenes deberá emitir.

En la estructura se usa una enumeración del tipo ENUM_PENDING_ORDER_TYPE:


//+------------------------------------------------------------------+
//| Tipo de orden pendiente                                           |
//+------------------------------------------------------------------+
enum ENUM_PENDING_ORDER_TYPE
  {
   PENDING_ORDER_TYPE_BUY_LIMIT=2,       // Buy-limit
   PENDING_ORDER_TYPE_SELL_LIMIT=3,      // Sell-limit
   PENDING_ORDER_TYPE_BUY_STOP=4,        // Buy-stop
   PENDING_ORDER_TYPE_SELL_STOP=5,       // Sell-stop
   PENDING_ORDER_TYPE_BUY_STOP_LIMIT=6,  // Buy-stop-limit
   PENDING_ORDER_TYPE_SELL_STOP_LIMIT=7, // Sell-stop-limit
  };

En general, lo que hace es doblar la enumeración estándar ENUM _ORDER_TYPE, pero con su ayuda se pueden elegir categóricamente solo órdenes pendientes, o para ser más exactos, solo tipos de estas órdenes.

Entonces, al iniciar el programa y elegir el tipo de orden correspondiente en los parámetros de entrada, no habrá errores (fig.2).


Fig.2 Campo "Tipo" con la lista desplegable de los tipos posibles de orden

Fig. 2. Campo "Tipo" con la lista desplegable de los posibles tipos de orden


Si usamos la enumeración estándar ENUM _ORDER_TYPE, entonces se podría establecer el tipo de orden de mercado ORDER_TYPE_BUY o ORDER_TYPE_SELL, lo cual es totalmente innecesario, ya que el trabajo se realiza con órdenes pendientes.


2.3. Inicialización de la conexión

Como ya se destacó con anterioridad, de la inicialización de la conexión de órdenes se ocupa el método CiOcoObject::Init().

En esencia, se encarga de emitir la propia conexión de las órdenes y registra el éxito o el fracaso de la aparición de la nueva conexión. Yo diría que se trata de un método activo, ya que realiza operaciones comerciales por sí mismo. Si se desea, es posible crear un método pasivo. Sencillamente conectará entre sí órdenes pendientes ya activas, que fueron emitidas independientemente la una de la otra.

No voy a mostrar el código del método al completo. Destacaré que aquí es importante calcular correctamente todos los precios (apertura, stop, profit, límite), para que el método de la clase comercial CTrade::OrderOpen() ejecute la orden comercial. Para esto hay que tener en cuenta 2 momentos: la dirección de la orden (compra o venta) y la ubicación del precio de ejecución de la orden con respecto al actual (más alto o más bajo).

Este método llama un par de métodos privados: BaseOrderType() y PendingType(). El primero determina la dirección de la orden, el segundo, el tipo de orden pendiente.

Si la orden está ubicada, entonces en la matriz m_order_tickets[] se introduce su incidencia.

Para poner a prueba este método, he usado el sencillo script Init_OCO.mq5.

#property script_show_inputs
//---
#include "CiOcoObject.mqh"
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
sinput string Info_order1="+===--Orden 1--====+";   // +===--Orden 1--====+
input ENUM_PENDING_ORDER_TYPE InpOrder1Type=PENDING_ORDER_TYPE_SELL_LIMIT; // Tipo
input double InpOrder1Volume=0.02;                  // Volumen
input uint InpOrder1PriceOffset=125;                // Retraso para los precios de ejecución, en pips
input uint InpOrder1LimitOffset=50;                 // Retraso para los precios de límite, en pips
input uint InpOrder1SL=250;                         // Stop-loss, en pips
input uint InpOrder1TP=455;                         // Profit, en pips
input string InpOrder1Comment="Orden OCO 1";        // Comentarios
//---
sinput string Info_order2="+===--Orden 2--====+";   // +===--Orden 2--====+
input ENUM_PENDING_ORDER_TYPE InpOrder2Type=PENDING_ORDER_TYPE_SELL_STOP; // Tipo
input double InpOrder2Volume=0.02;                  // Volumen    
input uint InpOrder2PriceOffset=125;                // Retraso para los precios de ejecución, en pips
input uint InpOrder2LimitOffset=50;                 // Retraso para los precios de límite, en pips
input uint InpOrder2SL=275;                         // Stop-loss, en pips
input uint InpOrder2TP=300;                         //  Profit, en pips
input string InpOrder2Comment="Orden OCO 2";        // Comentarios

//--- globals
CiOcoObject myOco;
SOrderProperties gOrdersProps[2];
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- propiedades de la 1 orden
   gOrdersProps[0].order_type=InpOrder1Type;
   gOrdersProps[0].volume=InpOrder1Volume;
   gOrdersProps[0].price_offset=InpOrder1PriceOffset;
   gOrdersProps[0].limit_offset=InpOrder1LimitOffset;
   gOrdersProps[0].sl=InpOrder1SL;
   gOrdersProps[0].tp=InpOrder1TP;
   gOrdersProps[0].comment=InpOrder1Comment;

//--- propiedades de la 2 orden
   gOrdersProps[1].order_type=InpOrder2Type;
   gOrdersProps[1].volume=InpOrder2Volume;
   gOrdersProps[1].price_offset=InpOrder2PriceOffset;
   gOrdersProps[1].limit_offset=InpOrder2LimitOffset;
   gOrdersProps[1].sl=InpOrder2SL;
   gOrdersProps[1].tp=InpOrder2TP;
   gOrdersProps[1].comment=InpOrder2Comment;

//--- inicialización de la conexión
   if(myOco.Init(gOrdersProps))
      PrintFormat("Id de la nueva conexión OCO: %I32u",myOco.Id());
   else
      Print("¡Error en la emisión de la conexión OCO!");
  }

En este se pueden establecer diferentes propiedades de las futuras órdenes de la conexión. En total, en MetaTrader 5, existen 6 tipos diferentes de órdenes pendientes .

Entonces pueden darse 15 variantes (combinaciones) de conexiones (con la condición de que los tipos de orden en la conexión sean diferentes).

C(k,N) = C(2,6) = 15

Con ayuda del script fueron puestas a prueba todas las combinaciones. Pondré un ejemplo para la conexión "Buy-stop - Buy-stop-limit".

En los parámetros del script, hay que indicar claramente los tipos de orden (fig.3).


Fig.3 Conexión de una orden del tipo "buy-stop" con una orden del tipo "buy-stop-limit"

Fig. 3. Conexión de una orden del tipo "buy-stop" con una orden del tipo "buy-stop-limit"

En la revista "Expertos" aparecerá esta información:

QO      0       17:17:41.020    Init_OCO (GBPUSD.e,M15) Código del resultado de la ejecución del requerimiento: 10009
JD      0       17:17:41.036    Init_OCO (GBPUSD.e,M15) Incidencia de la nueva orden: 24190813
QL      0       17:17:41.286    Init_OCO (GBPUSD.e,M15) Código del resultado de la ejecución del requerimiento: 10009
JH      0       17:17:41.286    Init_OCO (GBPUSD.e,M15) Incidencia de la nueva orden: 24190814
MM      0       17:17:41.379    Init_OCO (GBPUSD.e,M15) Id de la nueva conexión OCO: 3782950319

Sin embargo, con ayuda del script, no podremos trabajar al completo con órdenes OCO, si no recurrimos a los ciclos.


2.4. Desinicialización de la conexión

Este método es responsable del control de la conexión de las órdenes. En cuanto cualquiera de las órdenes deje de encontrarse en la lista de activas, entonces la conexión "morirá".

Supongo que en el código del asesor es conveniente ubicar este método en los procesadores OnTrade() o OnTradeTransaction(). De esta forma, el asesor puede procesar de manera operativa la activación de cualquier orden de conexión.

//+------------------------------------------------------------------+
//| Desinicialización de la conexión                                 |
//+------------------------------------------------------------------+
bool CiOcoObject::Deinit(void)
  {
//--- si la conexión está incializada
   if(this.m_is_init)
     {
      //--- comprobar las órdenes 
      for(int ord_idx=0;ord_idx<ArraySize(this.m_order_tickets);ord_idx++)
        {
         //--- orden de conexión actual
         ulong curr_ord_ticket=this.m_order_tickets[ord_idx];
         //--- otra orden de conexión
         int other_ord_idx=!ord_idx;
         ulong other_ord_ticket=this.m_order_tickets[other_ord_idx];

         //---
         COrderInfo order_obj;

         //--- si no existe orden actual
         if(!order_obj.Select(curr_ord_ticket))
           {
            PrintFormat("La orden #%d no existe en la lista de órdenes activas.",curr_ord_ticket);
            //--- intento de eliminar otra orden                 
            if(order_obj.Select(other_ord_ticket))
              {
               CTrade trade_obj;
               //---
               if(trade_obj.OrderDelete(other_ord_ticket))
                  return true;
              }
           }
        }
     }
//---
   return false;
  }

Llamo la atención sobre el siguiente detalle. En el cuerpo del método de la clase, se comprueba la bandera de inicialización de la conexión. Si la bandera está quitada, entonces no se realizará el intento de comprobar la orden. Este método no permite eliminar 1 orden activa cuando otra no ha sido emitida aún.

Ampliamos las posibilidades del script en el que se emitió un par de órdenes. Con este objetivo, creamos el asesor de prueba Control_OCO_EA.mq5.

En general, el asesor se diferenciará del script solo en el hecho de que en su código estará presente un bloque de procesamiento del evento Trade():

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- desinicialización de la conexión OCO
   if(myOco.Deinit())
     {
      Print("¡La conexión de órdenes ya no existe!");
      //--- desechar la conexión
      CiOcoObject new_oco;
      myOco=new_oco;
     }
  }

En el vídeo se puede ver cómo ambos programas funcionan en el terminal MetaTrader 5.



Sin embargo, en ambos programas de prueba podemos encontrar defectos.

El primer programa (script) puede crear solo de una forma activa una conexión, pero luego pierde el control sobre ella.

El segundo programa (asesor), aunque controle la conexión, no puede crear otras conexiones después de una primera creación. Para que un programa (asesor), que trabaje con órdenes OCO, esté completo, a su arsenal debemos añadir la posibilidad de que emita las propias órdenes. Probemos a hacerlo en el siguiente apartado.


3. Asesor-controlador

Para emitir y ajustar los parámetros de las órdenes de la conexión, crearemos en el gráfico el Panel "Gestión de órdenes OCO".

Este entrará en los componentes del asesor-controlador (fig.4). El código fuente se adjunta en el archivo del asesor Panel_OCO_EA.mq5.


Fig.4 Panel para la creación de órdenes OCO

Fig. 4. Panel para la creación de órdenes OCO - estado inicial


Para emitir una conexión de órdenes OCO, hay que elegir el tipo para la futura orden y rellenar los campos.

Entonces, el único botón en el panel cambiará su inscripción (propiedad de texto, fig.5).


Fig.5 Panel para la creación de órdenes OCO - nueva conexión

Fig. 5. Panel para la creación de órdenes OCO - nueva conexión


¿Los objetos de qué clases se han implicado para crear el Panel?

Se trata de las clases de la Biblioteca estándar:

  • CAppDialog - diálogo principal de la aplicación;
  • CPanel - etiqueta rectangular;
  • CLabel - etiqueta de texto;
  • CComboBox - campo con lista desplegable;
  • CEdit - campo de edición;
  • CButton - botón.

Naturalmente, se han llamado de forma automática los métodos de las clases-parentales.

Y ahora, sobre el código. Hay que decir que la parte de la Biblioteca estándar que está reconducida para las necesidades derivadas de la creación de paneles de indicaciones y diálogos de gestión, es bastante amplia.

Por ejemplo, para captar el evento de cierre de la lista desplegable, se deberá mirar a fondo en la pila de llamadas (fig. 6).


Fig.6 Pila de llamadas

Fig. 6. Pila de llamadas


Para los eventos concretos, el procesador establece sus macros y la anotación en el archivo %MQL5\Include\Controls\Defines.mqh.

Con objeto de crear las conexiones OCO, he inventado el evento personalizado ON_OCO.

#define ON_OCO (101) // evento "creación de conexión ОСО" 

Todo el trabajo de rellenado de los parámetros de las órdenes futuras y la generación de conexiones tiene lugar en el cuerpo del procesador OnChartEvent().

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- procesamiento de todos los eventos del gráfico con el diálogo principal
   myDialog.ChartEvent(id,lparam,dparam,sparam);

//--- procesamiento de las listas desplegables
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE)
     {
      //--- si se trata de la lista del Panel
      if(!StringCompare(StringSubstr(sparam,0,7),"myCombo"))
        {
         static ENUM_PENDING_ORDER_TYPE prev_vals[2];
         //--- índice de la lista
         int combo_idx=(int)StringToInteger(StringSubstr(sparam,7,1))-1;

         ENUM_PENDING_ORDER_TYPE curr_val=(ENUM_PENDING_ORDER_TYPE)(myCombos[combo_idx].Value()+2);
         //--- recordar el cambio del tipo de orden
         if(prev_vals[combo_idx]!=curr_val)
           {
            prev_vals[combo_idx]=curr_val;
            gOrdersProps[combo_idx].order_type=curr_val;
           }
        }
     }

//--- procesamiento de los campos de edición
   else if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      //--- si se trata del campo de edición del Panel
      if(!StringCompare(StringSubstr(sparam,0,6),"myEdit"))
        {
         //--- encontrar objeto
         for(int idx=0;idx<ArraySize(myEdits);idx++)
           {
            string curr_edit_obj_name=myEdits[idx].Name();
            long curr_edit_obj_id=myEdits[idx].Id();
            //--- si los nombres coinciden
            if(!StringCompare(sparam,curr_edit_obj_name))
              {
               //--- obtener el valor actual del campo
               double value=StringToDouble(myEdits[idx].Text());
               //--- determinar el índice de la matriz gOrdersProps[]
               int order_num=(idx<gEditsHalfLen)?0:1;
               //--- determinar el número del campo de la estructura gOrdersProps
               int jdx=idx;
               if(order_num)
                  jdx=idx-gEditsHalfLen;
               //--- rellenar el campo de la estructura gOrdersProps
               switch(jdx)
                 {
                  case 0: // volumen
                    {
                     gOrdersProps[order_num].volume=value;
                     break;
                    }
                  case 1: // ejecución
                    {
                     gOrdersProps[order_num].price_offset=(uint)value;
                     break;
                    }
                  case 2: // límite
                    {
                     gOrdersProps[order_num].limit_offset=(uint)value;
                     break;
                    }
                  case 3: // stop
                    {
                     gOrdersProps[order_num].sl=(uint)value;
                     break;
                    }
                  case 4: // profit
                    {
                     gOrdersProps[order_num].tp=(uint)value;
                     break;
                    }
                 }
              }
           }
         //--- bandera de creación de una conexión OCO
         bool is_to_fire_oco=true;
         //--- comprobar el rellenado de las estructuras 
         for(int idx=0;idx<ArraySize(gOrdersProps);idx++)
           {
            //---  si se ha establecido el tipo de orden 
            if(gOrdersProps[idx].order_type!=WRONG_VALUE)
               //---  si se ha establecido el volumen  
               if(gOrdersProps[idx].volume!=WRONG_VALUE)
                  //---  si se ha establecido el precio de ejecución
                  if(gOrdersProps[idx].price_offset!=(uint)WRONG_VALUE)
                     //---  si se ha establecido el retraso para el precio límite
                     if(gOrdersProps[idx].limit_offset!=(uint)WRONG_VALUE)
                        //---  si se ha establecido el stop loss
                        if(gOrdersProps[idx].sl!=(uint)WRONG_VALUE)
                           //---  si se ha establecido el take profit
                           if(gOrdersProps[idx].tp!=(uint)WRONG_VALUE)
                              continue;

            //--- reiniciar bandera de creación de la conexión OCO 
            is_to_fire_oco=false;
            break;
           }
         //--- ¿crear una conexión OCO?
         if(is_to_fire_oco)
           {
            //--- rellenar los campos de comentarios
            for(int ord_idx=0;ord_idx<ArraySize(gOrdersProps);ord_idx++)
               gOrdersProps[ord_idx].comment=StringFormat("OCO Order %d",ord_idx+1);
            //--- cambiar las propiedades del botón
            myButton.Text("Nueva conexión");
            myButton.Color(clrDarkBlue);
            myButton.ColorBackground(clrLightBlue);
            //--- responder a las acciones del usuario 
            myButton.Enable();
           }
        }
     }
//--- procesamiento del click en el botón
   else if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- si se trata del botón de creación de una conexión OCO
      if(!StringCompare(StringSubstr(sparam,0,6),"myFire"))
         //--- si se responde a las acciones del usuario
         if(myButton.IsEnabled())
           {
            //--- generar el evento "creación de una conexión OCO"
            EventChartCustom(0,ON_OCO,0,0.0,"OCO_fire");
            Print("Ha llegado la orden de crear una nueva conexión.");
           }
     }
//--- procesamiento de la orden de inicialización de la nueva conexión 
   else if(id==CHARTEVENT_CUSTOM+ON_OCO)
     {
      //--- inicialización de la conexión OCO
      if(gOco.Init(gOrdersProps,gOcoList.Total()+1))
        {
         PrintFormat("Id de la nueva conexión OCO: %I32u",gOco.Id());
         //--- copiar la conexión
         CiOcoObject *ptr_new_oco=new CiOcoObject(gOco);
         if(CheckPointer(ptr_new_oco)==POINTER_DYNAMIC)
           {
            //--- añadir a la lista
            int node_idx=gOcoList.Add(ptr_new_oco);
            if(node_idx>-1)
               PrintFormat("Conexiones totales: %d",gOcoList.Total());
            else
               PrintFormat("¡Error al añadir la conexión OCO %I32u a la lista!",gOco.Id());
           }
        }
      else
         Print("¡Error al emitir las órdenes OCO!");

      //--- reestablecer propiedades
      Reset();
     }
  }

El código del procesador no es pequeño. Aquí sería conveniente dividirlo en varios bloques.

Primero se da al diálogo principal el procesamiento de todos los eventos del gráfico.

A continuación, van los bloques de procesamiento de diferentes eventos:

  • El cambio de las listas desplegables es para determinar el tipo de órdenes;
  • La edición de los campos de edición es para rellenar las propiedades de las órdenes;
  • El click en el botón es para la generación del evento ON_OCO;
  • La reacción inmediata al evento ON_OCOes la creación de la conexión de las órdenes.

En el asesor no hay comprobación de la corrección del rellenado de los campos del panel. Por eso es necesario comprobar por nosotros mismos los valores rellenados, ya que de otra forma dará error en la emisión de las órdenes OCO.

En el cuerpo del procesador OnTrade() se comprueba la necesidad de eliminar las conexiones y cierres de la orden restante.


Conclusión

En el artículo he intentado demostrar la riqueza de las clases de la Biblioteca estándar, que puede servir para llevar a cabo tareas específicas.

En concreto, se ha resuelto el problema del procesamiento de las órdenes OCO. Espero que el código del asesor que tiene el Panel para el procesamiento de las órdenes OCO sirva de punto de partida para la creación de conexiones más complejas de órdenes.


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/1582

Archivos adjuntos |
init_oco.mq5 (6.42 KB)
control_oco_ea.mq5 (7.88 KB)
panel_oco_ea.mq5 (30.25 KB)
ciocoobject.mqh (27.71 KB)
crandom.mqh (5.19 KB)
Introducción a la teoría de la Lógica difusa Introducción a la teoría de la Lógica difusa

La lógica difusa extiende los límites habituales de la lógica matemática y teoría de conjuntos. En este artículo se muestran principales principios de esta teoría, así como se describen dos sistemas de inferencia lógica difusa tipo Mamdani y Sugeno. Se dan los ejemplos de implementación de los modelos difusos a base de estos dos sistemas usando los medios de la biblioteca FuzzyNet para MQL5.

Estudiamos la clase CCanvas. Implementación de la transparencia de los objetos gráficos Estudiamos la clase CCanvas. Implementación de la transparencia de los objetos gráficos

¿Está harto del gráfico anguloso de las medias móviles? ¿Quiere usted dibujar en el terminal algo más bonito que un simple rectángulo rellenado? Ahora es posible dibujar de manera vistosa en el terminal. Para ello existe la clase de creación de gráficos personalizados CCanvas. Con ayuda de esta clase, es posible conseguir transparencia, mezclar los colores y alcanzar la ilusión de transparencia con ayuda de la superposición y la mezcla de colores.

Programamos los modos de funcionamiento del Asesor Experto usando la programación orientada a objetos Programamos los modos de funcionamiento del Asesor Experto usando la programación orientada a objetos

En este artículo se considera la idea de la programación multi-modo de los robots comerciales usando el lenguaje MQL5. Se utiliza el enfoque orientado a objetos para la implementación de cada uno de los modos. Se muestra el ejemplo de la jerarquía de las clases de régimen y el ejemplo de las clases para el testeo (prueba). Se supone que la programación multi-modo de los robots comerciales toma en consideración las particularidades de cada modo de trabajo del Asesor Experto MQL5. Para la identificación de los modos se crean las funciones y enumeraciones.

Dibujo de indicadores de aguja usando la clase CCanvas Dibujo de indicadores de aguja usando la clase CCanvas

Los instrumentos indicadores de esfera nos rodean por todas partes: en los coches y aviones, en la industria y en nuestra vida cotidiana. Se aplican cuando se requiere una rápida reacción al valor controlado por parte del operador. En este artículo conoceremos la biblioteca de instrumentos indicadores para MetaTrader 5.