Descargar MetaTrader 5

Otra clase POO de MQL5

7 mayo 2014, 10:22
Jordi Bassaganas
0
628

Introducción

Implementar un Asesor Experto orientado a objetos completo que funcione realmente es, en mi humilde opinión, una tarea difícil que requiere muchas habilidades juntas: razonamiento lógico, pensamiento divergente, capacidad de análisis y síntesis, imaginación, etc. Digamos que si el sistema de trading automatizado que tenemos que resolver era un ajedrez, su idea del trading sería la estrategia del ajedrez. Y la ejecución de la estrategia del ajedrez mediante las tácticas sería la programación del robot mediante los indicadores técnicos, datos de los gráficos, conceptos económicos básicos y axiomas conceptuales.

Fragmento de "La escuela de Atenas" de Raffaello Sanzio

Figura 1. Fragmento de "La escuela de Atenas" de Raffaello Sanzio. En esta pintura podemos ver a los filósofos Platón y Aristóteles en una profunda discusión.
Platón representa aquí el mundo conceptual y Aristóteles el mundo empírico.

Soy consciente de la dificultad de este ejercicio. La programación de un Asesor Experto OO no es demasiado difícil, pero es verdad que tiene cierto grado de dificultad para personas con poca experiencia en el desarrollo de aplicaciones. Y debido a la falta de experiencia como en cualquier otra disciplina, voy a tratar de explicarle este tema a través de ejemplos concretos que estoy seguro de que va a entender. No se desanime si todavía no se siente cómodo con el manejo de los conceptos de programación orientada a objetos, digamos que una vez implementa sus primeros cinco Asesores Expertos las cosa le resultarán mucho más fáciles. Ahora no tiene que implementar nada desde cero, solo tiene que entender lo que voy a explicar aquí.

Se puede simplificar todo el concepto de diseño e implementación de un sistema de trading cuando se lleva a cabo por varias personas, a pesar de que esto suponga un problema de comunicación. Lo que quiero decir es que la persona que diseña la estrategia no está obligada a manejar los conceptos de programación que tiene que manejar su interlocutor, el programador. Y puede que el desarrollador de MQL5 no entienda de entrada algunos aspectos importantes de la estrategia de trading de su cliente.

Este es un problema clásico en la ingeniería de software que ha llevado a la creación de metodologías de desarrollo de software tales como Scrum, Desarrollo guiado por pruebas (Test Driven Development "TDD"", Programación extrema (eXtreme Programming "XP"), etc. Es fundamental ser consciente de las trampas del lenguaje. Por cierto, según Wikipedia: "Metodología de desarrollo de software en ingeniería de software es un marco de trabajo (framework) usado para estructurar, planificar y controlar el proceso de desarrollo en sistemas de información".

Vamos a suponer que somos capaces de diseñar e implementar rápidamente y con éxito ideas de trading. Podemos suponer también que estamos en la última iteración del proceso de desarrollo donde el sistema de trading que ha pensado nuestro cliente ya ha sido bien definido y entendido por cada uno. Como usted prefiera. Por cierto, a partir de ahora nos vamos a referir en este artículo a algunos artículos didácticos disponibles en los Artículos sobre programación en el lenguaje MQL5 de modo que pueda comprobar y recordar rápidamente algunos conceptos, cuando sea necesario, para llevar a buen término este ejercicio. ¿Está listo?


1. Sus primeros pasos en adoptar el nuevo paradigma


1.1. ¿Por qué POO es buena para la programación de sus Asesores Expertos Forex?

A estas alturas, tal vez se está preguntando, ¿por qué hacer algo así? En primer lugar, tengo que decirle que no está obligado a ser POO. De todas formas es muy recomendable ser una persona POO para dar un paso más en sus conocimientos de programación de sistemas de trading automatizados.

El método clásico para desarrollar aplicaciones, el conocido como programación procedimental, tiene estos inconvenientes:

  • Dificulta el modelado de los problemas. Con este viejo paradigma se reduce la solución del problema principal a dividirlo en subproblemas más simples que se resuelven mediante módulos funcionales, es decir, funciones y procedimientos.
  • Hace difícil la reutilización del código, que a su vez afecta el coste, la fiabilidad, la flexibilidad y el mantenimiento.

La reutilización del código es más fácil con el nuevo estilo orientado a objetos. ¡Esto es muy importante! Muchos expertos creen que la reutilización del código es la solución real a muchos problemas de desarrollo de software.

Llegados a este punto debemos mencionar los tipos abstractos de datos (TAD). La POO permite la creación de los TAD. Un TAD es una abstracción del concepto tradicional de datos que está presente en todos los lenguajes de programación. Su uso principal es definir de manera cómoda el dominio de aplicaciones de los datos. Include\Arrays\Array.mqh, Include\Arrays\List.mqh y Include\Arrays\Tree.mqh son algunos ejemplos de tipos abstractos de datos de MQL5.

En resumen, el paradigma de la programación orientada a objetos requiere que diseñe sus aplicaciones en un nivel conceptual para que pueda beneficiarse de la reutilización del código, la fiabilidad, la flexibilidad y la facilidad de mantenimiento.

1.2. ¿Es usted de razonamiento conceptual? UML acude al rescate

¿Ha oído hablar alguna vez de UML? UML significa Unified Modeling Language (Lenguaje Unificado de Modelado). Es un lenguaje gráfico para el diseño de sistemas orientados a objetos. Nosotros, los seres humanos, se supone que primero pensamos en nuestros sistemas en la fase de análisis y luego implementamos su código con un lenguaje de programación. Ir de arriba hacia abajo es menos descabellado para los desarrolladores. Sin embargo, mi experiencia como analista me ha demostrado que a veces esto no es posible debido a varios motivos: se debe hacer la aplicación en un tiempo muy corto, no hay nadie en el equipo que pueda aplicar rápidamente sus conocimientos de UML, o tal vez algunas personas en el equipo no conocen algunas partes de UML.

En mi opinión, UML es una buena herramienta de análisis que puede utilizar si se siente cómodo con ella y las circunstancias que rodean el proyecto lo permiten. Si le interesa explorar más a fondo el tema de UML, le recomiendo leer Cómo desarrollar un Asesor Experto mediante las herramientas UML. Este artículo puede ser un poco intenso, pero sirve para obtener una visión global acerca de cómo llevan a cabo los ingenieros profesionales de software sus análisis. ¡Tendrá que seguir un curso de varias semanas para entender completamente UML! Por ahora, basta con saber lo que es UML. Según mi experiencia, esta herramienta de análisis no se usa siempre en los proyectos de software debido a varias circunstancias presentes en el mundo real.

Logo UML

Figura 2. Logo UML.


1.3. ¡Hola mundo! Su primera clase OO

Si es un novato en la programación orientada a objetos, le recomiendo primero leer la documentación oficial acerca de OO disponible en la Manual de referencia de MQL5 y luego echar un vistazo a Escribir un Asesor Experto mediante la programación orientada a objetos de MQL5 para conseguir los conceptos básicos. Asegúrese de complementar estas lecturas con otros materiales. A partir de ahora voy a suponer que ya sabe algo sobre POO, así que entenderá fácilmente el siguiente ejemplo clásico de la clase Person en MQL5:

//+------------------------------------------------------------------+
//| CPerson Class                                                    |
//+------------------------------------------------------------------+
class CPerson
  {
protected:
   string            m_first_name;
   string            m_surname;
   datetime          m_birth;

public:
   //--- Constructor and destructor methods
                     CPerson(void);
                    ~CPerson(void);
   //--- Getter methods
   string            GetFirstName(void);
   string            GetSurname(void);
   datetime          GetBirth(void);
   //--- Setter methods
   void              SetFirstName(string first_name);
   void              SetSurname(string surname);
   void              SetBirth(datetime birth);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPerson::CPerson(void)
  {
   Alert("Hello world! I am run when an object of type CPerson is created!");
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPerson::~CPerson(void)
  {
   Alert("Goodbye world! I am run when the object is destroyed!");
  }
//+------------------------------------------------------------------+
//| GetFirstName                                                     |
//+------------------------------------------------------------------+
string CPerson::GetFirstName(void)
  {
   return m_first_name;
  }
//+------------------------------------------------------------------+
//| GetSurname                                                       |
//+------------------------------------------------------------------+
string CPerson::GetSurname(void)
  {
   return m_surname;
  }
//+------------------------------------------------------------------+
//| GetBirth                                                         |
//+------------------------------------------------------------------+
datetime CPerson::GetBirth(void)
  {
   return m_birth;
  }
//+------------------------------------------------------------------+
//| SetFirstName                                                     |
//+------------------------------------------------------------------+
void CPerson::SetFirstName(string first_name)
  {
   m_first_name=first_name;
  }
//+------------------------------------------------------------------+
//| SetSurname                                                       |
//+------------------------------------------------------------------+
void CPerson::SetSurname(string surname)
  {
   m_surname=surname;
  }
//+------------------------------------------------------------------+
//| SetBirth                                                         |
//+------------------------------------------------------------------+
void CPerson::SetBirth(datetime birth)
  {
   m_birth=birth;
  }
//+------------------------------------------------------------------+

Lo siguiente es para los desarrolladores puristas obsesionados con la escritura de un código de calidad. A diferencia de algunos ejemplos disponibles en los artículos sobre programación en el lenguaje MQL5 y muchos otros programas disponibles en la Base de códigos, la clase anterior ha usado las mismas convenciones de programación aplicadas por MetaQuotes Software Corp. para codificar su marco de trabajo (framework) MQL5. Le animo a escribir su código al igual que Metaquotes. Por cierto, el hilo con titulo About conventions in OOP MQL5 programs abarca este tema.

Estas son algunas convenciones importantes en la escritura de Person.mqh:

  • El nombre de la clase CPerson empieza con la letra mayúscula C.
  • Los nombres de los métodos son CamelCase (llevan mayúsculas y minúsculas) y comienzan con una letra mayúscula, por ejemplo, GetFirstName, SetSurname, etc.
  • Los nombres de las propiedades protegidas comienzan con el prefijo m_, por ejemplo, m_first_name, m_surname y m_birth.
  • No se usa la palabra reservada this para hacer referencia a los miembros de la clase dentro de la misma clase.

Eche un vistazo a algunos archivos framework (marco de trabajo) de MQL5, por ejemplo, Include\Arrays\Array.mqh, Include\Arrays\List.mqh, Include\Trade\Trade.mqh, y observe cómo está escrito el código de MQL5.


2. Vamos a programar nuestro primer Asesor Experto orientado a objetos


2.1. La idea del sistema de trading

Nuestra idea de trading es simple: "Las tendencias a corto de los mercados volátiles son casi aleatorias". ¡Y eso es todo! Esto ha sido observado por varios expertos bajo algunas circunstancias. Si esta hipótesis es cierta, nuestro robot de Forex debe funcionar por necesidad. Dado un punto aleatorio en un gráfico, el siguiente movimiento puede ser tanto hacia arriba como hacia abajo, no lo sabemos. El caso es que si la diferencia entre los niveles establecidos SL y TP es suficientemente pequeña esta diferencia no resulta significativa en términos absolutos y por tanto hemos alcanzado la esperanza matemática. Este sistema solo va a dejar funcionar la esperanza matemática. Después de obtener el código del Asesor Experto en este artículo y ejecutar el backtest, verá que requiere una política de gestión de dinero muy sencilla.


2.2. Estructura de la POO del robot

En esta sección, vamos a desarrollar la estructura anterior mediante el razonamiento abstracto que requiere la programación orientada a objetos. Así que, ¿por qué no empezamos pensando en nuestro Asesor Experto como si fuera un ser vivo? De acuerdo con esta visión, nuestra máquina de Forex puede estar compuesta por tres partes principales: el cerebro, una cosa que llamaremos evolución y un gráfico.

El cerebro es la parte del robot que contiene los datos necesarios para funcionar, algo como una memoria de lectura solo (ROM). El gráfico es la pieza de la información que emula el gráfico en el cual funciona el robot. Por último, la denominada evolución que es la pieza de los datos que contienen la información temporal como el estado del robot en un momento dado, el historial de las operaciones llevadas a cabo, etc. Es como si estuviéramos diseñando un ser humano a través de sus órganos, algo similar a Frankenstein, puesto que tenemos que desarrollar un aplicación para el apartado de la salud. En este contexto, cada órgano es un concepto semántico único asociado a otras partes del conjunto.

En primer lugar, vamos a crear la carpeta MQL5\Include\Mine para almacenar nuestro contenido personalizado. Esto es solo un concepto de organización de su código. Es bueno saber que puede hacer esto en sus desarrollos, pero está claro que no está obligado a hacerlo. Después, crearemos el archivo MQL5\Include\Mine\Enums.mqh para almacenar la enumeración que hemos creado:

//+------------------------------------------------------------------+
//| Status enumeration                                               |
//+------------------------------------------------------------------+
enum ENUM_STATUS_EA
  {
   BUY,
   SELL,
   DO_NOTHING
  };
//+------------------------------------------------------------------+
//| Lifetime enumeration                                             |
//+------------------------------------------------------------------+
enum ENUM_LIFE_EA
  {
   HOUR,
   DAY,
   WEEK,
   MONTH,
   YEAR
  };
//+------------------------------------------------------------------+

A continuación, tenemos que crear el embrión de nuestro Asesor Experto que llamaremos ExpertSimpleRandom.mq5. Así que, vamos a crear la carpeta MQL5\Experts\SimpleRandom y dentro de la misma crearemos el archivo ExpertSimpleRandom.mq5 mediante este código:

//+------------------------------------------------------------------+
//|                                           ExpertSimpleRandom.mq5 |
//|                               Copyright © 2013, Jordi Bassagañas |
//+------------------------------------------------------------------+

#property copyright     "Copyright © 2013, laplacianlab"
#property link          "https://www.mql5.com/es/articles"
#property version       "1.00"

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Indicators.mqh>
#include <Mine\Enums.mqh>
#include <..\Experts\SimpleRandom\CSimpleRandom.mqh>

input int               StopLoss;
input int               TakeProfit;
input double            LotSize;
input ENUM_LIFE_EA      TimeLife;

MqlTick tick;
CSimpleRandom *SR=new CSimpleRandom(StopLoss,TakeProfit,LotSize,TimeLife);
//+------------------------------------------------------------------+
//| Initialization function                                          |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   SR.Init();
   return(0);
  }
//+------------------------------------------------------------------+
//| Deinitialization function                                        |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   SR.Deinit();
   delete(SR);
  }
//+------------------------------------------------------------------+
//| OnTick event function                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
   SymbolInfoTick(_Symbol,tick);
   SR.Go(tick.ask,tick.bid);
  }
//+------------------------------------------------------------------+

Este es solo un enfoque de los muchos posibles que hay. Todo esto es para ilustrar cómo funciona la POO en MQL5. Cómo puede observar, la clase principal del Asesor Experto se llama CSimpleRandom.mqh, guárdela en MQL5\Experts\SimpleRandom\CSimpleRandom.mqh:

//+------------------------------------------------------------------+
//|                                           ExpertSimpleRandom.mq5 |
//|                               Copyright © 2013, Jordi Bassagañas |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Mine\Enums.mqh>
#include <..\Experts\SimpleRandom\CBrain.mqh>
#include <..\Experts\SimpleRandom\CEvolution.mqh>
#include <..\Experts\SimpleRandom\CGraphic.mqh>
//+------------------------------------------------------------------+
//| CSimpleRandom Class                                              |
//+------------------------------------------------------------------+
class CSimpleRandom
  {
protected:
   CBrain           *m_brain;
   CEvolution       *m_evolution;
   CGraphic         *m_graphic;
   CTrade           *m_trade;
   CPositionInfo    *m_positionInfo;
public:
   //--- Constructor and destructor methods
                     CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life);
                    ~CSimpleRandom(void);
   //--- Getter methods
   CBrain           *GetBrain(void);
   CEvolution       *GetEvolution(void);
   CGraphic         *GetGraphic(void);
   CTrade           *GetTrade(void);
   CPositionInfo    *GetPositionInfo(void);
   //--- Specific methods of CSimpleRandom
   bool              Init();
   void              Deinit(void);
   bool              Go(double ask,double bid);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSimpleRandom::CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life)
  {
   int lifeInSeconds;

   switch(time_life)
     {
      case HOUR:

         lifeInSeconds=3600;

         break;

      case DAY:

         lifeInSeconds=86400;

         break;

      case WEEK:

         lifeInSeconds=604800;

         break;

      case MONTH:

         lifeInSeconds=2592000;

         break;

         // One year

      default:

         lifeInSeconds=31536000;

         break;
     }

   m_brain=new CBrain(TimeLocal(),TimeLocal()+lifeInSeconds,lot_size,stop_loss,take_profit);
   m_evolution=new CEvolution(DO_NOTHING);
   m_graphic=new CGraphic(_Symbol);
   m_trade=new CTrade();
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSimpleRandom::~CSimpleRandom(void)
  {
   delete(m_brain);
   delete(m_evolution);
   delete(m_graphic);
   delete(m_trade);
  }
//+------------------------------------------------------------------+
//| GetBrain                                                         |
//+------------------------------------------------------------------+
CBrain *CSimpleRandom::GetBrain(void)
  {
   return m_brain;
  }
//+------------------------------------------------------------------+
//| GetBrain                                                         |
//+------------------------------------------------------------------+
CEvolution *CSimpleRandom::GetEvolution(void)
  {
   return m_evolution;
  }
//+------------------------------------------------------------------+
//| GetGraphic                                                       |
//+------------------------------------------------------------------+
CGraphic *CSimpleRandom::GetGraphic(void)
  {
   return m_graphic;
  }
//+------------------------------------------------------------------+
//| GetTrade                                                         |
//+------------------------------------------------------------------+
CTrade *CSimpleRandom::GetTrade(void)
  {
   return m_trade;
  }
//+------------------------------------------------------------------+
//| GetPositionInfo                                                  |
//+------------------------------------------------------------------+
CPositionInfo *CSimpleRandom::GetPositionInfo(void)
  {
   return m_positionInfo;
  }
//+------------------------------------------------------------------+
//| CSimpleRandom initialization                                     |
//+------------------------------------------------------------------+
bool CSimpleRandom::Init(void)
  {
// Initialization logic here...
   return true;
  }
//+------------------------------------------------------------------+
//| CSimpleRandom deinitialization                                   |
//+------------------------------------------------------------------+
void CSimpleRandom::Deinit(void)
  {
// Deinitialization logic here...
   delete(m_brain);
   delete(m_evolution);
   delete(m_graphic);
   delete(m_trade);
  }
//+------------------------------------------------------------------+
//| CSimpleRandom Go                                                 |
//+------------------------------------------------------------------+
bool CSimpleRandom::Go(double ask,double bid)
  {
   double tp;
   double sl;

   int coin=m_brain.GetRandomNumber(0,1);

// Is there any open position?     

   if(!m_positionInfo.Select(_Symbol))
     {
      // If not, we open one

      if(coin==0)
        {
         GetEvolution().SetStatus(BUY);
        }
      else
        {
         GetEvolution().SetStatus(SELL);
        }
     }

// If so, let it work the mathematical expectation.

   else GetEvolution().SetStatus(DO_NOTHING);

   switch(GetEvolution().GetStatus())
     {
      case BUY:

         tp = ask + m_brain.GetTakeProfit() * _Point;
         sl = bid - m_brain.GetStopLoss() * _Point;

         GetTrade().PositionOpen(_Symbol,ORDER_TYPE_BUY,m_brain.GetSize(),ask,sl,tp);

         break;

      case SELL:

         sl = ask + m_brain.GetStopLoss() * _Point;
         tp = bid - m_brain.GetTakeProfit() * _Point;

         GetTrade().PositionOpen(_Symbol,ORDER_TYPE_SELL,m_brain.GetSize(),bid,sl,tp);

         break;

      case DO_NOTHING:

         // Nothing...

         break;
     }

// If there is some error we return false, for now we always return true  

   return(true);
  }
//+------------------------------------------------------------------+


2.3. Enlazar CSimpleRandom a objetos de tipo complejo

Observe cómo están enlazados los objetos de tipo CBrain, CEvolution y CGraphic a CSimpleRandom.

Primero definimos las propiedades protegidas correspondientes:

protected:
   CBrain           *m_brain;
   CEvolution       *m_evolution;
   CGraphic         *m_graphic; 

Y a continuación, creamos las instancias de estos objetos dentro del constructor.

m_brain=new CBrain(TimeLocal(), TimeLocal() + lifeInSeconds, lot_size, stop_loss, take_profit);         
m_evolution=new CEvolution(DO_NOTHING);      
m_graphic=new CGraphic(_Symbol);

Lo que hemos hecho es crear objetos de tipo complejo de manera dinámica, justo cómo se explica en la documentación oficial de los punteros a objetos. Con este esquema, podemos acceder directamente a las funcionalidades de CBrain, CEvolution y CGraphic desde CSimpleRandom. Podríamos, por ejemplo, ejecutar el siguiente código en ExpertSimpleRandom.mq5:

//+------------------------------------------------------------------+
//| OnTick event function                                            |
//+------------------------------------------------------------------+
void OnTick()
   {              
      // ...
      
      int randNumber=SR.GetBrain().GetRandomNumber(4, 8);

      // ...          
  }

Para concluir esta sección, escribo ahora el código de CBrain, CEvolution y CGraphic. Tenga en cuenta que no está escrito el código de algunas partes, ya que no son estrictamente necesarias para el backtest SimpleRandom. Le dejo como ejercicio la escritura del código de las partes que faltan de estas clases, siéntese libre para desarrollarlas como quiera.  Por ejemplo, m_death no se está utilizando realmente, aunque la idea que hay detrás de ella es conocer desde el principio la fecha de finalización de la actividad del robot.

//+------------------------------------------------------------------+
//|                                               ExpertSimpleRandom |
//|                               Copyright © 2013, Jordi Bassagaсas |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| CBrain Class                                                     |
//+------------------------------------------------------------------+
class CBrain
  {
protected:
   ENUM_TIMEFRAMES   m_period;               // period must always be initialized to PERIOD_M1 to fit the system's idea
   datetime          m_birth;               // The datetime in which the robot is initialized for the first time
   datetime          m_death;               // The datetime in which the robot will die
   double            m_size;                // The size of the positions
   int               m_stopLoss;            // Stop loss
   int               m_takeProfit;          // Take profit

public:
   //--- Constructor and destructor methods
                     CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit);
                    ~CBrain(void);
   //--- Getter methods
   datetime          GetBirth(void);
   datetime          GetDeath(void);
   double            GetSize(void);
   int               GetStopLoss(void);
   int               GetTakeProfit(void);
   //--- Setter methods
   void              SetBirth(datetime birth);
   void              SetDeath(datetime death);
   void              SetSize(double size);
   void              SetStopLoss(int stopLoss);
   void              SetTakeProfit(int takeProfit);
   //--- Brain specific logic
   int               GetRandomNumber(int a,int b);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBrain::CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit)
  {
   MathSrand(GetTickCount());

   m_period=PERIOD_M1;
   m_birth=birth;
   m_death=death;
   m_size=size;
   m_stopLoss=stopLoss;
   m_takeProfit=takeProfit;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBrain::~CBrain(void)
  {
  }
//+------------------------------------------------------------------+
//| GetBirth                                                         |
//+------------------------------------------------------------------+
datetime CBrain::GetBirth(void)
  {
   return m_birth;
  }
//+------------------------------------------------------------------+
//| GetDeath                                                         |
//+------------------------------------------------------------------+
datetime CBrain::GetDeath(void)
  {
   return m_death;
  }
//+------------------------------------------------------------------+
//| GetSize                                                          |
//+------------------------------------------------------------------+
double CBrain::GetSize(void)
  {
   return m_size;
  }
//+------------------------------------------------------------------+
//| GetStopLoss                                                      |
//+------------------------------------------------------------------+
int CBrain::GetStopLoss(void)
  {
   return m_stopLoss;
  }
//+------------------------------------------------------------------+
//| GetTakeProfit                                                    |
//+------------------------------------------------------------------+
int CBrain::GetTakeProfit(void)
  {
   return m_takeProfit;
  }
//+------------------------------------------------------------------+
//| SetBirth                                                         |
//+------------------------------------------------------------------+
void CBrain::SetBirth(datetime birth)
  {
   m_birth=birth;
  }
//+------------------------------------------------------------------+
//| SetDeath                                                         |
//+------------------------------------------------------------------+
void CBrain::SetDeath(datetime death)
  {
   m_death=death;
  }
//+------------------------------------------------------------------+
//| SetSize                                                          |
//+------------------------------------------------------------------+
void CBrain::SetSize(double size)
  {
   m_size=size;
  }
//+------------------------------------------------------------------+
//| SetStopLoss                                                      |
//+------------------------------------------------------------------+
void CBrain::SetStopLoss(int stopLoss)
  {
   m_stopLoss=stopLoss;
  }
//+------------------------------------------------------------------+
//| SetTakeProfit                                                    |
//+------------------------------------------------------------------+
void CBrain::SetTakeProfit(int takeProfit)
  {
   m_takeProfit=takeProfit;
  }
//+------------------------------------------------------------------+
//| GetRandomNumber                                                  |
//+------------------------------------------------------------------+
int CBrain::GetRandomNumber(int a,int b)
  {
   return(a+(MathRand()%(b-a+1)));
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                               ExpertSimpleRandom |
//|                               Copyright © 2013, Jordi Bassagaсas |
//+------------------------------------------------------------------+
#include <Indicators\Indicators.mqh>
#include <Mine\Enums.mqh>
//+------------------------------------------------------------------+
//| CEvolution Class                                                 |
//+------------------------------------------------------------------+
class CEvolution
  {
protected:
   ENUM_STATUS_EA    m_status;            // The current EA's status
   CArrayObj*        m_operations;        // History of the operations performed by the EA

public:
   //--- Constructor and destructor methods
                     CEvolution(ENUM_STATUS_EA status);
                    ~CEvolution(void);
   //--- Getter methods
   ENUM_STATUS_EA    GetStatus(void);
   CArrayObj        *GetOperations(void);
   //--- Setter methods
   void              SetStatus(ENUM_STATUS_EA status);
   void              SetOperation(CObject *operation);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEvolution::CEvolution(ENUM_STATUS_EA status)
  {
   m_status=status;
   m_operations=new CArrayObj;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CEvolution::~CEvolution(void)
  {
   delete(m_operations);
  }
//+------------------------------------------------------------------+
//| GetStatus                                                        |
//+------------------------------------------------------------------+
ENUM_STATUS_EA CEvolution::GetStatus(void)
  {
   return m_status;
  }
//+------------------------------------------------------------------+
//| GetOperations                                                    |
//+------------------------------------------------------------------+
CArrayObj *CEvolution::GetOperations(void)
  {
   return m_operations;
  }
//+------------------------------------------------------------------+
//| SetStatus                                                        |
//+------------------------------------------------------------------+
void CEvolution::SetStatus(ENUM_STATUS_EA status)
  {
   m_status=status;
  }
//+------------------------------------------------------------------+
//| SetOperation                                                     |
//+------------------------------------------------------------------+
void CEvolution::SetOperation(CObject *operation)
  {
   m_operations.Add(operation);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                           ExpertSimpleRandom.mq5 |
//|                               Copyright © 2013, Jordi Bassagaсas |
//+------------------------------------------------------------------+
#include <Trade\SymbolInfo.mqh>
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| CGrapic Class                                                    |
//+------------------------------------------------------------------+
class CGraphic
  {
protected:
   ENUM_TIMEFRAMES   m_period;            // Graphic's timeframe
   string            m_pair;              // Graphic's pair
   CSymbolInfo*      m_symbol;            // CSymbolInfo object
   CArrayObj*        m_bars;              // Array of bars

public:
   //--- Constructor and destructor methods
                     CGraphic(string pair);
                    ~CGraphic(void);
   //--- Getter methods
   string            GetPair(void);
   CSymbolInfo      *GetSymbol(void);
   CArrayObj        *GetBars(void);
   //--- Setter methods
   void              SetPair(string pair);
   void              SetSymbol(CSymbolInfo *symbol);
   void              SetBar(CObject *bar);
  };
//+------------------------------------------------------------------+
//| Constuctor                                                       |
//+------------------------------------------------------------------+
CGraphic::CGraphic(string pair)
  {
   m_period=PERIOD_M1;
   m_pair=pair;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CGraphic::~CGraphic(void)
  {
  }
//+------------------------------------------------------------------+
//| GetPair                                                          |
//+------------------------------------------------------------------+
string CGraphic::GetPair(void)
  {
   return m_pair;
  }
//+------------------------------------------------------------------+
//| GetSymbol                                                        |
//+------------------------------------------------------------------+
CSymbolInfo *CGraphic::GetSymbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//| GetBars                                                          |
//+------------------------------------------------------------------+
CArrayObj *CGraphic::GetBars(void)
  {
   return m_bars;
  }
//+------------------------------------------------------------------+
//| SetPair                                                          |
//+------------------------------------------------------------------+
void CGraphic::SetPair(string pair)
  {
   m_pair=pair;
  }
//+------------------------------------------------------------------+
//| SetSymbol                                                        |
//+------------------------------------------------------------------+
void CGraphic::SetSymbol(CSymbolInfo *symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//| SetBar                                                           |
//+------------------------------------------------------------------+
void CGraphic::SetBar(CObject *bar)
  {
   m_bars.Add(bar);
  }
//+------------------------------------------------------------------+

3. Backtesting de ExpertSimpleRandom.mq5

Como era de esperar, este sistema de trading aleatorio ha demostrado ser eficaz solo para ciertos niveles de Stop Loss y Take Profit. Por supuesto, estos intervalos rentables de SL/TP no son los mismos para todos los símbolos. Esto se debe a que cada símbolo muestra su propia personalidad en un momento dado, o en otras palabras, cada par de divisas se mueve de forma diferente respecto al resto de pares. Por lo tanto, identifique primero estos niveles en el backtesting antes de ejecutar ExpertSimpleRandom.mq5 en un entorno real.

Comparto aquí algunos datos de la muestra por los cuales la idea presentada en este artículo resulta rentable. Se puede deducir esto después de ejecutar varias veces ExpertSimpleRandom.mq5 en el probador de estrategias de MetaTrader 5.

Algunas entradas rentables para EURUSD, de enero 2012, son:

  • StopLoss: 400
  • TakeProfit: 600
  • LotSize: 0.01
  • TimeLife: MONTH (mes)

Ejecución número 1:

EURUSD, enero 2012

Ejecución número 2:

EURUSD, enero 2012

Ejecución número 3:

EURUSD, enero 2012


Conclusión

Hemos aprendido a aplicar la programación orientada a objetos a nuestros sistemas de trading. Para ello, tenemos primero que definir una estrategia de trading mecánica. Nuestra estrategia de trading ha sido muy sencilla: "Las tendencias a corto de los mercados volátiles son casi aleatorias". Esto ha sido observado por varios expertos bajo algunas circunstancias.

A continuación, hemos pensado en nuestro Asesor Experto en términos reales, como si fuera un ser vivo. Gracias a esta visión, hemos visto que nuestra máquina de Forex puede estar compuesta de tres partes principales: el cerebro, una cosa que hemos llamado evolución y un gráfico.

Y por último, hemos programado el Asesor Experto del sistema que incorpora la lógica necesaria para ejecutar el backtest, hemos ejecutado el robot varias veces en enero 2012 y hemos descubierto que el sistema es rentable en la mayoría de las veces. Se ha confirmado la idea que hay detrás de este sistema, pero su eficacia no es muy alta debido a su simplicidad.

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

Archivos adjuntos |
cbrain.mqh (5.64 KB)
cevolution.mqh (2.97 KB)
cgraphic.mqh (3.55 KB)
enums.mqh (0.92 KB)
csimplerandom.mqh (6.35 KB)
Indicador para el la representación del gráfico de punto y figura Indicador para el la representación del gráfico de punto y figura

Hay muchos tipos de gráficos que proporcionan informaciones acerca del estado actual del mercado. Muchos de ellos, como el gráfico de punto y figura, se remontan a tiempos remotos. Este artículo describe un ejemplo de un gráfico de punto y figura usando un indicador en tiempo real.

Guía práctica de MQL5: Reducción del efecto del sobreajuste y el manejo de la falta de cotizaciones Guía práctica de MQL5: Reducción del efecto del sobreajuste y el manejo de la falta de cotizaciones

Sea cual sea la estrategia de trading que utilice, siempre habrá que preguntarse qué parámetros escoger para asegurar futuras ganancias. Este artículo proporciona un ejemplo de un Asesor Experto con una posibilidad de mejorar varios parámetros de símbolos a la vez. Este método está previsto para reducir el sobreajuste de los parámetros y manejar situaciones donde los datos de un solo símbolo no son suficientes para el estudio.

Creación de Asesores Expertos de red neuronal mediante MQL5 Wizard y el generador Hlaiman de Asesores Expertos Creación de Asesores Expertos de red neuronal mediante MQL5 Wizard y el generador Hlaiman de Asesores Expertos

El artículo describe un método de creación automatizada de la red neuronal de Asesores Expertos usando MQL5 Wizard y el generador Hlaiman de Asesores Expertos. Le muestra cómo puede empezar a trabajar fácilmente con redes neuronales, sin tener que aprender todo el contenido de la información teórica y escribir su propio código.

Implementar un trader de noticias automático Implementar un trader de noticias automático

Esta es la continuación de otro artículo acerca de la clase POO de MQL5, en el cual se le mostró el modo de implementar un Asesor Experto orientado a objetos desde cero y se le dieron algunos trucos sobre la programación orientada a objetos. Hoy, le voy a mostrar los fundamentos técnicos necesarios para desarrollar un Asesor Experto capaz de hacer trading con noticias. Mi objetivo es seguir proporcionándole ideas acerca de la POO y también abarcar un nuevo tema en esta serie de artículos, trabajando con el sistema de archivos.