English Русский 中文 Deutsch 日本語 Português
preview
Programación orientada a objetos (OOP) en MQL5

Programación orientada a objetos (OOP) en MQL5

MetaTrader 5Trading | 27 octubre 2023, 08:51
380 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Introducción

Hoy hablaremos de uno de los temas más importantes de la programación, que facilita la escritura de código y permite a los desarrolladores y programadores aplicar el principio DRY (Do Not Repeat Yourself, no te repitas). Además, entre otras cosas, aumentaremos la seguridad de cualquier software creado. Asimismo, hablaremos sobre la programación orientada a objetos (OOP) y cómo podemos utilizar este concepto al trabajar con MQL5 (MetaQuotes Language).

Así, hoy abarcaremos este interesante e importante tema en las siguientes secciones:

El objetivo principal de este artículo es abordar y comprender los fundamentos de la programación orientada a objetos (OOP) en general y cómo se puede utilizar esta en la creación de software. Asimismo, aprenderemos a aplicar este enfoque en MQL5 para crear programas más eficientes y seguros.

Si así lo necesita, podrá encontrar recursos informativos sobre este enfoque, tanto más que resulta aplicable a otros lenguajes de programación como C++, Java, Python y otros. Toda la información necesaria sobre el lenguaje MQL5 se puede encontrar en la documentación oficial.

¡Atención! Toda la información del presente artículo se ofrece «tal cual», únicamente con fines ilustrativos, y no supone ningún tipo de recomendación. El artículo no garantiza ningún resultado en absoluto. Todo lo que ponga en práctica usando este artículo como base, lo hará bajo su propia cuenta y riesgo; el autor no garantiza resultado alguno.


¿Qué es la OOP?

Comenzaremos con la definición de la OOP. La OOP ayuda a crear y desarrollar software reutilizable sin duplicar el trabajo y el código utilizando el concepto DRY (Don't Repite Yourself, no te repitas).

La OOP nos ayuda a acercarnos a la naturaleza del mundo, pues en la vida cotidiana estamos rodeados de objetos. Trataremos con objetos y clases: por objeto entenderemos un ejemplar de una clase, mientras que por clase entenderemos una plantilla para un objeto. Dentro de la plantilla, que es una clase, definiremos detalladamente el comportamiento del objeto.

Conceptos básicos de la OOP:

Al utilizar la OOP en el desarrollo, se aplican los siguientes principios.

  1. Encapsulación.
  2. Abstracción.
  3. Herencia.
  4. Polimorfismo.

OOP

1. Encapsulación:

La encapsulación es un método que nos permite asociar funciones y datos en una clase; los datos y funciones en una clase pueden ser privados (accesibles solo dentro de la clase) o públicos (accesibles fuera de la clase). La encapsulación nos ayuda a ocultar la complejidad de la implementación de la clase y ofrece al desarrollador un control total sobre sus datos, lo cual ayuda a realizar un seguimiento de todos los valores dependientes sin conflictos.

La encapsulación nos permite mantener la funcionalidad de nuestro sistema y evitar muchos posibles errores, y también ofrece un alto nivel de control para el desarrollador, simplificando además las pruebas y el procesamiento de datos de las clases sin tocar ni cambiar todo el código del software. También ayuda a eliminar errores y simplificar el código.

La siguiente figura describe el concepto de encapsulación:

Encapsulación

2. Abstracción:

La abstracción es un método que sirve para ocultar detalles innecesarios y presentar solo los esenciales. Resulta más amplio que el concepto de encapsulación, pero ayuda a lograr el mismo objetivo: proteger los datos e implementar funciones sin conocer al completo el proceso de implementación de todas las clases, es decir, usando solo con una comprensión de lo que se debe hacer para completar la implementación.

Para lograr este objetivo necesitaremos incluir dos métodos importantes: interfaz e implementación. Una interfaz es un método que permite que las clases se comuniquen entre sí, mientras que una implementación es un método que contiene todos los detalles del código o la lógica de las clases. Así, la abstracción ayuda a mejorar la seguridad del software y también permite evitar repetir el proceso de codificación desde cero, desarrollando y codificando más aplicaciones basadas en las ya creadas.

3. Herencia:

Como sugiere el nombre, el concepto de herencia implica que obtendremos una nueva clase de una antigua, en este caso, además, la clase nueva heredará las funciones de la anterior. Así, la clase antigua se llamará clase padre o superclase, mientras que la nueva clase derivada se llamará clase hija. Este concepto ayudará a aplicar el concepto DRY, manteniendo además la posibilidad de reutilización.

Herencia

4. Polimorfismo:

El polimorfismo permite que una función procese diferentes tipos de datos. Por ejemplo, podemos usar un método de suma para obtener los valores de la suma de (a) y (b) y otro para obtener la suma de (a), (b) y (c).

En términos simples, un polimorfismo implica una interfaz y muchos métodos.

En resumen, la OOP es un modelo de programación que se centra en organizar el desarrollo de software usando objetos que pueden tener comportamientos y características únicos. Se trata de un modelo útil para programas grandes y complejos, especialmente si se actualizan con frecuencia.

Características de la OOP:

  • Los programas que usan OOP incluyen clases y funciones.
  • El alto nivel de seguridad de los datos se oculta gracias a la aplicación de los principios de encapsulación y abstracción.
  • Esto facilita el trabajo en proyectos complejos porque el código se puede dividir en pequeños bloques y esto puede reducir la complejidad del proyecto.
  • Los procesos de actualización y desarrollo se vuelven más sencillos.
  • Tiene la capacidad de reutilizar código usando el principio de herencia.
  • Asimismo, ofrece la posibilidad de crear múltiples instancias de la misma clase sin conflictos.

Hay muchos lenguajes de programación que pueden adoptar el enfoque de OOP. Los más populares son C++, C#, Python, Java, JavaScript, PHP y otros. Aquí también podemos incluir MQL5.


OOP en MQL5

En esta sección, veremos la OOP en MQL5 y aprenderemos a utilizarla.

Antes de usar la OOP en MQL5, deberemos comprender cómo podemos usar lo siguiente en MQL5:

  • Clases
  • Modificadores de acceso
  • Constructores y destructores
  • Clases derivadas (hijas)
  • Funciones virtuales
  • Objetos

Clases:

En MQL5, si necesitamos crear una clase que sea un ejemplo de un objeto, deberemos declarar dicha clase en el ámbito global al igual que las funciones. Podemos crear esta clase usando la palabra clave class seguida del identificador único deseado; luego podemos colocar entre llaves nuestras variables y métodos que sean miembros de la clase y, después de la segunda llave, poner un punto y coma para completar la declaración de la clase. Podemos usar esta declaración de clase en un programa o en un archivo de inclusión.

A continuación le mostramos un ejemplo de declaración de esta clase:

class Cobject
{
   int var1;       // variable1
   double var2;    // variable1
   void method1(); // Method or function1
};

Como vemos en el ejemplo anterior, tenemos tres miembros de clase: dos variables y un método o función.

Modificadores de acceso:

Con estos modificadores de acceso podremos definir qué variables y funciones podemos usar fuera de la clase, y además tendremos tres palabras clave de acceso: public, private y protected.

  • Public: miembros a los que se puede acceder para su uso fuera de la clase.
  • Private: representa los miembros a los que no se puede acceder para su uso fuera de la clase, pero a los que se puede acceder para su uso dentro de la clase solo mediante funciones. Una eventual clase hija de esta clase no heredará estos miembros privados.
  • Protected: representa elementos que serán heredados por las clases hijas, pero que son de naturaleza privada.

A continuación le mostramos un ejemplo:

class Cobject
{
   private:
   int var1;       // variable1
   protected:
   double var2;    // variable1
   public:
   void method1(); // Method or function1
};

Como podemos ver en el ejemplo anterior, tenemos tres miembros en una clase con dos variables: una es privada (private), otra es protegida (protected) y la tercera es pública (public).

Constructores y destructores:

Si necesitamos inicializar variables en una clase, usaremos un constructor. Si este falta, el compilador lo creará por defecto, pero no será visible de forma predeterminada. También deberá estar disponible públicamente. Un destructor es una función que se llama automáticamente al destruir un objeto de clase. El nombre de un destructor puede ser el mismo que el de una clase, con una virgulilla (~). Independientemente de si hay un destructor o no, las cadenas, matrices dinámicas y objetos requieren desinicialización, por lo que se desinicializarán de todos modos.

A continuación le mostramos un constructor de ejemplo:

class CPrices
  {
private:
   double               open;         // Open price
   double               high;         // High price
   double               low;          // Low price
   double               close;        // Close price
public:
   //--- Default constructor
                     CPrices(void);
   //--- Parametric constructor
                     CPrices(double o,double h,double l, double c);
  };

Clases derivadas (hijas):

Como hemos aprendido antes, el concepto de herencia es una de las características más valiosas y útiles de la OOP porque podemos crear una clase hija a partir de una superclase o una clase padre, y esta clase hija heredará todos los miembros de la clase padre excepto los privados. Después de esto podremos añadir nuevas variables y funciones para esta clase hija.

Tomaremos el siguiente ejemplo: si tenemos una clase padre para los precios, podremos crear una clase hija para los precios diarios como se muestra a continuación:

class CDailyPrices : public CPrices
{
public:
   double               open;          // Open price
   double               high;          // High price
   double               low;           // Low price
   double               close;         // Close price
};

Como podemos ver, el nombre de la clase padre es CPrices, mientras que CDailyPrices será la clase hija o derivada. Todos los miembros públicos y protegidos de CPrices serán parte de la clase CDailyPrices y seguirán siendo públicos.

Funciones virtuales:

Si queremos actualizar la forma en que funciona un método o función en una clase hija, podremos hacerlo usando una función (virtual) en la clase padre y luego definiendo la función en la clase hija. Por ejemplo, si tenemos dos versiones diferentes de una función basada en una clase, para la clase padre definiremos una función usando la palabra clave virtual

class CVar
  {
public:
   virtual 
void varCal();
  };

Luego actualizaremos la misma función en la clase hija.

class CVar1 : public CVar
{
public:
 int varCal(int x, int y);
}; 

Objetos:

Los objetos tienen un identificador único. Al igual que cuando creamos una variable, utilizaremos el nombre de la clase como tipo antes del identificador del objeto. Podemos crear muchos objetos pertenecientes a nuestras clases, ya que para el proyecto solo necesitaremos utilizar un identificador único para cada uno. Tras declarar un objeto, podremos acceder a cualquier miembro público usando un punto (.)

Veamos un ejemplo que muestra la creación de una clase con una variable entera de número de operaciones (num_trades),

class CSystrades
{
public:
int num_trades;
};

Luego necesitaremos crear un objeto que pertenezca a esta clase llamado system1. Realizaremos esto ejecutando los pasos siguientes:

CSystrades system1;

Entonces podremos definir este objeto según el valor (3):

system1.num_trades=3;

Ya hemos analizado cómo podemos aplicar la OOP en MQL5, examinando algunos de los puntos más importantes.

Ejemplos de OOP

En este interesante apartado, presentaremos algunas aplicaciones sencillas que utilizan OOP.

priceClass:

En esta sencilla aplicación necesitaremos verificar los precios en múltiples marcos temporales. Así, utilizaremos tres marcos temporales (diario, semanal y mensual). También necesitaremos ver todos los precios (apertura, máximo, mínimo, cierre) en un solo lugar, digamos, en la pestaña "Expertos". Después de ello podremos seguir desarrollando programas más complejos.

Primero, deberemos declarar la clase ejecutando los pasos siguientes:

  • Necesitaremos declarar una clase para los precios en el ámbito global e incluir a todos los miembros públicos como de acceso público usando la palabra clave class.
  • Utilizaremos la palabra clave public.
  • Crearemos cinco variables (marco temporal, apertura, máximo, mínimo y cierre).
  • Crearemos una función void para mostrar todos los datos de precios.
class CPrices
  {
public:
   string            timeFrame;
   double            open;
   double            high;
   double            low;
   double            close;
   void              pricesPrint()
     {
      Print(timeFrame," Prices = Open: ",open," - ","High: ",high,"-","Low: ",low,"-","Close: ",close);
     }
  };

Crearemos objetos de clase para los precios diarios, semanales y mensuales.

CPrices CDailyPrices;
CPrices CWeeklyPrices;
CPrices CMonthlyPrices;

Dentro de la función OnInit, definiremos lo siguiente para los tres marcos temporales:

  • Una línea con el nombre del marco temporal.
  • El precio de apertura utilizando la función iOpen.
  • El precio máximo utilizando la función iHigh.
  • El precio mínimo utilizando la función iLow.
  • El precio de cierre utilizando la función iClose.
  • La llamar a la función o el método print.
int OnInit()
  {
//--- Daily time frame
   CDailyPrices.timeFrame="Daily";
   CDailyPrices.open=(iOpen(Symbol(),PERIOD_D1,1));
   CDailyPrices.high=(iHigh(Symbol(),PERIOD_D1,1));
   CDailyPrices.low=(iLow(Symbol(),PERIOD_D1,1));
   CDailyPrices.close=(iClose(Symbol(),PERIOD_D1,1));
   CDailyPrices.pricesPrint();

//--- Weekly time frame
   CWeeklyPrices.timeFrame="Weekly";
   CWeeklyPrices.open=(iOpen(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.high=(iHigh(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.low=(iLow(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.close=(iClose(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.pricesPrint();

//--- Monthly time frame
   CMonthlyPrices.timeFrame="Monthly";
   CMonthlyPrices.open=(iOpen(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.high=(iHigh(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.low=(iLow(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.close=(iClose(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.pricesPrint();
   return(INIT_SUCCEEDED);
  }

Después de esto, podremos ver los precios tras el funcionamiento del asesor en la pestaña "Expertos" de la barra de herramientas:

 Precios

Aquí vemos tres líneas:

  • La primera línea mostrará los precios diarios de apertura, máximo, mínimo y cierre.
  • La segunda, los mismos precios basados ​​en los datos semanales.
  • La tercera, según los datos mensuales.

indicatorClass:

El programa deberá mostrar los valores de los cuatro tipos de medias móviles (simple, exponencial, suavizada y ponderada linealmente) utilizando la OOP. A continuación le mostramos los sencillos pasos para lograr el objetivo:

Primero declararemos la clase de indicador CiMA usando la palabra clave class y crearemos los miembros públicos de esta clase. Estas son las cuatro variables generales: MAType - para determinar el tipo de media móvil, MAArray - para determinar el array de la media móvil, MAHandle - para determinar el identificador de cada tipo, MAValue - para determinar el valor de cada media móvil. También crearemos el método void o la función valuePrint y el cuerpo de la función para mostrar el valor de cada tipo de media móvil,

class CiMA
  {
public:
   string            MAType;
   double            MAArray[];
   int               MAHandle;
   double            MAValue;
   void              valuePrint()
     {
      Print(MAType," Current Value: ",MAValue);
     };
  };

y crearemos los siguientes objetos para cada media móvil de la clase:

  • Nombre de la media
  • Manejador de la media
  • Array de la media
//--- SMA
CiMA CSma;
CiMA CSmaHandle;
CiMA CSmaArray;

//--- EMA
CiMA CEma;
CiMA CEmaHandle;
CiMA CEmaArray;

//--- SMMA
CiMA CSmma;
CiMA CSmmaHandle;
CiMA CSmmaArray;

//--- LWMA
CiMA CLwma;
CiMA CLwmaHandle;
CiMA CLwmaArray;

Para cada tipo de media móvil, ejecutaremos los siguientes pasos dentro de la función OnInit:

  • Definiremos el nombre de la media.
  • Definiremos el identificador de la media.
  • Configuraremos el indicador AS_SERIES para el array usando ArraySetAsSeries.
  • Obtendremos los datos del búfer de media usando la función CopyBuffer.
  • Determinaremos el valor de la media y lo normalizaremos usando la función NormalizeDouble.
  • Llamaremos el método creado o la función Print
int OnInit()
  {
   //--- SMA
   CSma.MAType="Simple MA";
   CSmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_SMA,PRICE_CLOSE);
   ArraySetAsSeries(CSmaArray.MAArray,true);
   CopyBuffer(CSmaHandle.MAHandle,0,0,3,CSmaArray.MAArray);
   CSma.MAValue=NormalizeDouble(CSmaArray.MAArray[1],_Digits);
   CSma.valuePrint();

   //--- EMA
   CEma.MAType="Exponential MA";
   CEmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_EMA,PRICE_CLOSE);
   ArraySetAsSeries(CEmaArray.MAArray,true);
   CopyBuffer(CEmaHandle.MAHandle,0,0,3,CEmaArray.MAArray);
   CEma.MAValue=NormalizeDouble(CEmaArray.MAArray[1],_Digits);
   CEma.valuePrint();

   //--- SMMA
   CSmma.MAType="Smoothed MA";
   CSmmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_SMMA,PRICE_CLOSE);
   ArraySetAsSeries(CSmmaArray.MAArray,true);
   CopyBuffer(CSmmaHandle.MAHandle,0,0,3,CSmmaArray.MAArray);
   CSmma.MAValue=NormalizeDouble(CSmmaArray.MAArray[1],_Digits);
   CSmma.valuePrint();

   //--- LWMA
   CLwma.MAType="Linear-weighted MA";
   CLwmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_LWMA,PRICE_CLOSE);
   ArraySetAsSeries(CLwmaArray.MAArray,true);
   CopyBuffer(CLwmaHandle.MAHandle,0,0,3,CLwmaArray.MAArray);
   CLwma.MAValue=NormalizeDouble(CLwmaArray.MAArray[1],_Digits);
   CLwma.valuePrint();
   return(INIT_SUCCEEDED);
  }

Una vez compilado y ejecutado el código, veremos cuatro líneas para cada tipo de media móvil. Cada línea mostrará el valor de la media:

 Valores de los indicadores

Como hemos dicho, estas aplicaciones se podrán mejorar para realizar tareas más complejas. Sin embargo, nuestro objetivo en este artículo es aprender los conceptos básicos de la OOP y dominar su aplicación en la práctica.


Conclusión

En este artículo, hemos aprendido los conceptos básicos de un enfoque muy importante en la programación. La OOP ayuda a crear software seguro, simplifica el desarrollo dividiendo el código en pequeños bloques y permite crear muchos ejemplares de la misma clase sin conflictos, incluso si se comportan de manera diferente, ofreciendo mayor flexibilidad y seguridad al realizar actualizaciones.

También hemos aprendido a aplicar este importante enfoque en MQL5 para obtener todas estas increíbles funciones; asimismo, hemos explorado algunas aplicaciones simples que se pueden crear usando OOP en MQL5.

Espero que el artículo le haya sido útil.

Si desea obtener más información sobre la creación de sistemas comerciales en MQL5 basados ​​en los indicadores más populares, o sobre el desarrollo y la utilización de indicadores personalizados como parte de los asesores, puede leer mis otros artículos. Encontrará una lista completa con los artículos en mi perfil.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/12813

Archivos adjuntos |
indicatorClass.mq5 (2.09 KB)
priceClass.mq5 (1.7 KB)
Teoría de categorías en MQL5 (Parte 11): Grafos Teoría de categorías en MQL5 (Parte 11): Grafos
El presente artículo continúa la serie sobre la implementación de la teoría de categorías en MQL5. Aquí veremos cómo podemos integrar la teoría de grafos con los monoides y otras estructuras de datos al desarrollar una estrategia de cierre del sistema comercial.
Desarrollo de un sistema de repetición — Simulación de mercado (Parte 18):  Ticks y más ticks (II) Desarrollo de un sistema de repetición — Simulación de mercado (Parte 18): Ticks y más ticks (II)
En este caso, es extremadamente claro que las métricas están muy lejos del tiempo ideal para la creación de barras de 1 minuto. Entonces, lo primero que realmente corregiremos es precisamente esto. Corregir la cuestión de la temporización no es algo complicado. Por más increíble que parezca, en realidad es bastante simple de hacer. Sin embargo, no realicé la corrección en el artículo anterior porque allí el objetivo era explicar cómo llevar los datos de los ticks que se estaban utilizando para generar las barras de 1 minuto en el gráfico a la ventana de observación del mercado.
Teoría de categorías en MQL5 (Parte 12): Orden Teoría de categorías en MQL5 (Parte 12): Orden
El artículo forma parte de una serie sobre la implementación de grafos utilizando la teoría de categorías en MQL5 y está dedicado a la relación de orden (Order Theory). Hoy analizaremos dos tipos básicos de orden y exploraremos cómo los conceptos de relación de orden pueden respaldar conjuntos monoides en las decisiones comerciales.
Desarrollo de un sistema de repetición — Simulación de mercado (Parte 17): Ticks y más ticks (I) Desarrollo de un sistema de repetición — Simulación de mercado (Parte 17): Ticks y más ticks (I)
Aquí vamos a empezar a ver cómo implementar algo realmente interesante y curioso. Pero al mismo tiempo, es extremadamente complicado debido a algunas cuestiones que muchos confunden. Y lo peor que puede pasar es que algunos operadores que se autodenominan profesionales no tienen idea de la importancia de estos conceptos en el mercado de capitales. Sí, a pesar de que el enfoque aquí es la programación, comprender algunas cuestiones relacionadas con las operaciones en los mercados es de suma importancia para lo que vamos a empezar a implementar aquí.