另一个 MQL5 OOP 类

laplacianlab | 25 三月, 2014

简介

以我之见,构建一个真正有效的完善的面向对象EA,是一项要求综合多项技能的挑战。逻辑推理、发散思维、分析与综合能力以及想像力等等。比方说,如果我们要解决的自动交易系统是一个象棋游戏,其交易理念即为象棋策略。而象棋策略通过各种战术的执行,即是通过使用技术指标、图表数据、基础性经济理念和概念性公理来编制自动交易程序。

拉斐尔·圣齐奥《雅典学院》细部

图 1. 拉斐尔·圣齐奥《雅典学院》细部此图中所示,为深入讨论中的哲学家柏拉图和亚里士多德。
这里的柏拉图代表的是概念世界,而亚里士多德代表的是经验世界。

我清楚此练习的难度。面向对象 EA 对于编程来说不是特别难,但对于应用程序开发经验不足的人来讲,它确实有一定的难度。同其它任何学科一样,这反过来也是因为经验本身的缺乏。正因如此,我才试着通过一个确信您能明白的特定例子,为您讲解这一主题。如果您对处理 OOP 概念仍感觉不安,也不要沮丧,待您完成(比如说)前 5 个 EA 的实施之后,您就会发现事情变得简单多了。现在,您无需从头学习任何东西,只要弄懂我这里的解释就行!

交易系统的构思和实施的整个过程,如由多人执行,则会有多方面意义的简化,尽管这会造成沟通问题。我的意思是说,构思投资策略的人无需处理编程概念,而是面对他们的对话者-程序员。而且,MQL5 开发者最初可能理解不了其客户交易策略的一些重要方面。

这也是一个导致多种软件开发方法论得以创建的软件工程的经典问题,比如 Scrum测试驱动开发 (TDD)极限编程 (XP) 等。了解语言陷阱至关重要。顺便说一下,您知道维基百科指出:“软件工程学中的软件开发方法论或系统开发方法论,是一个用于结构、计划和控制信息系统开发过程的框架。”

继续假设我们具备快速构思并实施成功交易理念的能力。还可以假定,我们处于迭代开发过程的末期,客户对于交易系统的想法已有明确的定义、每个人都能理解。随您选择。附带提一下,从现在开始,我们在本文中陆续提到一些《MQL5 编程文集》中的介绍性文章,这样一来,您就能在必要时快速查看并回想起一些概念,从而成功开展此练习。您准备好了吗?


1. 采用新范式的前几步


1.1. 为什么说 OOP 对您的外汇交易 EA 编程有好处?

可能您在这一点上有疑虑:为什么要做这样的事情呢?我先要告诉您,没人逼着您成为 OOP。但不管怎样,想要在自动化交易系统编程知识领域再进一步,最好还是成为一名 OOP 人。

开发应用程序的经典方式,所谓的程序编程有下述缺陷:

新面向对象范式让代码重复使用变得更加简单。这重要得很!许多专家都相信,代码的重复使用是软件开发中大多数问题的真正解决方案。

这个时候,我们必须提及 抽象型数据类型 (ADT)了。OOP 让 ADT 的创建成为了可能。ADT 是所有编程语言中都存在的传统数据类型概念的一种抽象。其主要用途,就是舒适地定义应用程序的数据域。Include\Arrays\Array.mqh Include\Arrays\List.mqhInclude\Arrays\Tree.mqh 均为 MQL5 抽象数据类型的一些示例。

简而言之,面向对象编程范式就是想让您在一个概念层面设计自己的应用程序,从而享受到代码重复使用、可靠性、灵活性和方便维护等好处。

1.2. 您是概念推理器吗?UML 来帮您

您听说过 UML 吗?UML 代表的是“统一建模语言”。它是一种设计面向对象系统的图形语言。我们人类应该是首先在分析阶段考虑我们的系统,然后再用一种编程语言将其编成代码。对于开发人员而言,自上而下显得不那么疯狂。然而,担任分析师的经验告诉我,有些时候这也不太可能,原因如下:此应用必须在非常短的时间内完成,团队中没有任何一个人可以快速应用其 UML 知识,或者团队中某些人不清楚 UML 的某些环节也是极有可能的。

以我看来,UML 是一款很好的分析工具,如果您感觉称手、且项目周边的环境适合,您就可以使用。如果您有意探索 UML 主题,请阅读《如何利用 UML 工具开发 EA 交易》。此文的内容可能有点多,但它足以统观专业软件工程师如何处理其分析的相关情形。您将需要完成一项历时数周的课程才能完全掌握 UML!至于现在,了解什么是 UML 也就可以了。据我个人的经验,由于真实世界中存在环境的多样性,该分析工具并不能始终用于所有软件项目。

UML 标识

图 2. UML 标识


1.3. Hello World!您的第一个 OO 类

如果您是面向对象编程的完全新手,我建议您先阅读《MQL5 参考》中提供的面向对象相关官方文档,再看看《利用 MQL5 面向对象编程方法编写 EA 交易》以掌握一些基础知识。请一定再多读些其它相关材料,作为补充。从现在开始,我假设您已对 OOP 有一些了解,可以轻松理解 MQL5 中的 Person 类的如下经典示例:

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

下面内容专为沉迷于编写优质代码的纯粹开发人员准备。与《MQL5 编程文集》中的一些示例以及 代码库中的许多其它可用软件不同,上面的类并不使用 MetaQuotes Software Corp. 为编制其 MQL5 框架代码而应用的相同编程惯例。我建议您像 MetaQuotes 那样编写自己的代码。顺便提一下,题目为《关于 OOP MQL5 程序中的惯例》的帖子中涵盖了这一主题。

简括地说,编写 Person.mqh 的过程中适用的一些重要惯例包括:

请阅读一些 MQL5 框架文件,比如,Include\Arrays\Array.mqhInclude\Arrays\List.mqhInclude\Trade\Trade.mqh 等,再看看最初的 MQL5 代码是如何编写的。


2. 来编写我们的第一个面向对象 EA


2.1. 交易系统的理念

我们的交易理念很简单:“震荡市场的空头趋势接近随机。”就是这样!已有多个专家在某些情况下观测到了这一点。如果这一假设成立,我们的外汇自动交易势必会有用处。在图表上给定某个随机点,很明显下一个变化可上可下,我们根本不知道。重点在于,如果既定的止损与获利水平之间的差异足够小,那么按绝对价值计算,差异也不会大,而且我们也因此达到了数学期望值。此系统只是令其处理数学期望值。得到本文中的 EA 代码后,再运行回测,您就会看到它所需要的资金管理政策非常简单。


2.2. 自动交易的 OOP 框架

在此部分,我们要通过面向对象编程所需的抽象推理来开发上述策略。那么,我们开始将 EA 作为一个生物来看待,又未尝不可呢?按照这一设想,我们的外汇交易机器可由三大部分构成:大脑,我们可称其为演变的某样东西以及一个图表。

大脑是自动交易中包含运行所需数据的部分,有点类似只读存储器 (ROM)。图表是指模仿自动交易运行所本图形的信息部分。最后,所谓的演变则是指一条包含临时信息的数据,比如某给定时刻自动交易的状态、执行操作的历史等。就像是我们通过各个器官来设计一个人一样,有点类似于科学怪人,因为我们不得不为保健行业开发一种应用。这种背景下,每一个器官都是一个关联整体其它某个部位的独特语义概念。

首先,我们创建 MQL5\Include\Mine 文件夹,以存储我们的自定义内容。这只是一种代码组织理念。能在您的开发过程中这样做当然好,但也没人逼您这样做。之后,我们将创建 MQL5\Include\Mine\Enums.mqh 文件,以存储我们创建的枚举:

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

接下来,开始创建我们的 EA 胚胎,并将其命名为 ExpertSimpleRandom.mq5!请创建 MQL5\Experts\SimpleRandom 文件夹,再利用此代码在文件内创建 ExpertSimpleRandom.mq5

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

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

方法还有许多,这只是其中一个。而所有这些基本上都是在阐释 MQL5 中的 OOP 如何工作。可以看出,EA 交易主类被命名为 CSimpleRandom.mqh,请将其保存于 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. 将 CSimpleRandom 绑定到复杂型对象

注意 CBrainCEvolutionCGraphic 类型的自定义对象是如何连接到 CSimpleRandom 的。

首先,我们定义相应的受保护属性:

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

之后,我们马上实例化构造函数内的这些对象:

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

我们所做的,就是像对象指针中阐释的官方文档一样,动态创建复杂类型的对象。利用此方案,我们可以直接通过 CSimpleRandom 访问 CBrainCEvolutionCGraphic 的功能。我们可以运行 ExpertSimpleRandom.mq5 中的下述代码作为实例:

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

      // ...          
  }

我现在编写 CBrainCEvolution 以及 CGraphic的代码,作为此部分内容的结束。请注意,有些部分并未进行编码,因为它们并没有回测 SimpleRandom 的严格要求。这些类中的缺失部分编码,就留给您做练习了,请按照自己的想法自由开发它们。比如说,m_death 实际上就未被使用,尽管其背后的理念是从头了解自动交易完成其活动所在的日期。

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

与预期相同,此随机交易系统已被证实仅针对特定的止损与获利价位有效。当然,这些赢家止损或获利间隔也并非对所有交易品种都一致。这是因为每个交易品种都显示其在某给定时间的个性,或者换句话说,所有的货币对都相对于其余货币对以不同方式变化。所以,在真实环境中运行 ExpertSimpleRandom.mq5 之前,请首先确定回测中的这些价位。

我在这里分享了一些本文中呈示的赢家理念所本的样本数据。这一结果可在多次运行 MetaTrader 5 策略测试程序中的 ExpertSimpleRandom.mq5 之后推论得出。

2012 年 1 月 EURUSD 的一些赢家输入参数:

第 1 次运行:

EURUSD,2012 年 1 月

第 2 次运行:

EURUSD,2012 年 1 月

第 3 次运行:

EURUSD,2012 年 1 月


总结

我们学会了在自己的自动交易系统中应用面向对象编程。为此,我们必须首先定义一个机械交易策略。我们的交易理念也非常简单:“震荡市场的空头趋势近似随机。”已有多个专家在某些情况下观测到了这一点。

之后,我们将 EA 想像成为了真实世界中的生命体。借助这一设想,我们将外汇交易机器视作一个由三大部分构成的整体:大脑,我们可称其为演变的某样东西以及一个图表。

最后,我们编好了该系统的 EA 交易,加入了运行回测所需的逻辑,并于 2012 年 1 月完成了自动交易的多次运行,且发现大多数情况下,此系统都会赢。此系统背后的理念已经得到证实,但其效率却因其简单性而不是特别高。