Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte IX): Compatibilidade com a MQL4 - Preparação dos dados

Artyom Trishkin | 21 agosto, 2019

Conteúdo

Nas partes anteriores da série de artigos, nós preparamos as seguintes ferramentas para a biblioteca multi-plataforma da MetaTrader 5 e MetaTrader 4:

Agora é hora de implementar a compatibilidade da biblioteca com a MQL4, já que nós vamos desenvolver as classes de negociação, e a biblioteca deve funcionar corretamente tanto para a MQL5 quanto para a MQL4.

Neste artigo, nós começaremos a melhorar a biblioteca para implementar o seu conceito de multi-plataforma.

MQL4 vs MQL5

Copie a pasta inteira da biblioteca para o diretório apropriado da MetaTrader 4 \MQL4\Include\DoEasy. Vamos fazer o teste dos EAs a partir das pastas apropriadas contendo os EAs em MQL5 e salvá-los com a extensão *.mq4 para a pasta do EA \MQL4\Experts\TestDoEasy (para a pasta correspondente ao número do artigo, que neste caso é a Part09).

Encontre a pasta da biblioteca \MQL4\Include\DoEasy no Navegador do Editor, clique com o botão direito nele e selecione Compilar.


Isso irá compilar todos os arquivos da biblioteca, resultando em mais de dois mil erros de compilação:


Se nós analisarmos os erros obtidos, nós veremos que sua grande maioria tem a ver com as constantes e enumerações da MQL5 que a MQL4 não sabe nada a respeito. Isso significa que nós precisamos informar à MQL4 sobre as constantes usadas na biblioteca. Há também os erros de diversas naturezas, como a ausência de certas funções, o que significa que nós implementaremos a sua lógica de operação usando as funções MQL4.

Além disso, os sistemas de ordens da MQL4 e MQL5 são muito diferentes. Nós teremos que implementar um manipulador de eventos separado para a MQL4 diferente daquele implementado na MQL5, uma vez que a lista do histórico de ordens na MQL4 fornece muito menos dados sobre as ordens (e nenhum dado sobre os negócios), o que significa que não podemos receber os dados sobre as ordens e negócios diretamente a partir das listas do terminal. Aqui, nós teremos que comparar logicamente os eventos que ocorrem nas listas de ordens de mercado ativas e as do histórico e definir os eventos ocorridos com base na comparação.

Melhorando a biblioteca

Na pasta raiz da biblioteca DoEasy, nós criamos o novo arquivo de inclusão ToMQL4.mqh. Aqui nós vamos descrever todas as constantes e enumerações necessárias para a MQL4. Incluímos no arquivo Defines.mqh para a compilação em MQL4 no início da listagem de Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#ifdef __MQL4__      
#include "ToMQL4.mqh"
#endif               
//+------------------------------------------------------------------+

Depois disso, toda a biblioteca em MQL4 será capaz de ver o que está escrito no arquivo ToMQL4.mqh durante a compilação.

Vamos para o início da lista de erros na aba Erros da Caixa de Ferramentas do Editor, pressionando a tecla NUMPAD HOME ou simplesmente rolando para cima até o início. Clique duplo no primeiro erro:


O editor nos move para a linha de erro no arquivo Defines.mqh:

//+------------------------------------------------------------------+
//| List of possible trading events on the account                   |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // No trading event
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // Pending order placed
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // Pending order removed
//--- enumeration members matching the ENUM_DEAL_TYPE enumeration members
//--- (constant order below should not be changed, no constants should be added/deleted)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Charging credit (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // Additional charges

Naturalmente, a MQL4 não sabe nada sobre os negócios e seus tipos. Isso deve ser corrigido. Simplesmente abra o guia de Referência MQL5 e procure pelos dados nas propriedade do negócio usando a consulta DEAL_TYPE_CREDIT:

ID

Descrição

Tipo

DEAL_TICKET

Ticket do negócio. Número único atribuído a cada negócio

long

DEAL_ORDER

número da ordem do negócio

long

DEAL_TIME

Horário do negócio

datetime

DEAL_TIME_MSC

O horário do execução de um negócio em milissegundos desde 01.01.1970

long

DEAL_TYPE

Tipo do negócio

ENUM_DEAL_TYPE

DEAL_ENTRY

Direção do negócio - entrada, saída ou reversão do mercado

ENUM_DEAL_ENTRY

DEAL_MAGIC

Número mágico do negócio (ver ORDER_MAGIC)

long

DEAL_REASON

O motivo ou a origem da execução da negociação

ENUM_DEAL_REASON

DEAL_POSITION_ID

O ID da posição, que o negócio abriu, modificou ou encerrou. Cada posição tem um ID único que é atribuído a todos os negócios executados no símbolo durante a vida útil da posição.

long


Na tabela, nós estamos mais interessados no ENUM_DEAL_TYPE. Siga o link e obtenha a lista de todos os tipos de negócio:

ID

Descrição

DEAL_TYPE_BUY

Compra

DEAL_TYPE_SELL

Venda

DEAL_TYPE_BALANCE

Saldo

DEAL_TYPE_CREDIT

Crédito

DEAL_TYPE_CHARGE

Cobranças adicionais

DEAL_TYPE_CORRECTION

Correção

DEAL_TYPE_BONUS

Bônus

DEAL_TYPE_COMMISSION

Comissão adicional

DEAL_TYPE_COMMISSION_DAILY

Comissão diária

DEAL_TYPE_COMMISSION_MONTHLY

Comissão mensal

DEAL_TYPE_COMMISSION_AGENT_DAILY

Comissão diária do Agente

DEAL_TYPE_COMMISSION_AGENT_MONTHLY

Comissão Mensal do Agente

DEAL_TYPE_INTEREST

Taxa de juro

DEAL_TYPE_BUY_CANCELED

Contrato de compra cancelado. Pode haver uma situação em que um negócio de compra executado anteriormente seja cancelada. Nesse caso, o tipo de negócio executado anteriormente (DEAL_TYPE_BUY) é alterado para DEAL_TYPE_BUY_CANCELED e seu lucro/perda é zerado. O lucro/perda anteriormente obtido é cobrado/retirado usando uma operação de saldo separada

DEAL_TYPE_SELL_CANCELED

Contrato de venda cancelado. Pode haver uma situação em que um negócio de venda executado anteriormente seja cancelado. Nesse caso, o tipo do negócio executado anteriormente (DEAL_TYPE_SELL) é alterado para DEAL_TYPE_SELL_CANCELEDe seu lucro/perda é zerado. O lucro/perda anteriormente obtido é cobrado/retirado usando uma operação de saldo separada

DEAL_DIVIDEND

Operações de dividendos

DEAL_DIVIDEND_FRANKED

Operações de dividendos não tributáveis

DEAL_TAX

Taxas tributárias


Adicionamos os tipos de negócios da enumeração ENUM_DEAL_TYPE ao arquivo ToMQL4.mqh:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/en/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
#endif 

Salvamos o arquivo e compilamos todos os arquivos da biblioteca novamente. Existem menos erros agora:


Movemos para o início da lista de erros novamente e clicamos no primeiro. Agora é ENUM_POSITION_TYPE, então vamos adicionar:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/en/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal type                                                   |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
#endif 

Depois de compilar, nós temos ainda menos erros. Movemos para o primeiro erro da lista, definimos o motivo e adicionamos a seguinte enumeração:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/en/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
//| Order status                                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATE
  {
   ORDER_STATE_STARTED,
   ORDER_STATE_PLACED,
   ORDER_STATE_CANCELED,
   ORDER_STATE_PARTIAL,
   ORDER_STATE_FILLED,
   ORDER_STATE_REJECTED,
   ORDER_STATE_EXPIRED,
   ORDER_STATE_REQUEST_ADD,
   ORDER_STATE_REQUEST_MODIFY,
   ORDER_STATE_REQUEST_CANCEL
  };
//+------------------------------------------------------------------+
#endif 

Durante a próxima compilação, nós recebemos o tipo errado da ordem ORDER_TYPE_BUY_STOP_LIMIT.
A MQL4 já apresenta a enumeração ENUM_ORDER_TYPE. Não podemos adicionar as novas constantes a ela. Portanto, nós adicionamos elas como substituições de macro.

Na MQL5, a constante ORDER_TYPE_BUY_STOP_LIMIT da enumeração ENUM_ORDER_TYPE é configurada como 6, enquanto no MQL4, esse tipo de ordem existe. Essa operação de saldo, como ORDER_TYPE_SELL_STOP_LIMIT no MQL5, é definida como 7, enquanto na MQL4, esse tipo de ordem é uma operação de crédito.

Portanto, defina os valores excedendo a constante da ordem de fechamento na MQL5 para eles ORDER_TYPE_CLOSE_BY: ORDER_TYPE_CLOSE_BY + 1 e ORDER_TYPE_CLOSE_BY + 2 adequadamente:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/en/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
//| Order status                                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATE
  {
   ORDER_STATE_STARTED,
   ORDER_STATE_PLACED,
   ORDER_STATE_CANCELED,
   ORDER_STATE_PARTIAL,
   ORDER_STATE_FILLED,
   ORDER_STATE_REJECTED,
   ORDER_STATE_EXPIRED,
   ORDER_STATE_REQUEST_ADD,
   ORDER_STATE_REQUEST_MODIFY,
   ORDER_STATE_REQUEST_CANCEL
  };
//+------------------------------------------------------------------+
//| Order types                                                      |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8) 
#define ORDER_TYPE_BUY_STOP_LIMIT   (9) 
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
//+------------------------------------------------------------------+
#endif 

Compilamos a biblioteca inteira. Depois de implementar as substituições de macro dos tipos da ordem StopLimit, o erro indica as funções que retornam o preço de posicionamento da ordem correta, ou seja, as enumerações ENUM_ORDER_TYPE não tendo os valores de 9 e 10, já que usamos o valor do tipo de ordem no operador switch case com o tipo da enumeração ENUM_ORDER_TYPE:

//+------------------------------------------------------------------+
//| Return the correct order placement price                         |
//| relative to StopLevel                                            |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch(order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+
//| Return the correct order placement price                         |
//| relative to StopLevel                                            |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch(order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+

A solução é simples — a order_type no operador switch case é convertida para o tipo inteiro:

//+------------------------------------------------------------------+
//| Return the correct order placement price                         |
//| relative to StopLevel                                            |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+
//| Return the correct order placement price                         |
//| relative to StopLevel                                            |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+

Vamos fazer a compilação. Agora há um erro no arquivo Order.mqh — a MQL4 não conhece os valores das constantes ORDER_FILLING_RETURN, ORDER_TIME_GTC, ORDER_REASON_SL, ORDER_REASON_TP e ORDER_REASON_EXPERT.

//+------------------------------------------------------------------+
//| Return execution type by residue                                 |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return order lifetime                                            |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Order reason or source                                           |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_POSITION   : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Vamos adicionar as substituições de macro ao final do arquivo ToMQL4.mqh (eu não darei a listagem completa aqui para economizar espaço):

//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8)
#define ORDER_TYPE_BUY_STOP_LIMIT   (9)
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
#define ORDER_FILLING_RETURN        (2)
#define ORDER_TIME_GTC              (0)
#define ORDER_REASON_EXPERT         (3)
#define ORDER_REASON_SL             (4)
#define ORDER_REASON_TP             (5)
//+------------------------------------------------------------------+
#endif 

Outra compilação nos leva à função ausente HistoryOrderGetTicket() da MQL5 no arquivo HistoryCollection.mqh do método CHistoryCollection::OrderSearch(). A análise de código sugere a aplicação de diretivas de compilação condicional aqui. Vamos complementar o método:

//+------------------------------------------------------------------+
//| Return the "lost" order's type and ticket                        |
//+------------------------------------------------------------------+
ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type)
  {
   ulong order_ticket=0;
#ifdef __MQL5__
   for(int i=start-1;i>=0;i--)
     {
      ulong ticket=::HistoryOrderGetTicket(i);
      if(ticket==0)
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(ticket,ORDER_TYPE);
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
#else 
   for(int i=start-1;i>=0;i--)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      ulong ticket=::OrderTicket();
      if(ticket==0 || type<ORDER_TYPE_BUY_LIMIT || type>ORDER_TYPE_SELL_STOP)
         continue;
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
#endif    
   return order_ticket;
  }
//+------------------------------------------------------------------+

Todas as coisas destinadas a MQL5 são enquadrados pela diretiva #ifdef __MQL5__. O código é adicionado para a MQL4 após a diretiva #else até #endif.

O próximo erro está localizado no construtor da classe CEvent. Suplementamos o código usando as mesmas diretivas de compilação condicional:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif;
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

Ao verificar a conta para o tipo "hedging", nós enfrentamos a ausência de um erro constante, portanto, simplesmente ele retorna true de uma vez como todas as contas estão protegendo as do MetaTrader 4.
Além disso, ao receber o número de casas decimais na moeda da conta, retorna 2, pois o MQL4 não pode obter este valor.

A próxima compilação nos leva ao método CEventsCollection:NewDealEventHedge() — recebendo um evento para uma conta hedging MetaTrader 5. Funciona com a ausência de negócios na MQL4. Desative temporariamente o método colocando todo o código do método na estrutura da compilação condicional:

Insira a diretiva no início do método

//+------------------------------------------------------------------+
//| Create a hedging account event                                   |
//+------------------------------------------------------------------+
void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market)
  {
#ifdef __MQL5__
   double ask=::SymbolInfoDouble(deal.Symbol(),SYMBOL_ASK);
   double bid=::SymbolInfoDouble(deal.Symbol(),SYMBOL_BID);
   //--- Market entry

and at the end of the method

#endif 
  }
//+------------------------------------------------------------------+

Em seguida, nós acabamos com o erro no método CEventsCollection::NewDealEventNetto() — criando um evento para uma conta netting. A solução é a mesma que no caso anterior — enquadra todo o código do método NewDealEventNetto() com a diretiva de compilação condicional.

Compilar e enfrentar o erro constante desconhecido DEAL_ENTRY_IN no método CEventsCollection::GetListAllDealsInByPosID(). Adicionamos a enumeração necessária para o arquivo ToMQL4.mqh (poderíamos usar a compilação condicional novamente para desabilitar o código, mas podemos precisar dessa enumeração posteriormente):

//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Position change method                                           |
//+------------------------------------------------------------------+
enum ENUM_DEAL_ENTRY
  {
   DEAL_ENTRY_IN,
   DEAL_ENTRY_OUT,
   DEAL_ENTRY_INOUT,
   DEAL_ENTRY_OUT_BY
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+

Em seguida, nós acabamos com o erro já conhecido de verificar a conta para o tipo "hedging", mas agora ele está no construtor da classe de coleta de eventos. Vamos consertar isso:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT)
  {
   this.m_list_events.Clear();
   this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_events.Type(COLLECTION_EVENTS_ID);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_chart_id=::ChartID();
   ::ZeroMemory(this.m_tick);
  }
//+------------------------------------------------------------------+

Em seguida, nós implementamos a mesma correção para o construtor da classe CEngine:

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT)
  {
   ::ResetLastError();
   if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
      Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
  }
//+------------------------------------------------------------------+

Tudo está definido. Agora toda a biblioteca é compilada sem erros. Mas este é apenas o primeiro estágio. Agora precisamos lançá-la. Como nós desativamos alguns métodos usando compilação condicional, nós precisaremos desenvolvê-los para trabalhar na MetaTrader 4.

Na MQL5, as operações de saldo são negócios. Elas podem ser encontradas na lista de ordens e negócios históricos. Na MQL4, as operações de saldo são ordens dos tipos ORDER_TYPE_BALANCE (6) e ORDER_TYPE_CREDIT (7). Portanto, eu fiz uma classe separada de um objeto de operação de saldo para a MQL4 que está armazenado na lista de ordens e posições do histórico.

Criamos a nova classe CHistoryBalance em \MQL4\Include\DoEasy\Objects\Orders do arquivo HistoryBalance.mqh. COrder deve ser uma classe básica:

//+------------------------------------------------------------------+
//|                                               HistoryBalance.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Historical balance operation                                     |
//+------------------------------------------------------------------+
class CHistoryBalance : public COrder
  {
public:
   //--- Constructor
                     CHistoryBalance(const ulong ticket) : COrder(ORDER_STATUS_BALANCE,ticket) {}
   //--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property);
  };
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TICKET      ||
      property==ORDER_PROP_TIME_OPEN   || 
      property==ORDER_PROP_STATUS      ||
      property==ORDER_PROP_TYPE        ||
      property==ORDER_PROP_REASON
     ) return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   return(property==ORDER_PROP_PROFIT ? true : false);
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_STRING property)
  {
   if(property==ORDER_PROP_SYMBOL || property==ORDER_PROP_EXT_ID)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

A classe não contém nada de novo para nós. Nós Já analisamos todas as classes do histórico de ordens na segunda parte da descrição da biblioteca.

Nós temos dois tipos de operações de saldo — saldo e crédito. Assim, seus tipos têm valores numéricos de 6 e 7. Nós usaremos uma classe de operação de saldo único para os dois tipos e esclareceremos um determinado tipo na propriedade da ordem "reason".

Adicionamos dois "motivos" de ordem ausente ao arquivo ToMQL4.mqh:

//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8)
#define ORDER_TYPE_BUY_STOP_LIMIT   (9)
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
#define ORDER_FILLING_RETURN        (2)
#define ORDER_TIME_GTC              (0)
#define ORDER_REASON_EXPERT         (3)
#define ORDER_REASON_SL             (4)
#define ORDER_REASON_TP             (5)
#define ORDER_REASON_BALANCE        (6)
#define ORDER_REASON_CREDIT         (7)
//+------------------------------------------------------------------+

Como nós temos uma nova classe derivada da classe de ordem abstrata, nós precisamos adicionar a funcionalidade ausente na COrder.

No método COrder::OrderPositionID(), substitua o retorno do número mágico para MQL4

//+------------------------------------------------------------------+
//| Return position ID                                               |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else

com o retorno do ticket (um tipo de PositionID para as posições da MQL4 deve ser implementado posteriormente):

//+------------------------------------------------------------------+
//| Return position ID                                               |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else

O método que retorna o estado da ordem na MQL4 sempre retorna ORDER_STATE_FILLED da Enumeração ENUM_ORDER_STATE, o que não é verdade para as ordens pendentes remotas. Implementamos a verificação do estado da ordem, e se esta for uma ordem pendente remota, retorne ORDER_STATE_CANCELED.

//+------------------------------------------------------------------+
//| Return the order status                                          |
//+------------------------------------------------------------------+
long COrder::OrderState(void) const
  {
#ifdef __MQL4__
   return(this.Status()==ORDER_STATUS_HISTORY_ORDER ? ORDER_STATE_FILLED : ORDER_STATE_CANCELED);
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_STATE); break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_STATE);                 break;
      case ORDER_STATUS_MARKET_POSITION   : 
      case ORDER_STATUS_DEAL              : 
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

Adicionamos os dois "motivos" recém-adicionados para o método, retornando o motivo da ordem para a MQL4:

//+------------------------------------------------------------------+
//| Order reason or source                                           |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.TypeOrder()==ORDER_TYPE_BALANCE   ?  ORDER_REASON_BALANCE :
      this.TypeOrder()==ORDER_TYPE_CREDIT    ?  ORDER_REASON_CREDIT  :
      this.OrderCloseByStopLoss()            ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit()          ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0             ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 

Em nosso caso, o método que retorna o volume não executado para a MQL4 sempre retorna um lote de ordens, o que é incorreto para as posições. Para as ordens pendentes remotas, nós vamos retornar um lote de uma ordem, enquanto para as posições, vamos retornar zero:

//+------------------------------------------------------------------+
//| Return unexecuted volume                                         |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return(this.Status()==ORDER_STATUS_HISTORY_PENDING ? ::OrderLots() : 0);
#else 

Adicionamos as descrições dos dois novos "motivos" no método que retornam uma descrição do motivo da ordem. Para as operações de saldo e crédito, verificamos o lucro. Se for superior a zero, os fundos são depositados, caso contrário, os fundos são retirados:

//+------------------------------------------------------------------+
//| Reason description                                               |
//+------------------------------------------------------------------+
string COrder::GetReasonDescription(const long reason) const
  {
#ifdef __MQL4__
   return
     (
      this.IsCloseByStopLoss()            ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss")                  :
      this.IsCloseByTakeProfit()          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit")              :
      this.Reason()==ORDER_REASON_EXPERT  ?  TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program")   :
      this.Comment()=="cancelled"         ?  TextByLanguage("Отменён","Cancelled")                                      :
      this.Reason()==ORDER_REASON_BALANCE ?  (
                                              this.Profit()>0 ? TextByLanguage("Пополнение баланса","Deposit of funds on the account balance") :
                                              TextByLanguage("Снятие средств с баланса","Withdrawal from the balance")
                                             )                                                                          :
      this.Reason()==ORDER_REASON_CREDIT  ?  (
                                              this.Profit()>0 ? TextByLanguage("Начисление кредитных средств","Received credit funds") :
                                              TextByLanguage("Изъятие кредитных средств","Withdrawal of credit")
                                             )                                                                          :
                                             
      TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
     );
#else 

Além disso, algumas pequenas edições foram feitas. Elas são muito insignificantes para serem descritos aqui. Eles estão principalmente relacionados a um texto exibido no diário da MQL5/MQL4. Todas as edições estão disponíveis nos arquivos da biblioteca anexados ao artigo.

Agora vamos melhorar a classe de coleção do histórico no arquivo HistoryCollection.mqh.
Primeiro, incluímos o novo arquivo de classe nele:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\HistoryOrder.mqh"
#include "..\Objects\Orders\HistoryPending.mqh"
#include "..\Objects\Orders\HistoryDeal.mqh"
#ifdef __MQL4__
#include "..\Objects\Orders\HistoryBalance.mqh"
#endif 
//+------------------------------------------------------------------+

Como nós precisamos da classe CHistoryBalance somente para a versão MQL4 da biblioteca, a inclusão do arquivo dessa classe é incluído nas diretivas de compilação condicional para a MQL4.

Agora nós temos uma nova classe de operações de saldo. Para desenvolver e colocá-la na coleção, nós precisamos adicionar a verificação dos tipos de ordem para o tipo de operação de saldo e crédito e adicionando-os à coleção no método Refresh() da classe CHistoryCollection para a MQL4:

//+------------------------------------------------------------------+
//| Update the list of orders and deals                              |
//+------------------------------------------------------------------+
void CHistoryCollection::Refresh(void)
  {
#ifdef __MQL4__
   int total=::OrdersHistoryTotal(),i=m_index_order;
   for(; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType();
      //--- Closed positions
      if(order_type<ORDER_TYPE_BUY_LIMIT)
        {
         CHistoryOrder *order=new CHistoryOrder(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
      //--- Balance/credit operations
      else if(order_type>ORDER_TYPE_SELL_STOP)
        {
         CHistoryBalance *order=new CHistoryBalance(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
      else
        {
         //--- Removed pending orders
         CHistoryPending *order=new CHistoryPending(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
     }
//---
   int delta_order=i-m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;
   this.m_is_trade_event=(this.m_delta_order!=0 ? true : false);
//--- __MQL5__
#else 
Vamos fazer algumas correções na classe do histórico da ordem:
//+------------------------------------------------------------------+
//| Return 'true' if an order supports the passed                    |
//| real property, otherwise, return 'false'                         |
//+------------------------------------------------------------------+
bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(
   #ifdef __MQL5__
      property==ORDER_PROP_PROFIT                  || 
      property==ORDER_PROP_PROFIT_FULL             || 
      property==ORDER_PROP_SWAP                    || 
      property==ORDER_PROP_COMMISSION              ||
      property==ORDER_PROP_PRICE_CLOSE             ||
      (
       property==ORDER_PROP_PRICE_STOP_LIMIT       && 
       (
        this.TypeOrder()<ORDER_TYPE_BUY_STOP_LIMIT || 
        this.TypeOrder()>ORDER_TYPE_SELL_STOP_LIMIT  
       )
      )
   #else
      property==ORDER_PROP_PRICE_STOP_LIMIT        && 
      this.Status()==ORDER_STATUS_HISTORY_ORDER
   #endif 
     ) return false;

   return true;
  }
//+------------------------------------------------------------------+

Anteriormente, o preço da ordem StopLimit na MQL5 não era passado para o diário. Portanto, eu implementei uma verificação: se a propriedade verificada for o preço de uma ordem StopLimit, e se o tipo da ordem não for uma StopLimit, a propriedade não é usada. Caso contrário, esta é uma ordem StopLimit e a propriedade é necessária.
Em MQL4,
o preço da ordem StopLimit não é usada para as posições.

Isso conclui a melhoria do primeiro estágio de compatibilidade com a MQL4.

Testando o sistema de negociação

Para fins de teste, use o EA TestDoEasyPart03_1.mq5 de \MQL5\Experts\TestDoEasy\Part03 e salve-o com o nome de TestDoEasyPart09.mq4 na pasta para os EAs em MQL4 \MQL4\Experts\TestDoEasy\Part09.

O EA é compilado sem alterações, mas se dermos uma olhada no código, verifica-se que ele usa a lista de negócios, ausente na MQL4:

//--- enums
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // Market orders
   TYPE_ORDER_PENDING,  // Pending orders
   TYPE_ORDER_DEAL      // Deals
  };
//--- input parameters

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- update history
   history.Refresh();
//--- get the collection list within the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the order from the list
      COrder* order=list.At(i);
      if(order==NULL) continue;
      //--- if this is a deal
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
      //--- if this is a historical market order
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
      //--- if this is a removed pending order
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Basta substituir os negócios pelas operações de saldo. Nesse caso, nós usaremos a compilação condicional diretamente no EA, o que não é correto para o produto final, onde todas as ações para delimitar por versões de idioma devem ser ocultadas dos usuários. Mas, neste caso, nós simplesmente testamos os resultados de melhoria da biblioteca, portanto, isso não é grande coisa.

Vamos adicionar pequenas alterações ao código do EA substituindo os negócios em MQL5 com as operações de saldo da MQL4:

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_1.mq4 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- enums
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // Market orders
   TYPE_ORDER_PENDING,  // Pending orders
#ifdef __MQL5__
   TYPE_ORDER_DEAL      // Deals
#else 
   TYPE_ORDER_BALANCE   // Balance/Credit
#endif 
  };
//--- input parameters
input ENUM_TYPE_ORDERS  InpOrderType   =  TYPE_ORDER_MARKET;   // Show type:
input datetime          InpTimeBegin   =  0;                   // Start date of required range
input datetime          InpTimeEnd     =  END_TIME;            // End date of required range
//--- global variables
CHistoryCollection history;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- update history
   history.Refresh();
//--- get the collection list within the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the order from the list
      COrder* order=list.At(i);
      if(order==NULL) continue;
   //--- if this is a deal
   #ifdef __MQL5__
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
   #else 
   //--- if this is a balance/credit operation
      if(order.Status()==ORDER_STATUS_BALANCE && InpOrderType==TYPE_ORDER_BALANCE)
         order.Print();
   #endif 
   //--- if this is a historical market order
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
   //--- if this is a removed pending order
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Compile e execute o EA no terminal (o EA de teste do terceiro artigo funciona apenas no manipulador OnInit(), portanto, ele exibirá a lista de coleta do histórico necessária uma vez após o lançamento ou após a alteração da lista nas configurações).

Antes de iniciar o EA, selecione a opção "Todo o Histórico" no menu de contexto da guia "Histórico da Conta" do terminal, já que na MetaTrader 4, a quantidade de histórico disponível para os aplicativos depende do tamanho do histórico selecionado na guia.

Saldo/Crédito é selecionado nas configurações, e o primeiro saldo, proveniente do depósito, é exibido no diário:


Agora nós precisamos verificar se a busca e a exibição das posições fechadas estão corretas. Como eu abri recentemente uma conta na MetaTrader 4, não havia negociação nela. Eu abri uma Venda, coloquei o StopLoss e o TakeProfit e saí para fazer um café. Quando eu voltei, a posição foi fechada pelo stop loss, sobre o qual o mercado começou a se mover na direção da posição Vendida. Sim, é sempre assim! :)

Mas agora há uma posição fechada para o teste.
As "ordens de mercado" são selecionadas nas configurações:


Agora vamos verificar a lista de ordens pendentes removidas. Eu defini algumas ordens e os removi depois.
As "ordens pendentes" são selecionadas nas configurações:


A lista de ordens pendentes removidas também é exibida.

Qual é o próximo?

No próximo artigo, nós implementaremos a capacidade de trabalhar com posições de mercado e ordens pendentes ativas em MQL4.

Todos os arquivos da versão atual da biblioteca estão anexados abaixo, juntamente com os arquivos do EA de teste para você testar e fazer o download.
Deixe suas perguntas, comentários e sugestões nos comentários.

Voltar ao conteúdo

Artigos anteriores da série:

Parte 1. Conceito, gerenciamento de dados.
Parte 2. Coleção do histórico de ordens e negócios.
Parte 3 Coleção de ordens e posições de mercado, busca e ordenação.
Parte 4 Eventos de negociação. Conceito.
Parte 5. Classes e coleção de eventos de negociação. Envio de eventos para o programa.
Parte 6. Eventos da conta netting.
Parte 7. Eventos de ativação da ordem StopLimit, preparação da funcionalidade para os eventos de modificação de ordens e posições.
Parte 8. Eventos de modificação de ordens e posições.