
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
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- 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
Gradualmente, muitas funcionalidades serão adicionadas à biblioteca, de modo que será muito fácil trabalhar com algoritmos - do jeito que você quiser. E é exatamente para isso que ela foi projetada.
Agora, embora não exista tal funcionalidade, você pode ver como o Expert Advisor de teste funciona com a classe de negociação CTrade incluída na biblioteca padrão em MQL5 e escrever construções semelhantes para chamar as funções de negociação necessárias. Lá (no EA de teste) há uma chamada das funções de negociação do testador para MQL4.
Obrigado, vou estudar o assunto.
Boa tarde. Gostei de seu Expert Advisor de teste. Quero tentar usá-lo como um núcleo, que receberá sinais e filtros de vários indicadores, suas combinações ou controlados manualmente, por meio do pressionamento de botões.
Você já viu o primeiro desses Expert Advisors e me ajudou a dar vida a ele em um tópico vizinho deste fórum.
Você pode me mostrar como pressionar os botões de forma programática nesse seu EA de teste?
Existe uma função adequada - pode compartilhá-la?
Ou sugerir a melhor maneira de fazer isso, por favor.
Boa tarde!
Sergey, eu o apoiarei porque vejo que você está em uma situação semelhante.
Sim, os artigos são ótimos, mas contêm muito poucas informações sobre como usar o código escrito. As bibliotecas, de modo geral, são valiosas para ocultar a implementação e fornecer uma interface clara para tarefas práticas. A ajuda da função https://docs.mql4.com/strings/stringsubstr não contém uma única palavra sobre seus aspectos internos. Uma descrição dos parâmetros de entrada, o resultado de seu processamento e exemplo(s). Isso é o que eu gostaria de ver.
Sim, Artem, você é, sem dúvida, um programador talentoso, mas os engenheiros de aplicativos precisam desenvolver outro algoritmo o mais rápido possível e não passar horas sobre centenas de linhas de código de outras pessoas em busca de esclarecimento. A série de artigos até agora é mais teórica.
Esta não é minha primeira postagem sobre esse tópico). De forma alguma quero menosprezar os méritos da série. Pelo contrário, espero, Artem, que você leve em consideração as solicitações dos membros do fórum e que as bibliotecas escritas sejam usadas nos EAs com a mesma avidez com que os bons filmes são citados.
Boa tarde!
Sergey, eu o apoio, pois vejo que você está em uma situação semelhante.
Sim, os artigos são ótimos, mas contêm muito poucas informações sobre como usar o código escrito. As bibliotecas, de modo geral, são valiosas para ocultar a implementação e fornecer uma interface clara para tarefas práticas. A ajuda da função https://docs.mql4.com/strings/stringsubstr não contém uma única palavra sobre seus aspectos internos. Uma descrição dos parâmetros de entrada, o resultado de seu processamento e exemplo(s). Isso é o que eu gostaria de ver.
Sim, Artem, você é, sem dúvida, um programador talentoso, mas os engenheiros de aplicativos precisam resolver tarefas práticas em vez de passar horas sobre centenas de linhas de código de outras pessoas em busca de esclarecimento. A série de artigos até agora é mais teórica.
Esta não é a minha primeira postagem sobre esse tópico). De forma alguma quero menosprezar os méritos da série. Pelo contrário, espero, Artem, que você leve em consideração as solicitações dos membros do fórum e que as bibliotecas escritas sejam usadas nos EAs com a mesma avidez com que os bons filmes são citados.
O objetivo é orientar o leitor desde o início da criação da biblioteca até sua conclusão.
Como você pode ver, os artigos são mais educativos por natureza, embora tenham uma finalidade prática útil, e mais de uma. O design dos códigos é fácil de entender, sem o uso de reviravoltas e recursos não documentados por causa de reviravoltas e "frescor". Mas há um ponto positivo inegável: quantas versões beta do terminal foram lançadas e quantas pessoas já disseram que seus códigos pararam de funcionar, e a biblioteca vive de compilação em compilação sem correções forçadas porque algo parou de funcionar de repente....
Atualmente, a biblioteca tem um ponto de entrada - a classe CEngine (haverá um segundo ponto de entrada, mas muito mais tarde), e o objeto dessa classe no EA dá acesso total a todos os recursos.
Além disso, não é difícil criar um objeto desse tipo, por exemplo: CEngine lib; e no código digite lib e coloque um ponto (assim: lib.) - após o ponto, o editor mostrará uma janela com uma lista de todos os métodos disponíveis para uso da biblioteca. A maioria deles tem nomes significativos - com um pouco de prática, você poderá usá-los. Todos os métodos são descritos em artigos. Em cada artigo, há um exemplo de um programa de teste que mostra, no entanto, uma pequena parte das possibilidades.
Concordo: procurar os métodos mostrados e sua aplicação em vários artigos sem material de referência é uma tarefa difícil.... Mas o ciclo de artigos é um ciclo para que o leitor passe por ele junto comigo, e então algo será armazenado em sua cabeça :) E o objetivo, eu o lembro, é educacional.
Haverá material de referência. Mas no final, quando a biblioteca for criada. E exemplos, é claro, também.
Enquanto isso, você pode fazer perguntas práticas. Mostre uma parte do seu código e eu lhe darei uma dica. Estou aqui e não vou a lugar algum - não faz parte das minhas regras abandonar o que comecei.
O objetivo é levar o leitor desde o início da biblioteca até a sua conclusão.
Como você pode ver, os artigos são mais educativos por natureza, embora tenham uma finalidade prática útil, e mais de uma. O design dos códigos é fácil de entender, sem o uso de reviravoltas e recursos não documentados por causa de reviravoltas e "frescor". Mas há uma vantagem inegável: quantas versões beta do terminal foram lançadas e quantas pessoas já disseram que seus códigos pararam de funcionar, e a biblioteca vive de compilação em compilação sem correções forçadas porque algo parou de funcionar de repente....
Atualmente, a biblioteca tem um ponto de entrada - a classe CEngine (haverá um segundo ponto de entrada, mas muito mais tarde), e o objeto dessa classe no EA dá acesso total a todos os recursos.
Além disso, não é difícil criar um objeto desse tipo, por exemplo: CEngine lib; e no código digite lib e coloque um ponto (assim: lib.) - após o ponto, o editor mostrará uma janela com uma lista de todos os métodos disponíveis para uso da biblioteca. A maioria deles tem nomes significativos - com um pouco de prática, você poderá usá-los. Todos os métodos são descritos em artigos. Cada artigo contém um exemplo de um programa de teste, que mostra apenas uma pequena parte das possibilidades.
Concordo: procurar os métodos mostrados e sua aplicação em vários artigos sem material de referência é uma tarefa difícil.... Mas o ciclo de artigos é um ciclo para que o leitor passe por ele junto comigo, e então algo será armazenado em sua cabeça :) E o objetivo, eu o lembro, é educacional.
Haverá material de referência. Mas no final, quando a biblioteca for criada. E exemplos, é claro, também.
Enquanto isso, você pode fazer perguntas práticas. Mostre uma parte do seu código e eu lhe darei uma dica. Estou aqui e não vou a lugar algum - não faz parte das minhas regras abandonar o que comecei.
Entendo que suas intenções são as melhores e que você provavelmente tem muito tempo livre.)
Acabei de ver seus artigos da série "MakingSimple" [ Biblioteca para criação fácil e rápida de programas para o MetaTrader] e pensei que, após 10-15 minutos de leitura, eu seria capaz de usar um código útil. Eu esperava ver um artigo clássico comohttps://www.mql5.com/pt/articles/272, em que a lógica está oculta e a interface está aberta, em que as perguntas são respondidas: "por que isso é necessário", "como trabalhar com isso" e exemplos. Acontece que o objetivo é o treinamento, não o RAD (desenvolvimento rápido).
Bem, estamos ansiosos para ver você escrever um desses! ))
Sei que suas intenções são as melhores e que você provavelmente tem muito tempo livre.)
Acabei de ver seus artigos da série "MakingSimple"[ Biblioteca para a criação fácil e rápida de programas para o MetaTrader] e pensei que, após 10-15 minutos de leitura, eu seria capaz de usar um código útil. Eu esperava ver um artigo clássico comohttps://www.mql5.com/pt/articles/272, em que a lógica está oculta e a interface está aberta, em que as perguntas são respondidas: "por que isso é necessário", "como trabalhar com isso" e exemplos. Acontece que o objetivo era o treinamento, não o RAD (desenvolvimento rápido).
Bem, estamos ansiosos para ver você escrever um desses! ))
Oobjetivo é aprendizado + desenvolvimento rápido. Sobre o desenvolvimento rápido, só vale a pena fazer perguntas de aplicação prática se você tiver preguiça de ler e ainda não houver material de referência com exemplos.
O título se traduz como "Make it easy". (Inglês..., permite traduzir da maneira que você quiser, se não houver contexto).