Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte IX): Compatibilidade com a MQL4 - Preparação dos dados
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:
- para criar as funções de caso de usuário que permitem acesso rápido de programas até quaisquer dados de ordens e posições em contas de hedging e netting,
- para monitorar os eventos que ocorrem em ordens e posições — colocação, remoção e ativação de ordens pendentes, bem como a abertura, fechamento e modificação de posições e ordens.
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 |
|
DEAL_ENTRY |
Direção do negócio - entrada, saída ou reversão do mercado |
|
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 |
|
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__ #elseVamos 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.
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.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/6651
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso