English Русский 中文 Español Deutsch 日本語
Expert Advisor multiplataforma: reutilização de componentes a partir da Biblioteca padrão MQL5

Expert Advisor multiplataforma: reutilização de componentes a partir da Biblioteca padrão MQL5

MetaTrader 5Integração | 28 setembro 2016, 16:34
1 250 0
Enrico Lambino
Enrico Lambino

Tabela de conteúdos


Introdução

Na Biblioteca padrão MQL5, existem alguns componentes que podem ser úteis no desenvolvimento de EAs MQL4 multiplataforma. No entanto, a incompatibilidade com o compilador MQL4 os torna inadequados para as versões de Expert Advisors multiplataforma desenvolvidos na MQL4. Existem pelo menos duas opções neste caso.

  1. Reescrever a partir do zero, mantendo apenas os componentes que são suportados por ambas as versões da linguagem.

  2. Copiar as classes e modificá-las para que os arquivos de cabeçalho sejam compilados na MQL4.

Neste artigo, vamos olhar para a segunda opção. Usar o primeiro método poderia fazer uma implementação livre no lado MQL4. No entanto, sua principal desvantagem é que não queremos reescrever um monte de código. Seria mais fácil fazer a atualização para a versão de classes MQL5 mais recente do que reescrever tudo do zero.

Estrutura de pastas

Ao contrário da maioria das classes que serão utilizadas, você precisará copiar os objetos de classe, que são reutilizados, da biblioteca padrão MQL5, na pasta Include dentro do diretório de dados. Portanto, para o Expert Advisor ser usado independentemente da versão, você precisará realizar um pouco de trabalho organizacional. Os métodos para atingir esse objetivo podem variar. Mas uma das maneiras mais fáceis é vincular um arquivo de cabeçalho específico e fazê-lo funcionar com o arquivo de cabeçalho apropriado que usaremos. Algo assim parecido com o que é geralmente adotado em classes de objetos personalizados:

  1. Para a versão MQL5 do arquivo de cabeçalho principal, deve se referir a localização original do arquivo de cabeçalho usado.

  2. Na versão MQL4 do cabeçalho principal, o arquivo deve se referir a uma cópia (modificada) do arquivo de cabeçalho usado.

Uma maneira de fazer isso é criar uma pasta chamada "Lib" no diretório principal. Ele estará localizado com o arquivo de cabeçalho principal. Uma pasta semelhante será criada na pasta MQL4 do diretório de multiplataforma (será chamada "MQLx-Reuse"). Hospedará as cópias modificadas das classes MQL5 respetivas.

/Include

/MQLx-Reuse

/Base

/Lib

<Main Header Files>

/MQL4

/Lib

<Copy of MQL5 Header Files>

/MQL5

A pasta MQL5 não terá a pasta "Lib", desde que os arquivos de cabeçalho principal se referem diretamente ao arquivo de cabeçalho dentro do diretório de dados MQL5. Por exemplo, precisamos reutilizá-lo no Expert Advisor multiplataforma CSymbolInfo. Especificamente, essa classe é parte da biblioteca padrão MQL5 relacionada com as classes de negociação. O acesso a ele pode ser obtido usando a diretiva #include:

#include <Trade\SymbolInfo.mqh>

Para usar isso, em nossa configuração atual, precisamos criar o arquivo de cabeçalho principal no subdiretório Lib dentro do diretório Base usando o seguinte código:

(/Base/Lib/SymbolInfo.mqh)

#ifdef __MQL5__
   #include <Trade\SymbolInfo.mqh>
#else
   #include "..\..\MQL4\Lib\SymbolInfo.mqh"
#endif

A versão MQL4 chama a diretiva do arquivo de cabeçalho localizado na pasta "Lib". Mas até à data, esta será um pasta diretório "Lib" do diretório "MQL4" em nosso diretório de usuário (“MQLx-Reuse”).

Conforme mencionado anteriormente, a versão MQL5 não precisa estar na pasta "Lib", desde que o arquivo de cabeçalho principal está localizado na pasta Base. Ele já aponta para <Trade\SymbolInfo.mqh>, que geralmente está localizado dentro da pasta Include MQL5.

Esta é apenas uma abordagem recomendada e não é obrigação. Em qualquer caso, é útil quando as pastas são usadas exclusivamente para arquivos de cabeçalho que são reutilizados ou emprestados da MQL5.

CSymbolInfo

Ao usar o compilador MQL4, a compilação do arquivo de cabeçalho de classe original CSymbolInfo gera erros. Os erros causados por incompatibilidade com MQL4, nomeadamente relacionados com a utilização das funções SymbolInfoDouble e SymbolInfoInteger. A maioria das chamadas dessas funções pode ser vista no método Refresh(). O código original da classe CSymbolInfo é mostrado abaixo:

bool CSymbolInfo::Refresh(void)
  {
   long tmp=0;
//--- 
   if(!SymbolInfoDouble(m_name,SYMBOL_POINT,m_point))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE,m_tick_value))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE_PROFIT,m_tick_value_profit))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE_LOSS,m_tick_value_loss))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_SIZE,m_tick_size))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_CONTRACT_SIZE,m_contract_size))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_MIN,m_lots_min))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_MAX,m_lots_max))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_STEP,m_lots_step))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_LIMIT,m_lots_limit))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_SWAP_LONG,m_swap_long))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_SWAP_SHORT,m_swap_short))
      return(false);
   if(!SymbolInfoInteger(m_name,SYMBOL_DIGITS,tmp))
      return(false);
   m_digits=(int)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_ORDER_MODE,tmp))
      return(false);
   m_order_mode=(int)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_EXEMODE,tmp))
      return(false);
   m_trade_execution=(ENUM_SYMBOL_TRADE_EXECUTION)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_CALC_MODE,tmp))
      return(false);
   m_trade_calcmode=(ENUM_SYMBOL_CALC_MODE)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_MODE,tmp))
      return(false);
   m_trade_mode=(ENUM_SYMBOL_TRADE_MODE)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_SWAP_MODE,tmp))
      return(false);
   m_swap_mode=(ENUM_SYMBOL_SWAP_MODE)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_SWAP_ROLLOVER3DAYS,tmp))
      return(false);
   m_swap3=(ENUM_DAY_OF_WEEK)tmp;
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_INITIAL,m_margin_initial))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_MAINTENANCE,m_margin_maintenance))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_LONG,m_margin_long))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_SHORT,m_margin_short))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_LIMIT,m_margin_limit))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_STOP,m_margin_stop))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_STOPLIMIT,m_margin_stoplimit))
      return(false);
   if(!SymbolInfoInteger(m_name,SYMBOL_EXPIRATION_MODE,tmp))
      return(false);
   m_trade_time_flags=(int)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_FILLING_MODE,tmp))
      return(false);
   m_trade_fill_flags=(int)tmp;
//--- boa sorte
   return(true);
  }

Da mesma forma, como discutido no primeiro artigo desta série, nós usamos um arquivo de cabeçalho comum, que idealmente consolida as semelhanças entre o código das versões MQL4 e MQL5. Na verdade, você pode reescrever a classe CSymbolinfo em três arquivos separados, para que as semelhanças sejam fundidas num único arquivo, enquanto as diferenças sejam separadas em dois arquivos diferentes. No entanto, neste artigo vamos ver a maneira mais fácil (e rápida): copiamos o arquivo de classe CSymbolinfo e, em seguida, escrevemos as linhas que são incompatíveis com a MQL4. Para as duas versões, a estrutura resultante do arquivo ficará assim:

Algumas propriedades de identificadores como SYMBOL_TRADE_TICK_VALUE_PROFIT e SYMBOL_TRADE_TICK_VALUE_LOSS não são suportadas na MQL4. Melhor comentá-las, até que haja novos desenvolvimentos para criar uma alternativa para usá-los.

O código a seguir demonstra como as funções comentadas que podem causar erros quando você chamar o método Refresh na MQL4.

bool CSymbolInfo::Refresh(void)
  {
   long tmp=0;
//--- 
   if(!SymbolInfoDouble(m_name,SYMBOL_POINT,m_point))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE,m_tick_value))
      return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE_PROFIT,m_tick_value_profit))
//return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE_LOSS,m_tick_value_loss))
//return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_SIZE,m_tick_size))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_CONTRACT_SIZE,m_contract_size))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_MIN,m_lots_min))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_MAX,m_lots_max))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_STEP,m_lots_step))
      return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_LIMIT,m_lots_limit))
//return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_SWAP_LONG,m_swap_long))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_SWAP_SHORT,m_swap_short))
      return(false);
   if(!SymbolInfoInteger(m_name,SYMBOL_DIGITS,tmp))
      return(false);
   m_digits=(int)tmp;
//if(!SymbolInfoInteger(m_name,SYMBOL_ORDER_MODE,tmp))
//return(false);
//m_order_mode=(int)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_EXEMODE,tmp))
      return(false);
   m_trade_execution=(ENUM_SYMBOL_TRADE_EXECUTION)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_CALC_MODE,tmp))
      return(false);
   m_trade_calcmode=(ENUM_SYMBOL_CALC_MODE)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_MODE,tmp))
      return(false);
   m_trade_mode=(ENUM_SYMBOL_TRADE_MODE)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_SWAP_MODE,tmp))
      return(false);
   m_swap_mode=(ENUM_SYMBOL_SWAP_MODE)tmp;
   if(!SymbolInfoInteger(m_name,SYMBOL_SWAP_ROLLOVER3DAYS,tmp))
      return(false);
   m_swap3=(ENUM_DAY_OF_WEEK)tmp;
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_INITIAL,m_margin_initial))
      return(false);
   if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_MAINTENANCE,m_margin_maintenance))
      return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_LONG,m_margin_long))
//return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_SHORT,m_margin_short))
//return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_LIMIT,m_margin_limit))
//return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_STOP,m_margin_stop))
//return(false);
//if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_STOPLIMIT,m_margin_stoplimit))
//return(false);
//if(!SymbolInfoInteger(m_name,SYMBOL_EXPIRATION_MODE,tmp))
//return(false);
//m_trade_time_flags=(int)tmp;
//if(!SymbolInfoInteger(m_name,SYMBOL_FILLING_MODE,tmp))
//return(false);
//m_trade_fill_flags=(int)tmp;
//--- boa sorte
   return(true);
  }

Existem enumerações embutidas na MQL5, mas não na MQL4. Para esta classe ENUM_SYMBOL_CALC_MODE e ENUM_SYMBOL_SWAP_MODE seriam necessárias ao compilar o arquivo de cabeçalho na MQL4. Para habilitar estas enumerações, só precisamos declará-las no arquivo de cabeçalho de classe:

enum ENUM_SYMBOL_CALC_MODE
  {
   SYMBOL_CALC_MODE_FOREX,
   SYMBOL_CALC_MODE_FUTURES,
   SYMBOL_CALC_MODE_CFD,
   SYMBOL_CALC_MODE_CFDINDEX,
   SYMBOL_CALC_MODE_CFDLEVERAGE,
   SYMBOL_CALC_MODE_EXCH_STOCKS,
   SYMBOL_CALC_MODE_EXCH_FUTURES,
   SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS
  };
enum ENUM_SYMBOL_SWAP_MODE
  {
   SYMBOL_SWAP_MODE_DISABLED,
   SYMBOL_SWAP_MODE_POINTS,
   SYMBOL_SWAP_MODE_CURRENCY_SYMBOL,
   SYMBOL_SWAP_MODE_CURRENCY_MARGIN,
   SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT,
   SYMBOL_SWAP_MODE_INTEREST_CURRENT,
   SYMBOL_SWAP_MODE_INTEREST_OPEN,
   SYMBOL_SWAP_MODE_REOPEN_CURRENT,
   SYMBOL_SWAP_MODE_REOPEN_BID
  };

Observe que foram comentadas não todas as funções sem suporte em MQL4. Até agora, nós adotamos os passos mínimos necessários para garantir que um arquivo de cabeçalho pode ser processado pelo compilador MQL4. Se você não tiver certeza se um determinado método ou função não é suportado em ambas as versões, é recomendável que você consulte a documentação para os dois.

Como cada versão tem sua própria cópia privada CSymbolInfo, o arquivo de cabeçalho principal não irá conter a definição de classe. Em vez disso, ele irá incluir apenas uma referência para a localização do arquivo de cabeçalho para incluir, dependendo da versão do compilador a ser utilizado:

#ifdef __MQL5__
   #include <Trade\SymbolInfo.mqh>
#else
   #include "..\..\MQL4\Lib\SymbolInfo.mqh"
#endif

O arquivo de cabeçalho MQL5 irá apontar para o local original do CSymbolInfo dentro da Biblioteca padrão MQL5. Por sua vez, o arquivo de cabeçalho MQL4 irá apontar para o arquivo de cabeçalho CSymbolInfo, que é uma cópia modificada do arquivo de cabeçalho MQL5 CSymbolInfo localizado em Lib.


CSymbolManager

Para EAS multi-moeda sera requerido uma coleção de instâncias CSymbolInfo. Sua criação será a tarefa de CSymbolManager. A classe estende a CArrayObj e permite-lhe armazenar instâncias de objeto diferente, informações sobre símbolos, juntamente com alguns métodos adicionais. Seu "relacionamento" com instâncias CSymbolinfo é ilustrado pela figura a seguir:

Ou seja, ele se comporta como se a classe CArrayObj, embora seja melhor usar a classe para armazenar objetos de um tipo específico (instâncias CSymbolinfo). Como a CSymbolinfo baseia-se na CArrayObj, que por sua vez, está disponível e em MQL4 e MQL5, podemos limitar a maior parte do código para o arquivo de cabeçalho principal, em seguida, invocar os arquivos de classe vazia para duas versões de linguagem diferente (dependendo de qual compilador é usado). Como resultado, temos uma estrutura de arquivo semelhante à descrita anteriormente para a classe CSymbolInfo:


Adição de caracteres

As instâncias Csymbolnfo são adicionadas de maneira semelhante ao método Add da classe CArrayObj. No entanto, ao contrário de CArrayObj, a instância adicionada deve ser exclusiva. Em outras palavras, deve haver dois elementos com o mesmo nome do símbolo.

bool CSymbolManagerBase::Add(CSymbolInfo *node)
  {
   if(Search(node.Name())==-1)
      return CArrayObj::Add(node);       
   return false;
  }

A classe derivada usará o método Search, que é semelhante a sobrecarga de método da classe CArrayObj. Por sua vez, este último não será utilizado, uma vez que usaria o método Compare da classe CSymbolInfo, que na verdade é um método da classe CObject (CSymbolInfo não tem o método explícito Compare). Uso do método Compare requer estender a classe CSymbolInfo. Isto pode ser uma decisão difícil, porque já temos cópias separadas desta classe. O novo método Search irá comparar os objetos com os símbolos depurados por eles:

int CSymbolManagerBase::Search(string symbol=NULL)
  {
   if(symbol==NULL)
      symbol= Symbol();
   for(int i=0;i<Total();i++)
     {
      CSymbolInfo *item=At(i);
      if(StringCompare(item.Name(),symbol)==0)
         return i;
     }
   return -1;
  }

Para EAS multi-moeda, na classe CSymbolManager, há um ponteiro para um caráter de prioridade. Isto pode ser como o caractere atual, como qualquer outro.  Vamos nos referir a ele mais vezes do que o resto. Nesta classe, podemos permitir que o EA estabeleça o caráter de prioridade durante a inicialização. Se isso não for feito, o símbolo prioritário será que a primeira instância processada CSymbolInfo:
void CSymbolManagerBase::SetPrimary(string symbol=NULL)
  {
   if(symbol==NULL)
      symbol= Symbol();
   m_symbol_primary=Get(symbol);
  }

CSymbolInfo *CSymbolManagerBase::GetPrimary(void)
  {
   if (!CheckPointer(m_symbol_primary) && Total()>0)
      SetPrimary(0);
   return m_symbol_primary;
  }


Atualização de símbolos

Para atualizar as cotações para todos os símbolos, no administrador de símbolos, nós apenas passamos por todos os objetos da matriz e chamamos para cada método Refreshrates(). Observe que, para uma enorme coleção de caracteres, recurso para este método não pode ser a opção mais prática. Neste caso, é melhor chamar a função RefreshRates na instância particular CSymbolInfo. O código a seguir mostra o método de classe RefreshRates:

bool CSymbolManagerBase::RefreshRates(void)
  {
   for(int i=0;i<Total();i++)
     {
      CSymbolInfo *symbol=At(i);
      if(!CheckPointer(symbol))
         continue;
      if(!symbol.RefreshRates())
         return false;
     }
   return true;
  }

Observe que os métodos Refresh e RefreshRates em CSymbolInfo variam. O primeiro é usado durante a inicialização, o segundo é para atualizar as informações sobre o símbolo no último tick processado.

Abaixo se mostra o código completo para a implementação de classe base CSymbolManager:

(SymbolManagerBase.mqh)

#include <Arrays\ArrayObj.mqh>
#include "..\Lib\SymbolInfo.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CSymbolManagerBase : public CArrayObj
  {
protected:
   CSymbolInfo      *m_symbol_primary;
   CObject          *m_container;
public:
                     CSymbolManagerBase(void);
                    ~CSymbolManagerBase(void);
   virtual bool      Add(CSymbolInfo*);
   virtual void      Deinit(void);
   CSymbolInfo      *Get(string);
   virtual bool      RefreshRates(void);
   virtual int       Search(string);
   virtual CObject *GetContainer(void);
   virtual void      SetContainer(CObject*);
   virtual void      SetPrimary(string);
   virtual void      SetPrimary(const int);
   virtual CSymbolInfo *GetPrimary(void);
   virtual string    GetPrimaryName(void);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolManagerBase::CSymbolManagerBase(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolManagerBase::~CSymbolManagerBase(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CObject *CSymbolManagerBase::GetContainer(void)
  {
   return m_container;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolManagerBase::SetContainer(CObject *container)
  {
   m_container=container;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolManagerBase::Deinit(void)
  {
   Shutdown();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSymbolManagerBase::Add(CSymbolInfo *node)
  {
   if(Search(node.Name())==-1)
      return CArrayObj::Add(node);       
   return false;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolInfo *CSymbolManagerBase::Get(string symbol=NULL)
  {
   if(symbol==NULL)
      symbol= Symbol();
   for(int i=0;i<Total();i++)
     {
      CSymbolInfo *item=At(i);
      if(!CheckPointer(item))
         continue;
      if(StringCompare(item.Name(),symbol)==0)
         return item;
     }
   return NULL;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSymbolManagerBase::RefreshRates(void)
  {
   for(int i=0;i<Total();i++)
     {
      CSymbolInfo *symbol=At(i);
      if(!CheckPointer(symbol))
         continue;
      if(!symbol.RefreshRates())
         return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSymbolManagerBase::Search(string symbol=NULL)
  {
   if(symbol==NULL)
      symbol= Symbol();
   for(int i=0;i<Total();i++)
     {
      CSymbolInfo *item=At(i);
      if(StringCompare(item.Name(),symbol)==0)
         return i;
     }
   return -1;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSymbolManagerBase::SetPrimary(string symbol=NULL)
  {
   if(symbol==NULL)
      symbol= Symbol();
   m_symbol_primary=Get(symbol);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSymbolManagerBase::SetPrimary(const int idx)
  {
   m_symbol_primary=At(idx);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolInfo *CSymbolManagerBase::GetPrimary(void)
  {
   if (!CheckPointer(m_symbol_primary) && Total()>0)
      SetPrimary(0);
   return m_symbol_primary;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string CSymbolManagerBase::GetPrimaryName(void)
  {
   if(CheckPointer(m_symbol_primary))
      return m_symbol_primary.Name();
   return NULL;
  }
//+------------------------------------------------------------------+
#ifdef __MQL5__
#include "..\..\MQL5\Symbol\SymbolManager.mqh"
#else
#include "..\..\MQL4\Symbol\SymbolManager.mqh"
#endif
//+------------------------------------------------------------------+

Todos os métodos são compatíveis com ambas as plataformas. Portanto, as classes específicas de plataforma, serão as mesmas e não conterão quaisquer métodos adicionais.

(SymbolManager.mqh, MQL4 and MQL5)

class CSymbolManager : public CSymbolManagerBase
  {
public:
                     CSymbolManager(void);
                    ~CSymbolManager(void);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolManager::CSymbolManager(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CSymbolManager::~CSymbolManager(void)
  {
  }
//+------------------------------------------------------------------+

CAccountInfo

Da mesma forma como foi feito com CSymbolInfo, será necessário comentar a maioria das funções sem suporte. As funções sem suporte, por exemplo, manipulação de modo de cálculo de margem (somente em MetaTrader 5), OrderCalcMargin e OrderCalcProfit, que não têm análogos diretos em MQL4. Assim, como no caso da CSymbolinfo, vamos criar cópias separadas da mesma classe para duas plataformas e, em seguida, alteramos a versão da classe para MQL4 para que possa ser processada pelo compilador relevante. A estrutura do arquivo é a mesma para outras classes discutidas anteriormente:


A classe modificada é mostrada abaixo.

//+------------------------------------------------------------------+
//|                                                  AccountInfo.mqh |
//|                   Copyright 2009-2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Object.mqh>
//+------------------------------------------------------------------+
//| Class CAccountInfo.                                              |
//| Finalidade: classe para acesso a informações sobre a conta de registro A                  |
//|              Derivada da classe CObject.                         |
//+------------------------------------------------------------------+
/class CAccountInfo : public CObject
  {
public:
                     CAccountInfo(void);
                    ~CAccountInfo(void);
   //--- métodos de acesso rápido as propriedades integrais de conta
   long              Login(void) const;
   ENUM_ACCOUNT_TRADE_MODE TradeMode(void) const;
   string            TradeModeDescription(void) const;
   long              Leverage(void) const;
   ENUM_ACCOUNT_STOPOUT_MODE StopoutMode(void) const;
   string            StopoutModeDescription(void) const;
   //ENUM_ACCOUNT_MARGIN_MODE MarginMode(void) const;
   //string            MarginModeDescription(void) const;
   bool              TradeAllowed(void) const;
   bool              TradeExpert(void) const;
   int               LimitOrders(void) const;
   //--- métodos de acesso rápido as propriedades não numéricas de conta
   double            Balance(void) const;
   double            Credit(void) const;
   double            Profit(void) const;
   double            Equity(void) const;
   double            Margin(void) const;
   double            FreeMargin(void) const;
   double            MarginLevel(void) const;
   double            MarginCall(void) const;
   double            MarginStopOut(void) const;
   //--- métodos de acesso rápido as propriedades de cadeias de carateres de conta
   string            Name(void) const;
   string            Server(void) const;
   string            Currency(void) const;
   string            Company(void) const;
   //--- методы доступа к функциям API MQL5
   long              InfoInteger(const ENUM_ACCOUNT_INFO_INTEGER prop_id) const;
   double            InfoDouble(const ENUM_ACCOUNT_INFO_DOUBLE prop_id) const;
   string            InfoString(const ENUM_ACCOUNT_INFO_STRING prop_id) const;
   //--- проверки
   //double            OrderProfitCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                      //const double volume,const double price_open,const double price_close) const;
   //double            MarginCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                 //const double volume,const double price) const;
   //double            FreeMarginCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                     //const double volume,const double price) const;
   //double            MaxLotCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                 //const double price,const double percent=100) const;
  };
//+------------------------------------------------------------------+
//| Construtor                                                      |
//+------------------------------------------------------------------+
CAccountInfo::CAccountInfo(void)
  {
  }
//+------------------------------------------------------------------+
//| Destrutor                                                       |
//+------------------------------------------------------------------+
CAccountInfo::~CAccountInfo(void)
  {
  }
//+------------------------------------------------------------------+
//| obtemos o valor da propriedade "ACCOUNT_LOGIN"                           |
//+------------------------------------------------------------------+
long CAccountInfo::Login(void) const
  {
   return(AccountInfoInteger(ACCOUNT_LOGIN));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_TRADE_MODE"                      |
//+------------------------------------------------------------------+
ENUM_ACCOUNT_TRADE_MODE CAccountInfo::TradeMode(void) const
  {
   return((ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_TRADE_MODE" as string            |
//+------------------------------------------------------------------+
string CAccountInfo::TradeModeDescription(void) const
  {
   string str;
//--- 
   switch(TradeMode())
     {
      case ACCOUNT_TRADE_MODE_DEMO   : str="Demo trading account";    break;
      case ACCOUNT_TRADE_MODE_CONTEST: str="Contest trading account"; break;
      case ACCOUNT_TRADE_MODE_REAL   : str="Real trading account";    break;
      default                        : str="Unknown trade account";
     }
//--- 
   return(str);
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_LEVERAGE"                        |
//+------------------------------------------------------------------+
long CAccountInfo::Leverage(void) const
  {
   return(AccountInfoInteger(ACCOUNT_LEVERAGE));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_SO_MODE"                  |
//+------------------------------------------------------------------+
ENUM_ACCOUNT_STOPOUT_MODE CAccountInfo::StopoutMode(void) const
  {
   return((ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_SO_MODE" como cadeia de carateres        |
//+------------------------------------------------------------------+
string CAccountInfo::StopoutModeDescription(void) const
  {
   string str;
//--- 
   switch(StopoutMode())
     {
      case ACCOUNT_STOPOUT_MODE_PERCENT: str="Level is specified in percentage"; break;
      case ACCOUNT_STOPOUT_MODE_MONEY  : str="Level is specified in money";      break;
      default                          : str="Unknown stopout mode";
     }
//--- 
   return(str);
  }
/*
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_MODE"                     |
//+------------------------------------------------------------------+
ENUM_ACCOUNT_MARGIN_MODE CAccountInfo::MarginMode(void) const
  {
   return((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
  }
*/
/*
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_MODE" como cadeia de carateres           |
//+------------------------------------------------------------------+
string CAccountInfo::MarginModeDescription(void) const
  {
   string str;
//--- 
   switch(MarginMode())
     {
      case ACCOUNT_MARGIN_MODE_RETAIL_NETTING: str="Netting";  break;
      case ACCOUNT_MARGIN_MODE_EXCHANGE      : str="Exchange"; break;
      case ACCOUNT_MARGIN_MODE_RETAIL_HEDGING: str="Hedging";  break;
      default                                : str="Unknown margin mode";
     }
//--- 
   return(str);
  }
*/
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_TRADE_ALLOWED"                   |
//+------------------------------------------------------------------+
bool CAccountInfo::TradeAllowed(void) const
  {
   return((bool)AccountInfoInteger(ACCOUNT_TRADE_ALLOWED));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_TRADE_EXPERT"                    |
//+------------------------------------------------------------------+
bool CAccountInfo::TradeExpert(void) const
  {
   return((bool)AccountInfoInteger(ACCOUNT_TRADE_EXPERT));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_LIMIT_ORDERS"                    |
//+------------------------------------------------------------------+
int CAccountInfo::LimitOrders(void) const
  {
   return((int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_BALANCE"                         |
//+------------------------------------------------------------------+
double CAccountInfo::Balance(void) const
  {
   return(AccountInfoDouble(ACCOUNT_BALANCE));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_CREDIT"                          |
//+------------------------------------------------------------------+
double CAccountInfo::Credit(void) const
  {
   return(AccountInfoDouble(ACCOUNT_CREDIT));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_PROFIT"                          |
//+------------------------------------------------------------------+
double CAccountInfo::Profit(void) const
  {
   return(AccountInfoDouble(ACCOUNT_PROFIT));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_EQUITY"                          |
//+------------------------------------------------------------------+
double CAccountInfo::Equity(void) const
  {
   return(AccountInfoDouble(ACCOUNT_EQUITY));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN"                          |
//+------------------------------------------------------------------+
double CAccountInfo::Margin(void) const
  {
   return(AccountInfoDouble(ACCOUNT_MARGIN));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_FREEMARGIN"                      |
//+------------------------------------------------------------------+
double CAccountInfo::FreeMargin(void) const
  {
   return(AccountInfoDouble(ACCOUNT_FREEMARGIN));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_LEVEL"                    |
//+------------------------------------------------------------------+
double CAccountInfo::MarginLevel(void) const
  {
   return(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_SO_CALL"                  |
//+------------------------------------------------------------------+
double CAccountInfo::MarginCall(void) const
  {
   return(AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_MARGIN_SO_SO"                    |
//+------------------------------------------------------------------+
double CAccountInfo::MarginStopOut(void) const
  {
   return(AccountInfoDouble(ACCOUNT_MARGIN_SO_SO));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_NAME"                            |
//+------------------------------------------------------------------+
string CAccountInfo::Name(void) const
  {
   return(AccountInfoString(ACCOUNT_NAME));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_SERVER"                          |
//+------------------------------------------------------------------+
string CAccountInfo::Server(void) const
  {
   return(AccountInfoString(ACCOUNT_SERVER));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_CURRENCY"                        |
//+------------------------------------------------------------------+
string CAccountInfo::Currency(void) const
  {
   return(AccountInfoString(ACCOUNT_CURRENCY));
  }
//+------------------------------------------------------------------+
//| Obtemos o valor da propriedade "ACCOUNT_COMPANY"                         |
//+------------------------------------------------------------------+
string CAccountInfo::Company(void) const
  {
   return(AccountInfoString(ACCOUNT_COMPANY));
  }
//+------------------------------------------------------------------+
//| Função de acesso AccountInfoInteger(...)                         |
//+------------------------------------------------------------------+
long CAccountInfo::InfoInteger(const ENUM_ACCOUNT_INFO_INTEGER prop_id) const
  {
   return(AccountInfoInteger(prop_id));
  }
//+------------------------------------------------------------------+
//| Função de acesso AccountInfoDouble(...)                          |
//+------------------------------------------------------------------+
double CAccountInfo::InfoDouble(const ENUM_ACCOUNT_INFO_DOUBLE prop_id) const
  {
   return(AccountInfoDouble(prop_id));
  }
//+------------------------------------------------------------------+
//| Função de acesso AccountInfoString(...)                          |
//+------------------------------------------------------------------+
string CAccountInfo::InfoString(const ENUM_ACCOUNT_INFO_STRING prop_id) const
  {
   return(AccountInfoString(prop_id));
  }
/*
//+------------------------------------------------------------------+
//| Função de acesso OrderCalcProfit(...).                            |
//| INPUT:  name            - nome do símbolo,                           |
//|         trade_operation - operação de negociação                       |
//|         volume          - volume da posição aberta,        |
//|         price_open      - preço da apertura de posição,         |
//|         price_close     - preço de fechamento da posição.         |
//+------------------------------------------------------------------+
double CAccountInfo::OrderProfitCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                      const double volume,const double price_open,const double price_close) const
  {
   double profit=EMPTY_VALUE;
//--- 
   if(!OrderCalcProfit(trade_operation,symbol,volume,price_open,price_close,profit))
      return(EMPTY_VALUE);
//--- 
   return(profit);
  }
*/
/*
//+------------------------------------------------------------------+
//| Função de acesso OrderCalcMargin(...).                           |
//| INPUT:  name            - nome do símbolo,                           |
//|         trade_operation - operação de negociação                       |
//|         volume          - volume da posição aberta,        |
//|         price           - preço de abertura de posição.         |
//+------------------------------------------------------------------+
double CAccountInfo::MarginCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                 const double volume,const double price) const
  {
   double margin=EMPTY_VALUE;
//--- 
   if(!OrderCalcMargin(trade_operation,symbol,volume,price,margin))
      return(EMPTY_VALUE);
//--- 
   return(margin);
  }
*/
/*
//+------------------------------------------------------------------+
//| Função de acesso OrderCalcMargin(...).                           |
//| INPUT:  name            - nome do símbolo,                           |
//|         trade_operation - operação de negociação                       |
//|         volume          - volume da posição aberta,        |
//|         price           - preço de abertura de posição.         |
//+------------------------------------------------------------------+
double CAccountInfo::FreeMarginCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                     const double volume,const double price) const
  {
   return(FreeMargin()-MarginCheck(symbol,trade_operation,volume,price));
  }
*/
/*
//+------------------------------------------------------------------+
//| Função de acesso OrderCalcMargin(...).                           |
//| INPUT:  name            - nome do símbolo,                           |
//|         trade_operation - operação de negociação                       |
//|         price           - preço da apertura de posição,         |
//|         percent         - margem livre [1-100%].  |
//+------------------------------------------------------------------+
double CAccountInfo::MaxLotCheck(const string symbol,const ENUM_ORDER_TYPE trade_operation,
                                 const double price,const double percent) const
  {
   double margin=0.0;
//--- проверки
   if(symbol=="" || price<=0.0 || percent<1 || percent>100)
     {
      Print("CAccountInfo::MaxLotCheck invalid parameters");
      return(0.0);
     }
//--- calculate margin requirements for 1 lot
   if(!OrderCalcMargin(trade_operation,symbol,1.0,price,margin) || margin<0.0)
     {
      Print("CAccountInfo::MaxLotCheck margin calculation failed");
      return(0.0);
     }
//--- 
   if(margin==0.0) // para ordens pendentes
      return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX));
//--- cálculo do volume máximo
   double volume=NormalizeDouble(FreeMargin()*percent/100.0/margin,2);
//--- normalização e teste de limites
   double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
   if(stepvol>0.0)
      volume=stepvol*MathFloor(volume/stepvol);
//--- 
   double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   if(volume<minvol)
      volume=0.0;
//--- 
   double maxvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   if(volume>maxvol)
      volume=maxvol;
//--- Retorna-se o volume 
   return(volume);
  }
*/
//+------------------------------------------------------------------+


A plataforma pode obter o acesso simultaneamente apenas a partir uma conta. Assim, criar uma coleção de instâncias CAccountInfo não é mais necessário para nós.

CExpertTrade

Apesar dos mesmos objetivos finais, MQL4 e MQL5 diferem na execução de operações de negociação. Isto significa que temos a oportunidade de usar a classe CExpertTrade em Expert Advisors multiplataformas consultor na versão para MQL5, mas não completamente para MQL4. Neste caso, para a versão MQL4, criamos um novo arquivo de cabeçalho que contém a classe de mesmo nome. Armazenamos o arquivo de cabeçalho base de classe, em conformidade com a seguinte estrutura:

Essa classe irá emular os membros e métodos de classe CExpertTrade, localizados na versão MQL5. Assim, esse código é executado:

CExpertTrade trade;
trade.Buy(/*params*/);

ambas as versões têm a capacidade de "entender" que pretendemos introduzir uma posição longa.

Os requisitos mínimos consistem em que para emular os métodos para entrar numa posição. Estes são os métodos Buy e Sell na classe CExpertTrade. Para sair do mercado, nossas implementações serão diferente: OrderClose para a versão MQL4, enquanto para MQL5 serão usados os métodos "nativos" PositionClose (durante a cobertura) ou Buy e Sell (durante a compensação). O código a seguir mostra a versão da classe CExpertTrade para MQL4. Este é um dos pontos onde o código pode variar significativamente no desempenho de programadores diferentes:

#include <Arrays\ArrayInt.mqh>
enum ENUM_ORDER_TYPE_TIME
  {
   ORDER_TIME_GTC,
   ORDER_TIME_DAY,
   ORDER_TIME_SPECIFIED,
   ORDER_TIME_SPECIFIED_DAY
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CExpertTrade : public CObject
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
  {
protected:
   int               m_magic;
   ulong             m_deviation;
   ENUM_ORDER_TYPE_TIME m_order_type_time;
   datetime          m_order_expiration;
   bool              m_async_mode;
   uint              m_retry;
   int               m_sleep;
   color             m_color_long;
   color             m_color_short;
   color             m_color_buystop;
   color             m_color_buylimit;
   color             m_color_sellstop;
   color             m_color_selllimit;
   color             m_color_modify;
   color             m_color_exit;
   CSymbolInfo      *m_symbol;
public:
                     CExpertTrade(void);
                    ~CExpertTrade(void);
   //--- métodos de instalação e obtenção
   color             ArrowColor(const ENUM_ORDER_TYPE type);
   uint              Retry() {return m_retry;}
   void              Retry(uint retry){m_retry=retry;}
   int               Sleep() {return m_sleep;}
   void              Sleep(int sleep){m_sleep=sleep;}
   void              SetAsyncMode(const bool mode) {m_async_mode=mode;}
   void              SetExpertMagicNumber(const int magic) {m_magic=magic;}
   void              SetDeviationInPoints(const ulong deviation) {m_deviation=deviation;}
   void              SetOrderExpiration(const datetime expire) {m_order_expiration=expire;}
   bool              SetSymbol(CSymbolInfo *);
   //-- métodos de negociação   
   virtual ulong     Buy(const double,const double,const double,const double,const string);
   virtual ulong     Sell(const double,const double,const double,const double,const string);
   virtual bool      OrderDelete(const ulong);
   virtual bool      OrderClose(const ulong,const double,const double);
   virtual bool      OrderCloseAll(CArrayInt *,const bool);
   virtual bool      OrderModify(const ulong,const double,const double,const double,const ENUM_ORDER_TYPE_TIME,const datetime,const double);
   virtual ulong     OrderOpen(const string,const ENUM_ORDER_TYPE,const double,const double,const double,const double,const double,const ENUM_ORDER_TYPE_TIME,const datetime,const string);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTrade::CExpertTrade(void) : m_magic(0),
                                   m_deviation(10),
                                   m_order_type_time(0),
                                   m_symbol(NULL),
                                   m_async_mode(0),
                                   m_retry(3),
                                   m_sleep(100),
                                   m_color_long(clrGreen),
                                   m_color_buystop(clrGreen),
                                   m_color_buylimit(clrGreen),
                                   m_color_sellstop(clrRed),
                                   m_color_selllimit(clrRed),
                                   m_color_short(clrRed),
                                   m_color_modify(clrNONE),
                                   m_color_exit(clrNONE)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTrade::~CExpertTrade(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CExpertTrade::SetSymbol(CSymbolInfo *symbol)
  {
   if(symbol!=NULL)
     {
      m_symbol=symbol;
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CExpertTrade::OrderModify(const ulong ticket,const double price,const double sl,const double tp,
                               const ENUM_ORDER_TYPE_TIME type_time,const datetime expiration,const double stoplimit=0.0)
  {
   return ::OrderModify((int)ticket,price,sl,tp,expiration,m_color_modify);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CExpertTrade::OrderDelete(const ulong ticket)
  {
   return ::OrderDelete((int)ticket);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ulong CExpertTrade::Buy(const double volume,const double price,const double sl,const double tp,const string comment="")
  {
   if(m_symbol==NULL)
      return false;
   m_symbol.RefreshRates();
   string symbol=m_symbol.Name();
   double stops_level=m_symbol.StopsLevel()*m_symbol.Point();
   double ask=m_symbol.Ask();
   if(symbol=="")
      return 0;
   if(price!=0)
     {
      if(price>ask+stops_level)
         return OrderOpen(symbol,ORDER_TYPE_BUY_STOP,volume,0.0,price,sl,tp,m_order_type_time,m_order_expiration,comment);
      if(price<ask-stops_level)
         return OrderOpen(symbol,ORDER_TYPE_BUY_LIMIT,volume,0.0,price,sl,tp,m_order_type_time,m_order_expiration,comment);
     }
   return OrderOpen(symbol,ORDER_TYPE_BUY,volume,0.0,ask,sl,tp,m_order_type_time,m_order_expiration,comment);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ulong CExpertTrade::Sell(const double volume,const double price,const double sl,const double tp,const string comment="")
  {
   if(m_symbol==NULL)
      return false;
   m_symbol.RefreshRates();
   string symbol=m_symbol.Name();
   double stops_level=m_symbol.StopsLevel()*m_symbol.Point();
   double bid=m_symbol.Bid();
   if(symbol=="")
      return 0;
   if(price!=0)
     {
      if(price>bid+stops_level)
         return OrderOpen(symbol,ORDER_TYPE_SELL_LIMIT,volume,0.0,price,sl,tp,m_order_type_time,m_order_expiration,comment);
      if(price<bid-stops_level)
         return OrderOpen(symbol,ORDER_TYPE_SELL_STOP,volume,0.0,price,sl,tp,m_order_type_time,m_order_expiration,comment);
     }
   return OrderOpen(symbol,ORDER_TYPE_SELL,volume,0.0,bid,sl,tp,m_order_type_time,m_order_expiration,comment);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ulong CExpertTrade::OrderOpen(const string symbol,const ENUM_ORDER_TYPE order_type,const double volume,
                              const double limit_price,const double price,const double sl,const double tp,
                              const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,const datetime expiration=0,
                              const string comment="")
  {
   bool res;
   ulong ticket=0;
   color arrowcolor=ArrowColor(order_type);
   datetime expire=0;
   if(order_type>1 && expiration>0) expire=expiration*1000+TimeCurrent();
   double stops_level=m_symbol.StopsLevel();
   for(uint i=0;i<m_retry;i++)
     {
      if(ticket>0)
         break;
      if(IsStopped())
         return 0;
      if(IsTradeContextBusy() || !IsConnected())
        {
         ::Sleep(m_sleep);
         continue;
        }
      if(stops_level==0 && order_type<=1)
        {
         ticket=::OrderSend(symbol,order_type,volume,price,(int)(m_deviation*m_symbol.Point()),0,0,comment,m_magic,expire,arrowcolor);
         ::Sleep(m_sleep);
         for(uint j=0;j<m_retry;j++)
           {
            if(res) break;
            if(ticket>0 && (sl>0 || tp>0))
              {
               if(OrderSelect((int)ticket,SELECT_BY_TICKET))
                 {
                  res=OrderModify((int)ticket,OrderOpenPrice(),sl,tp,OrderExpiration());
                  ::Sleep(m_sleep);
                 }
              }
           }
        }
      else
        {
         ticket=::OrderSend(symbol,order_type,volume,price,(int)m_deviation,sl,tp,comment,m_magic,expire,arrowcolor);
         ::Sleep(m_sleep);
        }
     }
   return ticket>0?ticket:0;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CExpertTrade::OrderClose(const ulong ticket,const double lotsize=0.0,const double price=0.0)
  {
   if(!OrderSelect((int)ticket,SELECT_BY_TICKET))
      return false;
   if(OrderCloseTime()>0)
      return true;
   double close_price=0.0;
   int deviation=0;
   if(OrderSymbol()==m_symbol.Name() && price>0.0)
     {
      close_price=NormalizeDouble(price,m_symbol.Digits());
      deviation=(int)(m_deviation*m_symbol.Point());
     }
   else
     {
      close_price=NormalizeDouble(OrderClosePrice(),(int)MarketInfo(OrderSymbol(),MODE_DIGITS));
      deviation=(int)(m_deviation*MarketInfo(OrderSymbol(),MODE_POINT));
     }
   double lots=(lotsize>0.0 || lotsize>OrderLots())?lotsize:OrderLots();
   return ::OrderClose((int)ticket,lots,close_price,deviation,m_color_exit);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CExpertTrade::OrderCloseAll(CArrayInt *other_magic,const bool restrict_symbol=true)
  {
   bool res=true;
   int total= OrdersTotal();
   for(int i=total-1;i>=0;i--)
     {
      double bid=0.0,ask=0.0;
      if(!OrderSelect(i,SELECT_BY_POS)) continue;
      if(OrderSymbol()!=m_symbol.Name() && restrict_symbol) continue;
      if(OrderMagicNumber()!=m_magic && other_magic.Search(OrderMagicNumber())<0) continue;
      m_symbol.RefreshRates();
      if(OrderSymbol()==m_symbol.Name())
        {
         bid = m_symbol.Bid();
         ask = m_symbol.Ask();
        }
      else
        {
         bid = MarketInfo(OrderSymbol(),MODE_BID);
         ask = MarketInfo(OrderSymbol(),MODE_ASK);
        }
      if(res) res=OrderClose(OrderTicket(),OrderLots(),OrderType()==ORDER_TYPE_BUY?bid:ask);
     }
   return res;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
color CExpertTrade::ArrowColor(const ENUM_ORDER_TYPE type)
  {
   switch(type)
     {
      case ORDER_TYPE_BUY:        return m_color_long;
      case ORDER_TYPE_SELL:       return m_color_short;
      case ORDER_TYPE_BUY_STOP:   return m_color_buystop;
      case ORDER_TYPE_BUY_LIMIT:  return m_color_buylimit;
      case ORDER_TYPE_SELL_STOP:  return m_color_sellstop;
      case ORDER_TYPE_SELL_LIMIT: return m_color_selllimit;
     }
   return clrNONE;
  }
//+------------------------------------------------------------------+

Observe que, na versão MQL5, a classe CExpertTrade herda da classe CTrade (que, por sua vez, estende a CObject). Por outro lado, na versão MQL4 a classe CExpertTrade é diretamente herdada de CObject. Isto significa que na versão para MQL4, as classes CTrade e CExpertTrade são combinados num único objeto.

Algumas características, tais como ORDER_TYPE_TIME não têm análogos diretos em MQL4. No entanto, estas classes de negociação podem ser úteis no caso se expandi-las e emular o tempo de expiração de ordem MQL5 em MQL4.< br1 />

CTradeManager

Na classe CExpertTrade, que pertence a MQL5, existe o método SetSymbol. Esse método permite alternar o ponteiro para obter informações sobre o símbolo para que ele aponte para as informações da instância sobre outro símbolo (CSymbolInfo). Usando esta configuração, a maioria, localizada na classe de métodos, já não precisa ter o parâmetro de sequência de caracteres do símbolo que indica o nome de instrumento para processamento.

Na maioria dos casos, basta usar o CExpertTrade e a alternância simples de símbolos. No entanto, essa abordagem tem algumas ressalvas. Em alguns casos, quando você alterar o ponteiro de símbolo, você precisará atualizar o desvio ou a derrapagem máxima. Esta ressalva é válida, por exemplo, quando o EA opera com instrumentos que possuem um número diferente de casas decimais. Outro fator é o número mágico, ele deve ser atualizado se o Expert Advisor escolhe usar magics diferentes para as posições abertas para cada símbolo usado.

Uma maneira de conseguir isso é usar o "gerente comercial". Da mesma forma como o gerente de símbolos, discutido anteriormente, este objeto irá expandir a classe CArrayObj, e, na verdade, tem o mesmo conjunto de métodos de CSymbolManager. O arquivo de cabeçalho principal irá se referir o classe herdeiro correto em conformidade com o seu compilador. Como o gerente de símbolos, o gerente de negociação vai lidar com armazenar e recuperar dados. Assim, uma grande parte do seu código estará no arquivo de cabeçalho base. A estrutura do arquivo é mostrada na figura a seguir.

O código de classe base de nosso gerente de negociação é mostrado abaixo.

(TradeManagerBase.mqh)

#include <Arrays\ArrayObj.mqh>
#include "ExpertTradeXBase.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CTradeManagerBase : public CArrayObj
  {
public:
                     CTradeManagerBase(void);
                    ~CTradeManagerBase(void);
   virtual int       Search(string);
   virtual bool      Add(CExpertTradeX*);
   virtual void      Deinit(void);
   CExpertTrade     *Get(const string);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CTradeManagerBase::CTradeManagerBase(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CTradeManagerBase::~CTradeManagerBase(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CTradeManagerBase::Deinit(void)
  {
   Shutdown();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CTradeManagerBase::Add(CExpertTradeX *node)
  {
   if(Search(node.Name())==-1)
      return CArrayObj::Add(node);
   return false;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CTradeManagerBase::Search(string symbol=NULL)
  {
   if(symbol==NULL)
      symbol= Symbol();
   for(int i=0;i<Total();i++)
     {
      CExpertTradeX *item=At(i);
      if(StringCompare(item.Name(),symbol)==0)
         return i;
     }
   return -1;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTrade *CTradeManagerBase::Get(const string name=NULL)
  {
   if(name==NULL && Total()>0)
      return At(0);
   for(int i=0;i<Total();i++)
     {
      CExpertTradeX *item=At(i);
      if(StringCompare(item.Name(),name)==0)
         return item;
     }
   return NULL;
  }
//+------------------------------------------------------------------+
#ifdef __MQL5__
#include "..\..\MQL5\Trade\TradeManager.mqh"
#else
#include "..\..\MQL4\Trade\TradeManager.mqh"
#endif
//+------------------------------------------------------------------+

Também é possível simplesmente usar uma instância da classe CExpertTrade e estendê-la para que ele tenha sua própria coleção de caracteres para alternar. Mas este método utiliza o espaço de memória adicional, porque também é necessário armazenar magics e cálculos dos desvios relacionados com certos caracteres.

Como o gerente de símbolos, essa classe usa também o método personalizado Search para comparar dois instrumentos financeiros (que devem ser exclusivos). A comparação é feita usando os nomes dos instrumentos financeiros, que, por sua vez, baseiam-se nos nomes de instância da classe CSymbolInfo a que se referem. No entanto, para a versão MQL5, o objeto retorna o ponteiro para o símbolo, nem o nome do símbolo. Neste caso, nós precisamos estender CExpertTrade para permitir a instância desse objeto retornar o nome do símbolo, que ele contém. Damos o nome CExpertTradeX. Tal como o resto das classes descritas neste artigo, ele tem um arquivo de cabeçalho base, que define quais arquivos de cabeçalho herdeiros são referidos dependendo do compilador a ser utilizado:

O trecho de código a seguir mostra uma implementação básica da classe acima:

(CExpertTradeXBase.mqh)

#include "ExpertTradeBase.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CExpertTradeXBase : public CExpertTrade
  {
public:
                     CExpertTradeXBase(void);
                    ~CExpertTradeXBase(void);
   string            Name(void);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTradeXBase::CExpertTradeXBase(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTradeXBase::~CExpertTradeXBase(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string CExpertTradeXBase::Name(void)
  {
   if (CheckPointer(m_symbol))
      return m_symbol.Name();
   return NULL;
  }
//+------------------------------------------------------------------+
#ifdef __MQL5__
#include "..\..\MQL5\Trade\ExpertTradeX.mqh"
#else 
#include "..\..\MQL4\Trade\ExpertTradeX.mqh"
#endif
//+------------------------------------------------------------------+

Observe que essa classe CExpertTradeX estende a classe CExpertTrade. Pode parecer que este objeto herda de um único objeto. Na realidade, no entanto, a versão para MQL4 e MQL5 têm versões CExpertTrade diferentes. Este é um simples objeto e seus métodos são compatíveis com ambas as plataformas, assim como algumas classes, anunciamos as classes específicas de plataforma sem métodos adicionais:

(CExpertTradeX.mqh, versão para MQL4 e MQL5)

class CExpertTradeX : public CExpertTradeXBase
  {
public:
                     CExpertTradeX(void);
                    ~CExpertTradeX(void);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTradeX::CExpertTradeX(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpertTradeX::~CExpertTradeX(void)
  {
  }
//+------------------------------------------------------------------+


Conclusão

Neste artigo, demonstramos como alguns componentes da Biblioteca padrão de MQL5 podem ser convertidos para usá-los em Expert Advisor, escritos para MQL4, que aliviam da necessidade de codificar essas classes a partir do zero para a versão MQL4. As classes CSymbolInfo, CAccount e CExpertTrade foram modificadas, enquanto os gerentes de classes —CSymbolManager e CTradeManager— foram projetados para permitir-lhes gerenciar várias instâncias de um dos objetos acima.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/2574

Arquivos anexados |
MQLx-Reuse.zip (92.47 KB)
Interfaces Gráficas VI: Os Controles Deslizante e Deslizante Duplo (Capítulo 2) Interfaces Gráficas VI: Os Controles Deslizante e Deslizante Duplo (Capítulo 2)
No artigo anterior, nós enriquecemos a nossa biblioteca com quatro controles que são frequentemente usados ​​nas interfaces gráficas: caixa de seleção, campo de edição, campo de edição com caixa de seleção e a lista combinada com a caixa de seleção. O segundo capítulo da sexta parte será dedicado aos controles deslizante e deslizante duplo.
Expert Advisor multiplataforma: Introdução Expert Advisor multiplataforma: Introdução
Este artigo descreve um método que permite desenvolver rápida e facilmente um Expert Advisor multiplataforma. O método proposto combina as características, comuns para ambas as versões, numa classe e desenvolve a implementação para funções incompatíveis nas classes herdadas.
Expert Advisor multiplataforma: Ordens Expert Advisor multiplataforma: Ordens
MetaTrader 4 e MetaTrader 5 usam regras diferentes para o processamento de pedidos de negociação. Este artigo discute a possibilidade de utilizar o objeto de classe que representa a transação para processamento pelo servidor, graças a isso o Expert Advisor poderá trabalhar com elas independentemente da versão da plataforma de negociação e o modo usado.
Interfaces Gráficas VI: Os Controles Caixa de Seleção, Campo de Edição e seus Tipos Combinados (Capítulo 1) Interfaces Gráficas VI: Os Controles Caixa de Seleção, Campo de Edição e seus Tipos Combinados (Capítulo 1)
Este artigo é o começo da sexta parte da série dedicada ao desenvolvimento da biblioteca para a criação de interfaces gráficas nos terminais MetaTrader. No primeiro capítulo, nós vamos discutir o controle caixa de seleção, o controle campo de edição e seus tipos combinados.