Another MQL5 OOP Class

laplacianlab | 22 July, 2013


Introduction

Building a complete object-oriented EA that actually works is in my humble opinion a challenging task which requires many skills all put together: logical reasoning, divergent thinking, capacity for analysis and synthesis, imagination, etc. Let's say that if the automated trading system that we have to solve was a game of chess, its trading idea would be the chess strategy. And the execution of the chess strategy through tactics would be the programming of the robot through the use of technical indicators, chart figures, fundamental economic ideas and conceptual axioms.

Detail of The School of Athens by Raffaello Sanzio

Figure 1. Detail of The School of Athens by Raffaello Sanzio. In this picture we see the philosophers Plato and Aristotle in deep discussion.
Plato represents here the conceptual world and Aristotle the empiricist world.

I am aware of the difficulty of this exercise. An OO EA is not extremely difficult to program, but it is true that presents a certain degree of difficulty to people with little experience in application development. As in any other discipline, this is, in turn, due to the lack of experience itself, that is why I try to teach you this topic through a specific example that I am sure you will understand. Do not get discouraged if you still feel insecure in handling OOP concepts, you will find things much easier once you have implemented your first five EAs, let's say. You don't have to build anything from scratch for now, just understand what I explain here!

The whole process of conceiving and implementing a trading system can be simplified in many senses when it is performed by several persons, though this fact poses the problem of communication. I mean that the person who conceives the investment strategy is not required to handle the programming concepts who does handle their interlocutor, the programmer. And the MQL5 developer may not understand at first some important aspects of their customer's trading strategy.

This is a classical problem in software engineering that has led to the creation of many software development methodologies such as Scrum, Test Driven Development (TDD), Extreme programming (XP), etc. It is crucial being aware of the pitfalls of language. By the way, to your knowledge, Wikipedia states: "software development methodology or system development methodology in software engineering is a framework that is used to structure, plan, and control the process of developing an information system".

We are going to assume that we are capable of both quickly conceiving and implementing successful trading ideas. We can also suppose that we are at the end of an iterative development process where the trading system thought by our customer has already been well defined and understood by everybody. As you prefer. By the way, from now on I will refer in this text to some educational articles available in MQL5 Programming Articles so that you can quickly check and recall some ideas, when necessary, to successfully carry out this exercise. Are you ready?


1. Your First Steps in Adopting the New Paradigm


1.1. Why is OOP Good for Programming Your Forex EAs?

Maybe you are wondering at this point why you have to do something like this. Let me tell you first of all that you are not forced to be OOP. Anyway it is highly recommended to be an OOP person in order to take a step further in your knowledge of programming automated trading systems.

The classical way to develop applications, the so-called procedural programming, has these drawbacks:

Reusing code is easier with the new object-oriented style. This is very important! Many experts believe that reusing code is the real solution to most problems of software development.

At this point we must mention abstract data types (ADT). OOP enables the creation of ADTs. An ADT is an abstraction of the traditional concept of data type which is present in all programming languages. Its main use is to comfortably define the data domain of applications. Include\Arrays\Array.mqh, Include\Arrays\List.mqh and Include\Arrays\Tree.mqh are some examples of MQL5 abstract data types.

In short, the object-oriented programming paradigm wants you to design your applications in a conceptual level in order for you to benefit from code reuse, reliability, flexibility and ease of maintenance.

1.2. Are You a Conceptual Reasoner? UML Comes to the Rescue

Have you ever heard about UML? UML stands for Unified Modeling Language. It is a graphical language for designing object-oriented systems. We humans are supposed to first think our systems in the analysis phase and then code them with a programming language. Going from up to bottom is less insane for developers. Nevertheless, my experience as an analyst says that sometimes this is not possible because of several things: the app must be done in a very short time, there is no one in the team who can quickly apply their knowledge of UML, or perhaps some people on the team do not know some parts of UML.

UML is in my opinion a good analysis tool that you can use if you feel comfortable with it, and if circumstances surrounding the project are ok. Please, read How to Develop an Expert Advisor using UML Tools if you are interested in exploring the topic of UML. That article is maybe a little overwhelming but it serves to get a big picture about how professional software engineers work their analysis. You would need to complete a course of several weeks to fully understand UML! For now it's okay to know what UML is. According to my experience this analysis tool is not always used in all software projects due to several circumstances present in the real world.

UML logo

Figure 2. UML logo.


1.3. Hello World! Your First OO Class

If you are a complete newbie to object-oriented programming I recommend you first read the official documentation about OO available in MQL5 Reference and then take a look at Writing an Expert Advisor Using the MQL5 Object-Oriented Programming Approach to get the basics. Please be sure to complement these readings with some other materials. From now on I assume that you already know some OOP, so you will easily understand the classic example of the class Person which in MQL5 is as follows:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

The following is for purist developers obsessed in writing quality code. Unlike some examples available in MQL5 Programming Articles and many other software available in Code Base, the above class does use the same programming conventions applied by MetaQuotes Software Corp. for coding their MQL5 framework. I encourage you to write your code just as MetaQuotes does. By the way, the thread entitled About conventions in OOP MQL5 programs covers this topic.

In a nutshell, some important conventions applied in writing Person.mqh are:

Please, have a look at some MQL5 framework files, for example, Include\Arrays\Array.mqh, Include\Arrays\List.mqh, Include\Trade\Trade.mqh, and see how the original MQL5 code is written.


2. Let's Program Our First Object-Oriented EA


2.1. The Trading System's Idea

Our trading idea is simple: "Short trends of volatile markets are near random". That's it! This has been observed by several experts under some circumstances. If this hypothesis is true our Forex robot must work by necessity. Given a random point on a chart the next movement can obviously go both up and down, we don't know. The thing is that if the difference between the established SL and TP levels is sufficiently small then that difference is not significant in absolute terms, and we have therefore reached the mathematical expectation. This system is just going to let it work the mathematical expectation. Once you get the EA's code in this article and run the backtest you will see that it requires a very simple money management policy.


2.2. The Robot's OOP Skeleton

In this section we are developing the strategy above through the abstract reasoning required by object-oriented programming. So why don't we start thinking of our EA as if it was a living creature? Under this vision our Forex machine can be composed of three main parts: a brain, something that we will call evolution, and a chart.

The brain is the part of the robot that contains the data needed to operate, something like a read-only memory (ROM). The chart is the information piece emulating the graphic on which the robot operates. Finally, the so-called evolution is a piece of data containing temporal information such as the status of the robot at a given moment, the history of the operations performed, etc. It is as if we were designing a human being through their organs, something like a Frankenstein, because we have to develop an app for the health sector. Each organ is in this context a unique semantic concept associated with some other parts of the whole.

First of all let's create the folder MQL5\Include\Mine to store our custom stuff. This is just an idea to organize your code. It is good to know you can do this in your developments but of course you are not forced to. We will then create the file MQL5\Include\Mine\Enums.mqh in order to store the enums created by us:

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

Next, it is time to create our EA's embryo which will be named ExpertSimpleRandom.mq5! So, please, create the folder MQL5\Experts\SimpleRandom and then create inside the file ExpertSimpleRandom.mq5 with this code:

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

#property copyright     "Copyright © 2013, laplacianlab"
#property link          "https://www.mql5.com/en/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);
  }
//+------------------------------------------------------------------+

This is only one approach of the many possible out there. All of this is basically to illustrate how OOP works in MQL5. As you see, the Expert Advisor's main class is named CSimpleRandom.mqh, please, save it in 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. Binding CSimpleRandom to Objects of Complex Type

Note how the custom objects of type CBrain, CEvolution and CGraphic are linked to CSimpleRandom.

First we define the corresponding protected properties:

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

And right after we instantiate those objects inside the 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);

What we do is dynamically create objects of complex type just as the official docs explain in Object Pointers. With this scheme we can access CBrain's, CEvolution's and CGraphic's functionality directly from CSimpleRandom. We could run for instance the following code in ExpertSimpleRandom.mq5:

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

      // ...          
  }

I write now the code of CBrain, CEvolution and CGraphic to conclude this section. Please, note that there are some parts which are not coded because they are not strictly needed to backtest SimpleRandom. It is left as an exercise for you to code the missing parts of these classes, feel free to develop them as you want.  For example, m_death is not actually used, though the idea behind it is to know from the beginning the date in which the robot will finish its activity.

//+------------------------------------------------------------------+
//|                                               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 ExpertSimpleRandom.mq5

This random trading system has demonstrated to be valid only for certain stop loss and take profit levels, as expected. Of course, these winner SL/TP intervals are not the same for all symbols. This is because every symbol shows its own personality at a given time, or put another way, all currency pairs move differently with respect to the rest. So please identify first these levels in backtesting before running ExpertSimpleRandom.mq5 in a real environment.

I share here some sample data for which the idea exposed in this article seems to be winner. This can be deduced after running many times ExpertSimpleRandom.mq5 in MetaTrader 5 Strategy Tester.

Some winner inputs for EURUSD, January 2012, are:

Run number 1:

EURUSD, January 2012

Run number 2:

EURUSD, January 2012

Run number 3:

EURUSD, January 2012


Conclusion

We have learned to apply object-oriented programming in our automated trading systems. In order to do this we have first had to define a mechanical trading strategy. Our trading idea has been very simple: "Short trends of volatile markets are near random". This has been observed by several experts under some circumstances.

Right after we have thought of our EA in real-world terms as if it was a living creature. Thanks to this vision we have seen that our Forex machine can be composed of three main parts: a brain, something that we have called evolution, and a chart.

And finally we have programmed the system's expert advisor which incorporates the logic needed to run the backtest, have run many times the robot in January 2012, and we have found that most of the times the system is winning. The idea behind this system has been proved true but its effectiveness is not very high because of its simplicity.