Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte V): Classes e coleções de eventos de negociação, envio de eventos para o programa
Conteúdo
- Reorganizando a estrutura da biblioteca
- Classes de eventos
- Coleção de eventos de negociação
- Testando os processos de definição, manipulação e recebimento de eventos
- Qual é o próximo?
Reorganizando a estrutura da biblioteca
Nos artigos anteriores, nós começamos a criar uma grande biblioteca multi-plataforma, simplificando o desenvolvimento de programas para as plataformas MetaTrader 5 e MetaTrader 4. Na quarta parte, nós testamos o monitoramento de eventos de negociação na conta. Neste artigo, nós vamos desenvolver classes de eventos de negociação e colocá-los nas coleções de eventos. A partir daí, eles serão enviados ao objeto base da biblioteca Engine e ao gráfico do programa de controle.
Mas primeiro, vamos preparar o terreno para o desenvolvimento da estrutura da biblioteca.
Como nós teremos muitas coleções diferentes, e cada coleção terá seus próprios objetos inerentes apenas a essa coleção, parece razoável armazenar objetos para cada coleção em subpastas separadas.
Para fazer isso, vamos criar as pastas Orders e Events na subpasta Objetcs do diretório raíz da
biblioteca
DoEasy.
Mova todas as classes criadas anteriormente da pasta Objects para a de Orders, enquanto a pasta Events é para armazenar
as classes de objetos de evento que nós vamos desenvolver neste artigo.
Além disso, realoque o arquivo Select.mqh de Collections para Services, já que nós vamos incluir ainda outra classe de serviço nele. A
classe apresenta os métodos para o acesso rápido a quaisquer propriedades de qualquer objeto das coleções existentes e futuras, o que
significa que ela deve estar localizada na pasta service das classes.
Depois de realocar o arquivo da classe CSelect e mover as classes de objeto order para o novo diretório, os endereços relativos dos arquivos necessários para sua compilação também são alterados. Portanto, vamos percorrer as listagens das classes movidas e substituir os endereços dos arquivos incluídos nelas:
No arquivo Order.mqh, substitua o caminho para incluir o arquivo de funções de serviço
#include "..\Services\DELib.mqh"
para
#include "..\..\Services\DELib.mqh"
No arquivo HistoryCollection.mqh, substitua os caminhos
#include "Select.mqh" #include "..\Objects\HistoryOrder.mqh" #include "..\Objects\HistoryPending.mqh" #include "..\Objects\HistoryDeal.mqh"
para
#include "..\Services\Select.mqh" #include "..\Objects\Orders\HistoryOrder.mqh" #include "..\Objects\Orders\HistoryPending.mqh" #include "..\Objects\Orders\HistoryDeal.mqh"
No arquivo MarketCollection.mqh, substitua os caminhos
#include "Select.mqh" #include "..\Objects\MarketOrder.mqh" #include "..\Objects\MarketPending.mqh" #include "..\Objects\MarketPosition.mqh"
para
#include "..\Services\Select.mqh" #include "..\Objects\Orders\MarketOrder.mqh" #include "..\Objects\Orders\MarketPending.mqh" #include "..\Objects\Orders\MarketPosition.mqh"
Agora tudo deve ser compilado sem erros.
Como o número de coleções futuras é enorme, seria bom distinguir a propriedade da lista de coleção com base na CArrayObj para identificar a lista. Cada coleção apresenta um método que retorna o ponteiro para a lista completa de coleções. Se em algum lugar houver um método que receba uma determinada lista de uma determinada coleção, então, dentro desse método, nós precisamos identificar com precisão a lista passada ao método se ela pertence a uma coleção ou outra para evitar passar um sinalizador adicional indicando o tipo da lista passada para o método.
Felizmente, a biblioteca padrão já fornece a ferramenta necessária para isso na forma do método virtual Type()
retornando o ID do objeto.
Por exemplo, para a CObject,
o ID retornado é 0, enquanto que para a
CArrayObj, o ID é 0x7778. Como o
método é virtual, isso permite que os descendentes das classes tenham seus próprios métodos retornando IDs específicos.
Todas as nossas listas de coleções são baseadas na classe CArrayObj. Nós criaremos nossa própria classe CListObj que é um descendente da classe CArrayObj e o ID da lista é retornado em seu método virtual Type(). O ID em si é definido como uma constante no construtor da classe. Assim, nós continuaremos obtendo acesso às nossas coleções quanto ao objeto CArrayObj, mas agora cada lista terá seu próprio ID específico.
Primeiro, vamos definir as listas de coleção de IDs no arquivo Defines.mqh e adicionar a macro descrevendo a função com o número da linha de erro para exibir mensagens de depuração contendo uma string da qual esta mensagem é enviada para localizar o problema no código durante a depuração:
//+------------------------------------------------------------------+ //| Substituições de macro | //+------------------------------------------------------------------+ //--- Descreve a função com o número da linha de erro #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Descrição da função" #define COUNTRY_LANG ("Russian") // Idioma do país #define END_TIME (D'31.12.3000 23:59:59') // Hora de término para solicitar os dados do histórico da conta #define TIMER_FREQUENCY (16) // Frequência mínima do timer da biblioteca em milissegundos #define COLLECTION_PAUSE (250) // Pausa do timer da coleção de ordens e negócios em milissegundos #define COLLECTION_COUNTER_STEP (16) // Incremento do contador do timer da coleção de ordens e negócios #define COLLECTION_COUNTER_ID (1) // ID do contador do timer da coleção de ordens e negócios #define COLLECTION_HISTORY_ID (0x7778+1) // ID da lista de coleção do histórico #define COLLECTION_MARKET_ID (0x7778+2) // ID da lista de coleção à Mercado #define COLLECTION_EVENTS_ID (0x7778+3) // ID da lista de coleção de eventos //+------------------------------------------------------------------+
Agora crie a classe CListObj no arquivo ListObj.mqh dentro da pasta Collections. A classe base para isso é a CArrayObj:
//+------------------------------------------------------------------+ //| ListObj.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| Classe das listas de coleções | //+------------------------------------------------------------------+ class CListObj : public CArrayObj { private: int m_type; // Tipo da lista public: void Type(const int type) { this.m_type=type; } virtual int Type(void) const { return(this.m_type); } CListObj() { this.m_type=0x7778; } }; //+------------------------------------------------------------------+
Tudo o que temos a fazer aqui é declarar um membro da classe que contém o tipo
lista, adicionar o método para definir o tipo lista e o método
virtual para retorna-lo.
No construtor da classe, defina o tipo
lista padrão igual ao da lista CArrajObj. Ele pode ser redefinido a partir do programa que realizou a chamada usando o método Type().
Agora nós precisamos herdar todas as listas de coleções da classe para poder atribuir um ID de busca separada a cada lista. Esse ID nos permitirá monitorar a propriedade da lista em qualquer método para o qual a lista será passada.
Abra o arquivo HistoryCollection.mqh, adicione a inclusão da classe
CListObj e herde a classe CHistoryCollection de CListObj.
//+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\HistoryOrder.mqh" #include "..\Objects\Orders\HistoryPending.mqh" #include "..\Objects\Orders\HistoryDeal.mqh" //+------------------------------------------------------------------+ //| Coleção do histórico de ordens e negócios | //+------------------------------------------------------------------+ class CHistoryCollection : public CListObj {
No construtor da classe, defina o tipo lista de coleção do histórico, que nós especificamos como COLLECTION_HISTORY_ID no arquivo Defines.mqh:
//+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CHistoryCollection::CHistoryCollection(void) : m_index_deal(0),m_delta_deal(0),m_index_order(0),m_delta_order(0),m_is_trade_event(false) { this.m_list_all_orders.Sort(#ifdef __MQL5__ SORT_BY_ORDER_TIME_OPEN #else SORT_BY_ORDER_TIME_CLOSE #endif ); this.m_list_all_orders.Clear(); this.m_list_all_orders.Type(COLLECTION_HISTORY_ID); } //+------------------------------------------------------------------+
Faça o mesmo para a classe CMarketCollection no arquivo MarketCollection.mqh:
//+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\MarketOrder.mqh" #include "..\Objects\Orders\MarketPending.mqh" #include "..\Objects\Orders\MarketPosition.mqh" //+------------------------------------------------------------------+ //| Coleção de ordens e posições | //+------------------------------------------------------------------+ class CMarketCollection : public CListObj {
No construtor de classe, defina o tipo coleção à mercado, que nós especificamos no arquivo Defines.mqh como COLLECTION_MARKET_ID:
//+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CMarketCollection::CMarketCollection(void) : m_is_trade_event(false),m_is_change_volume(false),m_change_volume_value(0) { this.m_list_all_orders.Sort(SORT_BY_ORDER_TIME_OPEN); this.m_list_all_orders.Clear(); ::ZeroMemory(this.m_struct_prev_market); this.m_struct_prev_market.hash_sum_acc=WRONG_VALUE; this.m_list_all_orders.Type(COLLECTION_MARKET_ID); } //+------------------------------------------------------------------+
Agora, cada uma das listas de coleção tem seu ID simplificando a identificação das listas pelos seus tipos.
Como nós devemos adicionar novas coleções para trabalhar com os novos tipos de dados (incluindo a coleção de eventos da conta no presente artigo), nós usaremos as novas enumerações. Para evitar conflitos de nomes, nós precisamos substituir os nomes de algumas substituições de macro criadas anteriormente:
//+------------------------------------------------------------------+ //| Possíveis critérios de classificação de ordens e negócios | //+------------------------------------------------------------------+ #define FIRST_ORD_DBL_PROP (ORDER_PROP_INTEGER_TOTAL) #define FIRST_ORD_STR_PROP (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL) enum ENUM_SORT_ORDERS_MODE { //--- Classifica por propriedades do tipo inteiro SORT_BY_ORDER_TICKET = 0, // Classifica pelo ticket da ordem SORT_BY_ORDER_MAGIC = 1, // Classifica pelo número mágico da ordem SORT_BY_ORDER_TIME_OPEN = 2, // Classifica pelo horário de abertura da ordem SORT_BY_ORDER_TIME_CLOSE = 3, // Classifica pelo horário de fechamento da ordem SORT_BY_ORDER_TIME_OPEN_MSC = 4, // Classifica pelo horário de abertura da ordem em milissegundos SORT_BY_ORDER_TIME_CLOSE_MSC = 5, // Classifica pelo horário de fechamento da ordem em milissegundos SORT_BY_ORDER_TIME_EXP = 6, // Classifica pela data de expiração da ordem SORT_BY_ORDER_STATUS = 7, // Classifica pelo estado da ordem (ordem à mercado/ordem pendente/negócio/saldo/ operação de crédito) SORT_BY_ORDER_TYPE = 8, // Classifica pelo tipo da ordem SORT_BY_ORDER_REASON = 10, // Classifica por ordem/posição/fonte SORT_BY_ORDER_STATE = 11, // Classifica pelo estado da ordem SORT_BY_ORDER_POSITION_ID = 12, // Classifica pelo ID da posição SORT_BY_ORDER_POSITION_BY_ID = 13, // Classifica pelo ID da posição oposta SORT_BY_ORDER_DEAL_ORDER = 14, // Classifica pela ordem que o negócio se baseou SORT_BY_ORDER_DEAL_ENTRY = 15, // Classifica pela direção do negócio – IN, OUT ou IN/OUT SORT_BY_ORDER_TIME_UPDATE = 16, // Classifica pelo horário de alteração da posição em segundos SORT_BY_ORDER_TIME_UPDATE_MSC = 17, // Classifica pelo horário de alteração da posição em milissegundos SORT_BY_ORDER_TICKET_FROM = 18, // Classifica pelo ticket da ordem pai SORT_BY_ORDER_TICKET_TO = 19, // Classifica pelo ticket da ordem derivada SORT_BY_ORDER_PROFIT_PT = 20, // Classifica pelo lucro da ordem em pontos SORT_BY_ORDER_CLOSE_BY_SL = 21, // Classifica pelo fechamento da ordem pela flag StopLoss SORT_BY_ORDER_CLOSE_BY_TP = 22, // Classifica pelo fechamento da ordem pela flag TakeProfit //--- Classifica por propriedades do tipo real SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP, // Classifica pelo preço de abertura SORT_BY_ORDER_PRICE_CLOSE = FIRST_ORD_DBL_PROP+1, // Classifica pelo preço do fechamento SORT_BY_ORDER_SL = FIRST_ORD_DBL_PROP+2, // Classifica pelo preço de StopLoss SORT_BY_ORDER_TP = FIRST_ORD_DBL_PROP+3, // Classifica pelo preço de TakeProfit SORT_BY_ORDER_PROFIT = FIRST_ORD_DBL_PROP+4, // Classifica pelo lucro SORT_BY_ORDER_COMMISSION = FIRST_ORD_DBL_PROP+5, // Classifica pela comissão SORT_BY_ORDER_SWAP = FIRST_ORD_DBL_PROP+6, // Classifica pelo swap SORT_BY_ORDER_VOLUME = FIRST_ORD_DBL_PROP+7, // Classifica pelo volume SORT_BY_ORDER_VOLUME_CURRENT = FIRST_ORD_DBL_PROP+8, // Classifica pelo volume não executado SORT_BY_ORDER_PROFIT_FULL = FIRST_ORD_DBL_PROP+9, // Classifica pelo critério de lucro+comissão+swap SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_ORD_DBL_PROP+10, // Classifica pela ordem limitada quando a ordem StopLimit é ativada //--- Classifica pelas propriedades do tipo string SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP, // Classifica pelo símbolo SORT_BY_ORDER_COMMENT = FIRST_ORD_STR_PROP+1, // Classifica pelo comentário SORT_BY_ORDER_EXT_ID = FIRST_ORD_STR_PROP+2 // Classifica pelo ID da ordem em um sistema de negociação externo }; //+------------------------------------------------------------------+
Já que atualmente nós estamos editando os arquivos Defines.mqh, vamos adicionar todas as enumerações necessárias para as classes de eventos e coleção de eventos da conta:
//+------------------------------------------------------------------+ //| Estado do evento | //+------------------------------------------------------------------+ enum ENUM_EVENT_STATUS { EVENT_STATUS_MARKET_POSITION, // Evento de posição (abertura, abertura parcial, encerramento parcial, incremento do volume, reversão) EVENT_STATUS_MARKET_PENDING, // Evento de ordem pendente (colocação) EVENT_STATUS_HISTORY_PENDING, // Evento do histórico de ordem pendente (remoção) EVENT_STATUS_HISTORY_POSITION, // Evento do histórico de posição (encerramento) EVENT_STATUS_BALANCE, // Evento de operação de saldo (saldo acumulado, retirada de fundos e eventos da enumeração ENUM_DEAL_TYPE) }; //+------------------------------------------------------------------+ //| Motivo do evento | //+------------------------------------------------------------------+ enum ENUM_EVENT_REASON { EVENT_REASON_ACTIVATED_PENDING = 0, // Ativação da ordem pendente EVENT_REASON_ACTIVATED_PENDING_PARTIALLY = 1, // Ativação parcial da ordem pendente EVENT_REASON_CANCEL = 2, // Cancelamento EVENT_REASON_EXPIRED = 3, // Expiração da ordem EVENT_REASON_DONE = 4, // Solicitação executada por completo EVENT_REASON_DONE_PARTIALLY = 5, // Solicitação executada parcialmente EVENT_REASON_DONE_SL = 6, // Encerramento por StopLoss EVENT_REASON_DONE_SL_PARTIALLY = 7, // Encerramento parcial por StopLoss EVENT_REASON_DONE_TP = 8, // Encerramento por TakeProfit EVENT_REASON_DONE_TP_PARTIALLY = 9, // Encerramento parcial por TakeProfit EVENT_REASON_DONE_BY_POS = 10, // Encerramento por uma posição oposta EVENT_REASON_DONE_PARTIALLY_BY_POS = 11, // Encerramento parcial por uma posição oposta EVENT_REASON_DONE_BY_POS_PARTIALLY = 12, // Encerramento uma posição oposta pelo volume parcial EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY = 13, // Encerramento parcial de uma posição oposta pelo volume parcial //--- Constantes relacionadas ao tipo de negócio DEAL_TYPE_BALANCE da enumeração ENUM_DEAL_TYPE EVENT_REASON_BALANCE_REFILL = 14, // Recarga de saldo EVENT_REASON_BALANCE_WITHDRAWAL = 15, // Saque de fundos da conta //--- A lista de constantes é relevante para a TRADE_EVENT_ACCOUNT_CREDIT da enumeração ENUM_TRADE_EVENT e alterada para +13 em relação a ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3) EVENT_REASON_ACCOUNT_CREDIT = 16, // Crédito acumulado EVENT_REASON_ACCOUNT_CHARGE = 17, // Cobranças adicionais EVENT_REASON_ACCOUNT_CORRECTION = 18, // Correção de entrada EVENT_REASON_ACCOUNT_BONUS = 19, // Bônus acumulado EVENT_REASON_ACCOUNT_COMISSION = 20, // Comissões adicionais EVENT_REASON_ACCOUNT_COMISSION_DAILY = 21, // Comissão cobrada no fim da negociação do dia EVENT_REASON_ACCOUNT_COMISSION_MONTHLY = 22, // Comissão cobrada no fim da negociação do mês EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY = 23, // Comissão do agente cobrada no fim da negociação do dia EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY = 24, // Comissão do agente cobrada no fim da negociação do mês EVENT_REASON_ACCOUNT_INTEREST = 25, // juros acumulado sobre o saldo livre EVENT_REASON_BUY_CANCELLED = 26, // Compra cancelada EVENT_REASON_SELL_CANCELLED = 27, // Venda cancelada EVENT_REASON_DIVIDENT = 28, // Dividendos acumulados EVENT_REASON_DIVIDENT_FRANKED = 29, // Dividendos não tributáveis EVENT_REASON_TAX = 30 // Imposto }; #define REASON_EVENT_SHIFT (EVENT_REASON_ACCOUNT_CREDIT-3) //+------------------------------------------------------------------+ //| Propriedades de evento do tipo inteiro | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_INTEGER { EVENT_PROP_TYPE_EVENT = 0, // Tipo do evento da conta de negociação (da enumeração ENUM_TRADE_EVENT) EVENT_PROP_TIME_EVENT, // Hora do evento em milissegundos EVENT_PROP_STATUS_EVENT, // Estado do evento (da enumeração ENUM_EVENT_STATUS) EVENT_PROP_REASON_EVENT, // Motivo do evento (da enumeração ENUM_EVENT_REASON) EVENT_PROP_TYPE_DEAL_EVENT, // Tipo do evento de negócio EVENT_PROP_TICKET_DEAL_EVENT, // Ticket do evento de negócio EVENT_PROP_TYPE_ORDER_EVENT, // Tipo de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) EVENT_PROP_TICKET_ORDER_EVENT, // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) EVENT_PROP_TIME_ORDER_POSITION, // Hora de uma ordem, com base em qual evento de posição está aberto (a ordem da primeira posição) EVENT_PROP_TYPE_ORDER_POSITION, // Tipo de uma ordem, com base em qual evento de posição está aberto (a ordem da primeira posição) EVENT_PROP_TICKET_ORDER_POSITION, // Ticket de uma ordem, com base em qual evento de posição está aberto (a ordem da primeira posição) EVENT_PROP_POSITION_ID, // ID da posição EVENT_PROP_POSITION_BY_ID, // ID da posição oposta EVENT_PROP_MAGIC_ORDER, // Ordem/negócio/número mágico da posição }; #define EVENT_PROP_INTEGER_TOTAL (14) // Número total de propriedades do evento do tipo inteiro //+------------------------------------------------------------------+ //| Propriedades de evento do tipo real | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_DOUBLE { EVENT_PROP_PRICE_EVENT = (EVENT_PROP_INTEGER_TOTAL), // Preço de um evento que ocorreu EVENT_PROP_PRICE_OPEN, // Ordem/negócio/preço de abertura da posição EVENT_PROP_PRICE_CLOSE, // Ordem/negócio/preço de encerramento da posição EVENT_PROP_PRICE_SL, // Ordem StopLoss/negócio/preço da posição EVENT_PROP_PRICE_TP, // Ordem TakeProfit/negócio/preço da posição EVENT_PROP_VOLUME_INITIAL, // Volume solicitado EVENT_PROP_VOLUME_EXECUTED, // Volume executado EVENT_PROP_VOLUME_CURRENT, // Volume restante EVENT_PROP_PROFIT // Lucro }; #define EVENT_PROP_DOUBLE_TOTAL (9) // Número total de propriedades do evento do tipo real //+------------------------------------------------------------------+ //| Propriedades de evento do tipo string | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_STRING { EVENT_PROP_SYMBOL = (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL), // Símbolo da ordem }; #define EVENT_PROP_STRING_TOTAL (1) // Número total de propriedades de evento do tipo string //+------------------------------------------------------------------+ //| Possíveis critérios de ordenação de eventos | //+------------------------------------------------------------------+ #define FIRST_EVN_DBL_PROP (EVENT_PROP_INTEGER_TOTAL) #define FIRST_EVN_STR_PROP (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL) enum ENUM_SORT_EVENTS_MODE { //--- Classifica por propriedades do tipo inteiro SORT_BY_EVENT_TYPE_EVENT = 0, // Classifica pelo tipo do evento SORT_BY_EVENT_TIME_EVENT = 1, // Classifica pela hora do evento SORT_BY_EVENT_STATUS_EVENT = 2, // Classifica pelo estado do evento(da enumeração ENUM_EVENT_STATUS) SORT_BY_EVENT_REASON_EVENT = 3, // Classifica pelo estado do evento (da enumeração ENUM_EVENT_REASON) SORT_BY_EVENT_TYPE_DEAL_EVENT = 4, // Classifica pelo tipo do evento de negociação SORT_BY_EVENT_TICKET_DEAL_EVENT = 5, // Classifica pelo ticket do evento de negociação SORT_BY_EVENT_TYPE_ORDER_EVENT = 6, // Classifica pelo tipo de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) SORT_BY_EVENT_TYPE_ORDER_POSITION = 7, // Classifica pelo tipo de uma ordem, com base em qual evento de posição está aberto (a ordem da primeira posição) SORT_BY_EVENT_TICKET_ORDER_EVENT = 8, // Classifica pelo ticket de uma ordem, com base em qual posição de negociação está aberto (a ordem da última posição) SORT_BY_EVENT_TICKET_ORDER_POSITION = 9, // Classifica pelo ticket de uma ordem, com base em qual posição de negociação está aberto (a ordem da última posição) SORT_BY_EVENT_POSITION_ID = 10, // Classifica pelo ID da posição SORT_BY_EVENT_POSITION_BY_ID = 11, // Classifica pela posição oposta SORT_BY_EVENT_MAGIC_ORDER = 12, // Classifica pela Ordem/negócio/número mágico da posição SORT_BY_EVENT_TIME_ORDER_POSITION = 13, // Classifica pelo horário de uma ordem, com base em qual posição de negociação está aberto (a ordem da primeira posição) //--- Classifica por propriedades do tipo real SORT_BY_EVENT_PRICE_EVENT = FIRST_EVN_DBL_PROP, // Classifica pelo preço que um evento ocorreu SORT_BY_EVENT_PRICE_OPEN = FIRST_EVN_DBL_PROP+1, // Classifica pelo preço de abertura da posição SORT_BY_EVENT_PRICE_CLOSE = FIRST_EVN_DBL_PROP+2, // Classifica pelo preço de encerramento da posição SORT_BY_EVENT_PRICE_SL = FIRST_EVN_DBL_PROP+3, // Classifica pelo preço do StopLoss da posição SORT_BY_EVENT_PRICE_TP = FIRST_EVN_DBL_PROP+4, // Classifica pelo preço do TakeProfit da posição SORT_BY_EVENT_VOLUME_INITIAL = FIRST_EVN_DBL_PROP+5, // Classifica pelo volume inicial SORT_BY_EVENT_VOLUME = FIRST_EVN_DBL_PROP+6, // Classifica pelo volume atual SORT_BY_EVENT_VOLUME_CURRENT = FIRST_EVN_DBL_PROP+7, // Classifica pelo volume restante SORT_BY_EVENT_PROFIT = FIRST_EVN_DBL_PROP+8, // Classifica pelo lucro //--- Classifica pelas propriedades do tipo string SORT_BY_EVENT_SYMBOL = FIRST_EVN_STR_PROP // Classifica pela ordem/posição/símbolo do negócio }; //+------------------------------------------------------------------+
Aqui, nós temos todos os possíveis estados do objeto de evento (semelhante aos estados de ordem descritos no primeiro
artigo), motivos de ocorrência de eventos, todas as propriedades de eventos e critérios para ordenar os eventos para a busca pelas
propriedades. Tudo isso já é familiar dos artigos anteriores. Nós sempre podemos
voltar ao começo e refrescar a memória para esclarecimento.
Além do estado do evento fornecendo um dado geral do evento, o motivo do evento (ENUM_EVENT_REASON) já contém todos os detalhes sobre a origem
específica do evento.
Por exemplo, se um evento tiver um estado de posição no mercado (EVENT_STATUS_MARKET_POSITION), o motivo da ocorrência do evento será
especificado no campo do objeto EVENT_PROP_REASON_EVENT. Pode ser a execução de uma ordem pendente (EVENT_REASON_ACTIVATED_PENDING)
ou a abertura de uma posição por uma ordem à mercado (EVENT_REASON_DONE). Além disso, as seguintes nuances também são consideradas: se uma
posição for aberta parcialmente (nem todo o volume da ordem pendente ou à mercado foi executado), o motivo do evento é
EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ou EVENT_REASON_DONE_PARTIALLY etc.
Assim, um objeto do evento contém os dados inteiros do evento e uma ordem que o acionou. Além disso, os eventos históricos fornecem os
dados sobre as duas ordens — a primeira ordem da posição e a ordem de encerramento da posição.
Assim, os dados sobre as ordens, negócios e posição no objeto de evento nos permitem monitorar toda a cadeia de eventos da posição dentro
de toda o histórico de sua existência — da abertura ao encerramento.
As constantes de enumeração ENUM_EVENT_REASON estão localizadas e numeradas para que, quando o estado do evento for "deal", o tipo de
negócio caia na
enumeração ENUM_DEAL_TYPE no caso do tipo de negócio exceder o DEAL_TYPE_SELL. Assim, nós acabamos com os
tipos de operações de saldo. A descrição da operação de saldo é enviada ao evento de motivo ao definir o tipo de negócio na classe preparada
para a criação.
O mudança a ser adicionada ao tipo de negócio é calculado na substituição de
macro #define REASON_EVENT_SHIFT. É necessário definir o tipo de operação de saldo na enumeração ENUM_EVENT_REASON.
Vamos adicionar as funções que retornam as descrições dos tipos de ordem, posição e negócio, bem como a função retornando o nome da posição dependendo do tipo da ordem que abriu ela. Todas as funções são adicionadas ao arquivo DELib.mqh localizado na biblioteca Services. Isso permite a saída conveniente das ordens, posições e ofertas.
//+------------------------------------------------------------------+ //| Retorna o nome da ordem | //+------------------------------------------------------------------+ string OrderTypeDescription(const ENUM_ORDER_TYPE type) { string pref=(#ifdef __MQL5__ "Market order" #else "Position" #endif ); return ( type==ORDER_TYPE_BUY_LIMIT ? "Buy Limit" : type==ORDER_TYPE_BUY_STOP ? "Buy Stop" : type==ORDER_TYPE_SELL_LIMIT ? "Sell Limit" : type==ORDER_TYPE_SELL_STOP ? "Sell Stop" : #ifdef __MQL5__ type==ORDER_TYPE_BUY_STOP_LIMIT ? "Buy Stop Limit" : type==ORDER_TYPE_SELL_STOP_LIMIT ? "Sell Stop Limit" : type==ORDER_TYPE_CLOSE_BY ? TextByLanguage("Закрывающий ордер","Order for closing by") : #else type==ORDER_TYPE_BALANCE ? TextByLanguage("Балансовая операция","Balance operation") : type==ORDER_TYPE_CREDIT ? TextByLanguage("Кредитная операция","Credit operation") : #endif type==ORDER_TYPE_BUY ? pref+" Buy" : type==ORDER_TYPE_SELL ? pref+" Sell" : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+ //| Retorna o nome da posição | //+------------------------------------------------------------------+ string PositionTypeDescription(const ENUM_POSITION_TYPE type) { return ( type==POSITION_TYPE_BUY ? "Buy" : type==POSITION_TYPE_SELL ? "Sell" : TextByLanguage("Неизвестный тип позиции","Unknown position type") ); } //+------------------------------------------------------------------+ //| Retorna o nome do negócio | //+------------------------------------------------------------------+ string DealTypeDescription(const ENUM_DEAL_TYPE type) { return ( type==DEAL_TYPE_BUY ? TextByLanguage("Сделка на покупку","Buy deal") : type==DEAL_TYPE_SELL ? TextByLanguage("Сделка на продажу","Sell deal") : type==DEAL_TYPE_BALANCE ? TextByLanguage("Балансовая операция","Balance operation") : type==DEAL_TYPE_CREDIT ? TextByLanguage("Начисление кредита","Credit") : type==DEAL_TYPE_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : type==DEAL_TYPE_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : type==DEAL_TYPE_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : type==DEAL_TYPE_COMMISSION ? TextByLanguage("Дополнительные комиссии","Additional comissions") : type==DEAL_TYPE_COMMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : type==DEAL_TYPE_COMMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : type==DEAL_TYPE_COMMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : type==DEAL_TYPE_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Agency commission charged at the end of month") : type==DEAL_TYPE_BUY_CANCELED ? TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") : type==DEAL_TYPE_SELL_CANCELED ? TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") : type==DEAL_DIVIDEND ? TextByLanguage("Начисление дивиденда","Dividend operations") : type==DEAL_DIVIDEND_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : type==DEAL_TAX ? TextByLanguage("Начисление налога","Tax charges") : TextByLanguage("Неизвестный тип сделки","Unknown deal type") ); } //+------------------------------------------------------------------+ //| Retorna o tipo de posição pelo tipo da ordem | //+------------------------------------------------------------------+ ENUM_POSITION_TYPE PositionTypeByOrderType(ENUM_ORDER_TYPE type_order) { if( type_order==ORDER_TYPE_BUY || type_order==ORDER_TYPE_BUY_LIMIT || type_order==ORDER_TYPE_BUY_STOP #ifdef __MQL5__ || type_order==ORDER_TYPE_BUY_STOP_LIMIT #endif ) return POSITION_TYPE_BUY; else if( type_order==ORDER_TYPE_SELL || type_order==ORDER_TYPE_SELL_LIMIT || type_order==ORDER_TYPE_SELL_STOP #ifdef __MQL5__ || type_order==ORDER_TYPE_SELL_STOP_LIMIT #endif ) return POSITION_TYPE_SELL; return WRONG_VALUE; } //+------------------------------------------------------------------+
Ao testar a classe de coleta de eventos, um problema muito desagradável foi descoberto: ao criar as listas de ordens e negócios no terminal usando a HistorySelect() e o acesso subsequente aos novos elementos das listas, eu descobri que as ordens não são listados pela ordem de ocorrência do evento, mas pelo tempo de colocação delas. Deixe-me explicar:
- abrir uma posição,
- colocar uma ordem pendente imediatamente
- encerrar parte de uma posição,
- aguardar até que uma ordem pendente seja ativada
Espera-se que a sequência dos eventos no histórico seja a seguinte:
abrir uma posição, colocar uma ordem, encerramento
parcial, ativação da ordem — na ordem de conduzir as operações ao longo do tempo. Mas a sequência de eventos na ordem comum e no histórico de
negócios é a seguinte:
- abrir uma posição
- colocar uma ordem
- ativação da ordem
- encerramento parcial
Em outras palavras, os históricos de ordens e negócios vivem suas próprias vidas dentro do terminal e não se correlacionam entre si, o que também é razoável, já que são duas listas com seus próprias históricos.
A classe de coleções de ordens e negócios é feita de tal forma que ao alterar qualquer uma das listas (ordens ou negócios), o último evento da conta é lido para não escanear constantemente o histórico, que é muito caro. Mas, considerando o que foi exposto acima e ao conduzir as operações de negociação, nós não monitoramos a sequência de ações. Nós simplesmente colocamos uma ordem e esperamos pela sua ativação. Depois de abrir uma posição, nós trabalhamos exclusivamente com ela. Nesse caso, todos os eventos devem estar localizados na ordem necessária, permitindo o seu monitoramento. No entanto, isso é insuficiente. Nós precisamos trabalhar em qualquer sequência, e o programa deve ser capaz de encontrar o evento certo e apontá-lo com precisão.
Com base no exposto acima, eu melhorei a classe de coleções do histórico de ordens e eventos. Agora, se um evento fora de ordem for exibido, a classe localizará a ordem necessária, criará seu objeto e o colocará na lista para ser o último, de modo que a classe de coleção de eventos sempre possa definir com precisão o último evento ocorrido.
Para implementar o recurso, vamos adicionar três novos métodos para a seção privada do histórico de ordens e da classe de coleção de negócios:
//--- Retorna a flag do objeto da ordem pelo seu tipo e ticket na lista do histórico de ordens e negócios bool IsPresentOrderInList(const ulong order_ticket,const ENUM_ORDER_TYPE type); //--- Retorna o tipo da ordem e ticket "perdidos" ulong OrderSearch(const int start,ENUM_ORDER_TYPE &order_type); //--- Cria o objeto da ordem e coloca-o na lista bool CreateNewOrder(const ulong order_ticket,const ENUM_ORDER_TYPE order_type);
e sua implementação além do corpo da classe também.
O método retornando a flag da presença do objeto de ordem na lista pelo seu ticket e tipo:
//+-----------------------------------------------------------------------------+ //| Retorna a flag da presença do objeto de ordem na lista pelo tipo e ticket | //+-----------------------------------------------------------------------------+ bool CHistoryCollection::IsPresentOrderInList(const ulong order_ticket,const ENUM_ORDER_TYPE type) { CArrayObj* list=dynamic_cast<CListObj*>(&this.m_list_all_orders); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,type,EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,order_ticket,EQUAL); return(list.Total()>0); } //+------------------------------------------------------------------+
Cria o ponteiro para a lista usando o typecasting dinâmico
(envia a lista CArrayObj para a classe CSelect, enquanto as listas de coleções são do tipo CListObj e são herdadas da CArrayObj)
Deixar apenas as ordens tendo
o tipo passado para o método pela entrada.
Deixar apenas as ordens
tendo o ticket passado para o método pela entrada.
Se
tal ordem existir (a lista é maior que zero), retorne
true.
O método retornando um tipo e um método de uma ordem, que não é o último da lista de terminais, mas não está na lista de coleções:
//+------------------------------------------------------------------+ //| Retorna o tipo da ordem e ticket "perdidos" | //+------------------------------------------------------------------+ ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type) { ulong order_ticket=0; 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; } return order_ticket; } //+------------------------------------------------------------------+
O índice da última ordem é passado para a lista de ordens do
terminal. Como o índice especifica a ordem já presente na coleção, o
o loop de busca deve ser iniciado a partir da ordem anterior na lista
(start-1).
Como a ordem necessária geralmente está localizada
próximo ao final da lista, procura-se por uma ordem com um ticket e um tipo ausente na coleção no loop do final da lista usando o método
IsPresentOrderInList().
Se a ordem estiver presente na coleção, verifica a próxima.
Assim que houver uma ordem faltando na coleção,
escrevemos o seu ticket e o tipo
e devolvemos ao programa que realizou a chamada. O ticket é retornado pelo
resultado do método, enquanto o tipo é retornado na variável
através do link.
Já que agora nós precisamos criar os objetos de ordem em vários lugares dentro da classe (ao definir uma nova ordem e ao procurar por uma "perdida"), vamos fazer um método separado da criação de um objeto de ordem e colocá-lo na lista de coleções:
//+------------------------------------------------------------------+ //| Cria o objeto da ordem e coloca-o na lista | //+------------------------------------------------------------------+ bool CHistoryCollection::CreateNewOrder(const ulong order_ticket,const ENUM_ORDER_TYPE order_type) { COrder* order=NULL; if(order_type==ORDER_TYPE_BUY) { order=new CHistoryOrder(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_BUY_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_BUY_STOP) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL) { order=new CHistoryOrder(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL_STOP) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } #ifdef __MQL5__ else if(order_type==ORDER_TYPE_BUY_STOP_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL_STOP_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_CLOSE_BY) { order=new CHistoryOrder(order_ticket); if(order==NULL) return false; } #endif if(this.m_list_all_orders.InsertSort(order)) return true; else { delete order; return false; } return false; } //+------------------------------------------------------------------+
Aqui tudo é simples e claro: o método recebe o ticket da ordem e o seu tipo, e um novo objeto de ordem é criado dependendo do tipo da ordem. Se o objeto não puder ser criado, é retornado false. Se o objeto for criado com sucesso, ele é colocado na coleção e é retornado true. Se ele não puder ser colocado na coleção, um objeto recém-criado será removido e é retornando false.
Vamos mudar o método Refresh() da classe de coleção, já que deve-se processar a "perda" de uma ordem necessária:
//+------------------------------------------------------------------+ //| Atualiza a lista de ordens e negócios | //+------------------------------------------------------------------+ 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(); //--- Posições fechadas e operações de saldo/crédito if(order_type<ORDER_TYPE_BUY_LIMIT || order_type>ORDER_TYPE_SELL_STOP) { CHistoryOrder *order=new CHistoryOrder(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order)) { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list")); delete order; } } else { //--- Ordens pendentes removidas CHistoryPending *order=new CHistoryPending(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order))this.m_list_all_orders.Type() { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list")); delete order; } } } //--- int delta_order=i-m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; this.m_is_trade_event=(this.m_delta_order!=0 ? true : false); //--- __MQL5__ #else if(!::HistorySelect(0,END_TIME)) return; //--- Ordens int total_orders=::HistoryOrdersTotal(),i=m_index_order; for(; i<total_orders; i++) { ulong order_ticket=::HistoryOrderGetTicket(i); if(order_ticket==0) continue; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE); if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY) { //--- Se não houver uma ordem desse tipo e com este ticket na lista, cria um objeto de ordem e adiciona-o à lista if(!this.IsPresentOrderInList(order_ticket,type)) { if(!this.CreateNewOrder(order_ticket,type)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } //--- Essa ordem já está presente na lista, o que significa que a ordem necessária não é a última da lista do histórico. Vamos encontrá-la else { ENUM_ORDER_TYPE type_lost=WRONG_VALUE; ulong ticket_lost=this.OrderSearch(i,type_lost); if(ticket_lost>0 && !this.CreateNewOrder(ticket_lost,type_lost)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } } else { //--- Se não houver uma ordem pendente deste tipo e com este ticket na lista, cria um objeto de ordem e adiciona-o à lista if(!this.IsPresentOrderInList(order_ticket,type)) { if(!this.CreateNewOrder(order_ticket,type)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } //--- Essa ordem já está presente na lista, o que significa que a ordem necessária não é a última da lista do histórico. Vamos encontrá-la else { ENUM_ORDER_TYPE type_lost=WRONG_VALUE; ulong ticket_lost=this.OrderSearch(i,type_lost); if(ticket_lost>0 && !this.CreateNewOrder(ticket_lost,type_lost)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } } } //--- salva o índice da última ordem adicionada e a diferença em comparação com a verificação anterior int delta_order=i-this.m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; //--- Negócios int total_deals=::HistoryDealsTotal(),j=m_index_deal; for(; j<total_deals; j++) { ulong deal_ticket=::HistoryDealGetTicket(j); if(deal_ticket==0) continue; CHistoryDeal *deal=new CHistoryDeal(deal_ticket); if(deal==NULL) continue; if(!this.m_list_all_orders.InsertSort(deal)) { ::Print(DFUN,TextByLanguage("Не удалось добавить сделку в список","Could not add deal to list")); delete deal; } } //--- salve o índice do último negócio adicionado e a diferença em comparação com a verificação anterior int delta_deal=j-this.m_index_deal; this.m_index_deal=j; this.m_delta_deal=delta_deal; //--- Define a nova flag de evento no histórico this.m_is_trade_event=(this.m_delta_order+this.m_delta_deal); #endif } //+------------------------------------------------------------------+
O bloco de tratamento de novas ordens para a MQL5 foi alterado no método. Todas as mudanças implementadas são descritas por comentários e destacados no texto da listagem.
Vamos adicionar a definição do método para procurar por ordens semelhantes na seção pública da classe COrder:
//--- Compara COrder por todas as propriedades (para procurar por objetos de evento iguais) bool IsEqual(COrder* compared_order) const;
e sua implementação além do corpo da classe também:
//+------------------------------------------------------------------+ //| Compara os objetos da COrder por todas as propriedades | //+------------------------------------------------------------------+ bool COrder::IsEqual(COrder *compared_order) const { int beg=0, end=ORDER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; } beg=end; end+=ORDER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; } beg=end; end+=ORDER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i; if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
O método passa por todas as propriedades do objeto de ordem atual e pela ordem
comparada passada para o método pelo ponteiro em um loop.
Assim que qualquer uma das propriedades da ordem atual não for
igual à mesma propriedade da ordem comparada, é retornado
false significando que as ordens não são iguais.
Classes de eventos
O estágio preparatório está completo. Vamos começar a criar as classes de objetos de eventos.
Nós faremos exatamente o mesmo que ao criar classes de ordens. Nós vamos desenvolver uma classe básica de eventos e cinco classes descendentes descritas por seus estados:
- evento de abertura de posição,
- evento de encerramento da posição,
- evento de colocação da ordem pendente,
- evento de remoção da ordem pendente,
- evento de operação de saldo
Criamos uma nova classe CEvent, herdada da classe base CObject, na pasta Events que foi criada anteriormente no diretório da biblioteca Objects. No modelo de classe recém-criado, defina as inclusões necessárias do arquivo de funções de serviço, ordene as classes de coleção, bem como os membros e métodos privados e protegidos da classe:
//+------------------------------------------------------------------+ //| Event.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" #property strict // Necessário para mql4 //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include <Object.mqh> #include "\..\..\Services\DELib.mqh" #include "..\..\Collections\HistoryCollection.mqh" #include "..\..\Collections\MarketCollection.mqh" //+------------------------------------------------------------------+ //| Classe de evento abstrata | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Código do evento //--- Retorna o índice do array, As propriedades do evento do tipo (1) double e (2) string estão localizadas em int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Evento de negociação long m_chart_id; // ID do gráfico do programa de controle int m_digits_acc; // Número de casas decimais da moeda da conta long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Proprieades do evento do tipo inteiro double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Propriedades do evento do tipo real string m_string_prop[EVENT_PROP_STRING_TOTAL]; // Propriedades do evento do tipo string //--- retorna a presença da flag no evento de negociação bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- Construtor paramétrico protegido CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- Construtor padrão CEvent(void){;} //--- Define as propriedades do tipo inteiro, (2) real e (3) string do evento void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Retorna as propriedades do evento do tipo (1) inteiro, (2) real e (3) string do array de propriedades long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Retorna a flag do evento que suporta a propriedade virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Define o ID do gráfico do programa de controle void SetChartID(const long id) { this.m_chart_id=id; } //--- Decodifica o código do evento e configura o evento de negociação, (2) retorna o evento de negociação void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Envia o evento para o gráfico (implementação em classes descendentes) virtual void SendEvent(void) {;} //--- Compara os objetos CEvent por uma propriedade especificada (para classificar as listas por uma propriedade de objeto de evento especificada) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compara os objetos CEvent por todas as propriedades (para procurar por objetos de evento iguais) bool IsEqual(CEvent* compared_event) const; //+--------------------------------------------------------------------+ //| Métodos de acesso simplificado às propriedades do objeto de evento | //+--------------------------------------------------------------------+ //--- Retorna o (1) tipo do evento, (2) horário do evento em milissegundos, (3) estado do evento, (4) motivo do evento, (5) tipo de negócio, (6) ticket do negócio, //--- (7) tipo da ordem, com base no negócio que foi executado, (8) tipo da ordem que abriu uma posição, (9) ticket da última ordem que gerou a posição, //--- (10) ticket da primeira ordem que gerou a posição, (11) ID da posição, (12) ID da posição oposta, (13) número mágico, (14) horário da abertura de posição ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } long TypeDeal(void) const { return this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } long TypeOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } long TypeOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- Retorna (1) o preço em que o evento ocorreu, (2) preço de abertura, (3) preço de fechamento, //--- (4) preço do StopLoss, (5) preço do TakeProfit, (6) lucro, (7) volume solicitado, (8), volume executado, (9) volume restante double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_INITIAL); } double VolumeExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_EXECUTED); } double VolumeCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_CURRENT); } //--- Retorna um símbolo string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } //+------------------------------------------------------------------+ //| Descrições de propriedades de objetos da ordem | //+------------------------------------------------------------------+ //--- Retorna a descrição das propriedades do tipo (1) inteiro, (2) real e (3) string da ordem string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Retorna o (1) estado e o (2) tipo do evento string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Retorna o nome de uma (1) ordem/posição/negócio, (2) ordem pai, (3) posição string TypeOrderDescription(void) const; string TypeOrderBasedDescription(void) const; string TypePositionDescription(void) const; //--- Retorna o nome do motivo do negócio/ordem/posição string ReasonDescription(void) const; //--- Exibe (1) descrição das propriedades da ordem (full_prop=true - todas as propriedades, false - somente as suportadas), //--- (2) mensagem de evento curta (implementação nas classes descendentes) no diário void Print(const bool full_prop=false); virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
O construtor recebe o estado
do evento, código de evento de negociação e o ticket
da ordem ou negócio que desencadeou o evento.
Aqui, quase tudo é semelhante a um construtor protegido da classe COrder considerada anteriormente na primeira parte da descrição da biblioteca.
A diferença é que apenas duas propriedades do evento são preenchidas no construtor
protegido da classe. Estes são o estado do evento e o ticket de uma ordem/negócio que acionou o evento. O tipo de evento é detectado e
salvo no método da classe SetTypeEvent() com base no código de evento passado para o construtor. Todas as outras propriedades de evento são
detectadas a partir do estado de ordens e negócios envolvidas no evento e definidas pelos métodos de classe correspondentes
separadamente. Isso é feito porque os eventos devem ser detectados na classe de coleção de eventos, com o método definindo todas as
propriedades para um evento recém-criado.
Nós consideramos o código do evento (m_event_code),
bem como seu preenchimento e interpretação na
quarta parte da descrição da biblioteca. Nós o movemos da classe CEngine já que
ele foi colocado na classe base da biblioteca temporariamente para verificar o funcionamento dos eventos. Agora ele será calculado na
classe de coleção de eventos e passado para o construtor da classe ao criar um objeto de evento.
O evento de negociação em si (m_trade_event) é
compilado decodificando o código do evento no método SetTypeEvent(), nós já descrevemos o método de decodificação do código de evento no
quarto artigo.
Nós precisamos do ID
do gráfico do programa de controle (
m_chart_id) para enviar as mensagens personalizadas sobre os eventos para ele.
O
número de casas decimais da moeda da conta (
m_digits_acc) é necessário para a exibição correta das mensagens sobre os eventos para o diário.
Os métodos de comparação Compare() e IsEqual() das propriedades de evento do objeto são bastante simples e transparentes. Nós consideramos o método Compare() na primeira parte da descrição da biblioteca. Ele foi semelhante ao método do objeto COrder. Em comparação com o primeiro método que compara dois objetos apenas por uma das propriedades, IsEqual() compara esses dois objetos por todos os campos. Se todos os campos dos dois objetos forem semelhantes (cada propriedade do objeto atual é igual à propriedade apropriada da comparação), os dois objetos são idênticos. O método verifica todas as propriedades dos dois objetos em um loop e retorna false assim que uma discrepância é detectada. Não há sentido em outras verificações, pois uma das propriedades do objeto não é mais igual à mesma propriedade do objeto comparado.
//+------------------------------------------------------------------+ //| Compara os objetos CEvent pela propriedade especificada | //+------------------------------------------------------------------+ int CEvent::Compare(const CObject *node,const int mode=0) const { const CEvent *event_compared=node; //--- compara as propriedades inteiras de dois eventos if(mode<EVENT_PROP_INTEGER_TOTAL) { long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compara as propriedades inteiras de dois objetos if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL) { double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compara as propriedades de string de dois objetos else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL) { string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Compara os eventos CEvent por todas as propriedades | //+------------------------------------------------------------------+ bool CEvent::IsEqual(CEvent *compared_event) const { int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Vamos dar uma olhada mais de perto no método SetTypeEvent()
Todas as verificações e ações necessárias são definidas diretamente nos comentários do código:
//+------------------------------------------------------------------+ //| Decodifica o código do evento e define um evento de negociação | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Ordem pendente colocada (verifica a correspondência do código do evento, pois só pode haver uma flag aqui) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Ordem pendente removida (verifica a correspondência do código do evento, pois só pode haver uma flag aqui) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Posição aberta (Verifica a presença de várias flags no código do evento) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- Se a ordem pendente for ativada por um preço if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "ordem pendente ativada" ou "ordem pendente parcialmente ativada" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- verifica a flag de abertura parcial e define o evento de negociação "Posição aberta" ou "Posição parcialmente aberta" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Posição encerrada (Verifica a presença de várias flags no código do evento) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- se uma posição for encerrada por StopLoss if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada por StopLoss" ou "Posição parcialmente encerrada por StopLoss" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- se uma posição for encerrada por TakeProfit else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada por TakeProfit" ou "Posição parcialmente encerrada por TakeProfit" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- se uma posição é encerrada por uma oposta else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada pelo lado oposto" ou "Posição parcialmente encerrada pelo lado oposto" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Se uma posição foi encerrada else { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada" ou "Posição parcialmente encerrada" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Operação de saldo na conta (esclarece o evento pelo tipo de negócio) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Inicializa um evento de negociação this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Obtém o tipo de negócio ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- se o negócio é uma operação de saldo if(deal_type==DEAL_TYPE_BALANCE) { //--- verifica o lucro do negócio e define um evento (depósito ou retirada de fundos) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- Os tipos de operação de saldo restantes correspondem à enumeração ENUM_DEAL_TYPE, começando com DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- configura o evento this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+
Aqui tudo é simples: um código de evento é passado para o método e os sinalizadores de código de evento são verificados. Se o código tiver a flag marcada, o evento de negociação apropriado é definido. Como o código do evento pode ter várias flags, todos as flags possíveis para o evento são verificadas e o tipo de evento é definido a partir de sua combinação. Em seguida, o tipo de evento é adicionado à variável da classe apropriada e é inserido na propriedade do objeto de evento (EVENT_PROP_TYPE_EVENT).
Vamos dar uma olhada nos métodos de classe restantes:
//+------------------------------------------------------------------+ //| Retorna a descrição da propriedade do tipo inteiro de um evento | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event type")+": "+this.TypeEventDescription() : property==EVENT_PROP_TIME_EVENT ? TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_STATUS_EVENT ? TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\"" : property==EVENT_PROP_REASON_EVENT ? TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription() : property==EVENT_PROP_TYPE_DEAL_EVENT ? TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_DEAL_EVENT ? TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORDER_EVENT ? TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORDER_POSITION ? TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORDER_POSITION ? TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TICKET_ORDER_EVENT ? TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_ORDER ? TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position open time")+": "+TimeMSCtoString(this.GetProperty(property)) : "" ); } //+------------------------------------------------------------------+ //| Retorna a descrição da propriedade do tipo real de um evento | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property) { int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS); int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL)); return ( property==EVENT_PROP_PRICE_EVENT ? TextByLanguage("Цена события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_OPEN ? TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_CLOSE ? TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL ? TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP ? TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_VOLUME_INITIAL ? TextByLanguage("Начальный объём","Initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_EXECUTED ? TextByLanguage("Исполненный объём","Executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_CURRENT ? TextByLanguage("Оставшийся объём","Remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : "" ); } //+------------------------------------------------------------------+ //| Retorna a descrição da propriedade do tipo string de um evento | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property) { return TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""; } //+------------------------------------------------------------------+ //| Retorna o nome do estado do evento | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? TextByLanguage("Установлен отложенный ордер","Pending order placed") : status==EVENT_STATUS_MARKET_POSITION ? TextByLanguage("Открыта позиция","Position opened") : status==EVENT_STATUS_HISTORY_PENDING ? TextByLanguage("Удален отложенный ордер","Pending order removed") : status==EVENT_STATUS_HISTORY_POSITION ? TextByLanguage("Закрыта позиция","Position closed") : status==EVENT_STATUS_BALANCE ? TextByLanguage("Балансная операция","Balance operation") : "" ); } //+------------------------------------------------------------------+ //| Retorna o nome do evento de negociação | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_PENDING_ORDER_PLASED ? TextByLanguage("Отложенный ордер установлен","Pending order placed") : event==TRADE_EVENT_PENDING_ORDER_REMOVED ? TextByLanguage("Отложенный ордер удалён","Pending order removed") : event==TRADE_EVENT_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : event==TRADE_EVENT_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : event==TRADE_EVENT_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : event==TRADE_EVENT_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : event==TRADE_EVENT_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : event==TRADE_EVENT_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : event==TRADE_EVENT_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : event==TRADE_EVENT_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : event==TRADE_EVENT_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : event==TRADE_EVENT_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : event==TRADE_EVENT_TAX ? TextByLanguage("Начисление налога","Tax charges") : event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL ? TextByLanguage("Пополнение средств на балансе","Balance refill") : event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED ? TextByLanguage("Отложенный ордер активирован ценой","Pending order activated") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ? TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially") : event==TRADE_EVENT_POSITION_OPENED ? TextByLanguage("Позиция открыта","Position opened") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position opened partially") : event==TRADE_EVENT_POSITION_CLOSED ? TextByLanguage("Позиция закрыта","Position closed") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL ? TextByLanguage("Позиция закрыта частично","Position closed partially") : event==TRADE_EVENT_POSITION_CLOSED_BY_POS ? TextByLanguage("Позиция закрыта встречной","Position closed by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS ? TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_BY_SL ? TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_BY_TP ? TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL ? TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP ? TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit") : event==TRADE_EVENT_POSITION_REVERSED ? TextByLanguage("Разворот позиции","Position reversal") : event==TRADE_EVENT_POSITION_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : TextByLanguage("Нет торгового события","No trade event") ); } //+------------------------------------------------------------------+ //| Retorna o nome da ordem/posição/negócio | //+------------------------------------------------------------------+ string CEvent::TypeOrderDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : "Unknown" ); } //+------------------------------------------------------------------+ //| Retorna o nome da ordem pai | //+------------------------------------------------------------------+ string CEvent::TypeOrderBasedDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Retorna o nome da posição | //+------------------------------------------------------------------+ string CEvent::TypePositionDescription(void) const { ENUM_POSITION_TYPE type=PositionTypeByOrderType((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); return PositionTypeDescription(type); } //+------------------------------------------------------------------+ //| Retorna o nome do motivo do negócio/ordem/posição | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Запрос выполнен полностью","Request fully executed") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Запрос выполнен частично","Request partially executed") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals from balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+ //| Exibe as propriedades do evento no diário | //+------------------------------------------------------------------+ void CEvent::Print(const bool full_prop=false) { ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" ============="); int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
A lógica de todos esses métodos é semelhante à dos métodos de saída de dados de ordem já descritos. Portanto, nós não nos concentraremos neles, pois tudo é bem simples e visualmente compreensível aqui.
A listagem completa da classe de eventos:
//+------------------------------------------------------------------+ //| Event.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" #property strict // Necessário para mql4 //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include <Object.mqh> #include "\..\..\Services\DELib.mqh" #include "..\..\Collections\HistoryCollection.mqh" #include "..\..\Collections\MarketCollection.mqh" //+------------------------------------------------------------------+ //| Classe de evento abstrata | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Código de evento //--- Retorna o índice do array, As propriedades do evento do tipo (1) double e (2) string estão localizadas em int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Evento de negociação long m_chart_id; // ID do gráfico do programa de controle int m_digits_acc; // Número de casas decimais da moeda da conta long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Proprieades do evento do tipo inteiro double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Propriedades do evento do tipo real string m_string_prop[EVENT_PROP_STRING_TOTAL]; // Propriedades do evento do tipo string //--- retorna a presença da flag no evento de negociação bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- Construtor paramétrico protegido CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- Construtor padrão CEvent(void){;} //--- Define as propriedades do tipo inteiro, (2) real e (3) string do evento void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Retorna as propriedades do evento do tipo (1) inteiro, (2) real e (3) string do array de propriedades long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Retorna a flag do evento que suporta a propriedade virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Define o ID do gráfico do programa de controle void SetChartID(const long id) { this.m_chart_id=id; } //--- Decodifica o código do evento e configura o evento de negociação, (2) retorna o evento de negociação void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Envia o evento para o gráfico (implementação em classes descendentes) virtual void SendEvent(void) {;} //--- Compara os objetos CEvent por uma propriedade especificada (para classificar as listas por uma propriedade de objeto de evento especificada) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compara os objetos CEvent por todas as propriedades (para procurar por objetos de evento iguais) bool IsEqual(CEvent* compared_event); //+--------------------------------------------------------------------+ //| Métodos de acesso simplificado às propriedades do objeto de evento | //+--------------------------------------------------------------------+ //--- Retorna o (1) tipo do evento, (2) horário do evento em milissegundos, (3) estado do evento, (4) motivo do evento, (5) tipo de negócio, (6) ticket do negócio, //--- (7) tipo da ordem, com base no negócio que foi executado, (8) tipo da ordem que abriu uma posição, (9) ticket da última ordem que gerou a posição, //--- (10) ticket da primeira ordem que gerou a posição, (11) ID da posição, (12) ID da posição oposta, (13) número mágico, (14) horário da abertura de posição ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } long TypeDeal(void) const { return this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } long TypeOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } long TypeOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- Retorna (1) o preço em que o evento ocorreu, (2) preço de abertura, (3) preço de fechamento, //--- (4) preço do StopLoss, (5) preço do TakeProfit, (6) lucro, (7) volume solicitado, (8), volume executado, (9) volume restante double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_INITIAL); } double VolumeExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_EXECUTED); } double VolumeCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_CURRENT); } //--- Retorna um símbolo string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } //+------------------------------------------------------------------+ //| Descrições de propriedades de objetos da ordem | //+------------------------------------------------------------------+ //--- Retorna a descrição das propriedades do tipo (1) inteiro, (2) real e (3) string da ordem string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Retorna o (1) estado e o (2) tipo do evento string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Retorna o nome de uma (1) ordem/posição/negócio, (2) ordem pai, (3) posição string TypeOrderDescription(void) const; string TypeOrderBasedDescription(void) const; string TypePositionDescription(void) const; //--- Retorna o nome do motivo do negócio/ordem/posição string ReasonDescription(void) const; //--- Exibe (1) descrição das propriedades da ordem (full_prop=true - todas as propriedades, false - somente as suportadas), //--- (2) mensagem de evento curta (implementação nas classes descendentes) no diário void Print(const bool full_prop=false); virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+ //| Compara os objetos CEvent pela propriedade especificada | //+------------------------------------------------------------------+ int CEvent::Compare(const CObject *node,const int mode=0) const { const CEvent *event_compared=node; //--- compara as propriedades inteiras de dois eventos if(mode<EVENT_PROP_INTEGER_TOTAL) { long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compara as propriedades reais de dois eventos if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL) { double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compara as propriedades de string de dois eventos else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL) { string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Compara os objetos CEvent por todas as suas propriedades | //+------------------------------------------------------------------+ bool CEvent::IsEqual(CEvent *compared_event) { int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } //--- return true; } //+------------------------------------------------------------------+ //| Decodifica o código do evento e define um evento de negociação | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Ordem pendente colocada (verifica a correspondência do código do evento, pois só pode haver uma flag aqui) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Ordem pendente removida (verifica a correspondência do código do evento, pois só pode haver uma flag aqui) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Posição aberta (Verifica a presença de várias flags no código do evento) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- Se a ordem pendente for ativada por um preço if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "ordem pendente ativada" ou "ordem pendente parcialmente ativada" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- verifica a flag de abertura parcial e define o evento de negociação "Posição aberta" ou "Posição parcialmente aberta" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Posição encerrada (Verifica a presença de várias flags no código do evento) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- se uma posição for encerrada por StopLoss if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada por StopLoss" ou "Posição parcialmente encerrada por StopLoss" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- se uma posição for encerrada por TakeProfit else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada por TakeProfit" ou "Posição parcialmente encerrada por TakeProfit" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- se uma posição é encerrada por uma oposta else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada pelo lado oposto" ou "Posição parcialmente encerrada pelo lado oposto" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Se uma posição foi encerrada else { //--- verifica a flag de encerramento parcial e define o evento de negociação "Posição encerrada" ou "Posição parcialmente encerrada" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Operação de saldo na conta (esclarece o evento pelo tipo de negócio) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Inicializa um evento de negociação this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Obtém o tipo de negócio ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- se o negócio é uma operação de saldo if(deal_type==DEAL_TYPE_BALANCE) { //--- verifica o lucro do negócio e define um evento (depósito ou retirada de fundos) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- Os tipos de operação de saldo restantes correspondem à enumeração ENUM_DEAL_TYPE, começando com DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- configura o evento this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+ //| Retorna a descrição da propriedade do tipo inteiro de um evento | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event type")+": "+this.TypeEventDescription() : property==EVENT_PROP_TIME_EVENT ? TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_STATUS_EVENT ? TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\"" : property==EVENT_PROP_REASON_EVENT ? TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription() : property==EVENT_PROP_TYPE_DEAL_EVENT ? TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_DEAL_EVENT ? TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORDER_EVENT ? TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORDER_POSITION ? TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORDER_POSITION ? TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TICKET_ORDER_EVENT ? TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_ORDER ? TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property)) : "" ); } //+------------------------------------------------------------------+ //| Retorna a descrição da propriedade do tipo real de um evento | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property) { int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS); int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL)); return ( property==EVENT_PROP_PRICE_EVENT ? TextByLanguage("Цена события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_OPEN ? TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_CLOSE ? TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL ? TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP ? TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_VOLUME_INITIAL ? TextByLanguage("Начальный объём","Initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_EXECUTED ? TextByLanguage("Исполненный объём","Executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_CURRENT ? TextByLanguage("Оставшийся объём","Remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : "" ); } //+------------------------------------------------------------------+ //| Retorna a descrição da propriedade do tipo string de um evento | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property) { return TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""; } //+------------------------------------------------------------------+ //| Retorna o nome do estado do evento | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? TextByLanguage("Установлен отложенный ордер","Pending order placed") : status==EVENT_STATUS_MARKET_POSITION ? TextByLanguage("Открыта позиция","Position opened") : status==EVENT_STATUS_HISTORY_PENDING ? TextByLanguage("Удален отложенный ордер","Pending order removed") : status==EVENT_STATUS_HISTORY_POSITION ? TextByLanguage("Закрыта позиция","Position closed") : status==EVENT_STATUS_BALANCE ? TextByLanguage("Балансная операция","Balance operation") : "" ); } //+------------------------------------------------------------------+ //| Retorna o nome do evento de negociação | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_PENDING_ORDER_PLASED ? TextByLanguage("Отложенный ордер установлен","Pending order placed") : event==TRADE_EVENT_PENDING_ORDER_REMOVED ? TextByLanguage("Отложенный ордер удалён","Pending order removed") : event==TRADE_EVENT_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : event==TRADE_EVENT_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : event==TRADE_EVENT_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : event==TRADE_EVENT_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : event==TRADE_EVENT_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : event==TRADE_EVENT_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : event==TRADE_EVENT_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : event==TRADE_EVENT_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : event==TRADE_EVENT_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : event==TRADE_EVENT_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : event==TRADE_EVENT_TAX ? TextByLanguage("Начисление налога","Tax charges") : event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL ? TextByLanguage("Пополнение средств на балансе","Balance refill") : event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED ? TextByLanguage("Отложенный ордер активирован ценой","Pending order activated") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ? TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially") : event==TRADE_EVENT_POSITION_OPENED ? TextByLanguage("Позиция открыта","Position opened") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position opened partially") : event==TRADE_EVENT_POSITION_CLOSED ? TextByLanguage("Позиция закрыта","Position closed") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL ? TextByLanguage("Позиция закрыта частично","Position closed partially") : event==TRADE_EVENT_POSITION_CLOSED_BY_POS ? TextByLanguage("Позиция закрыта встречной","Position closed by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS ? TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_BY_SL ? TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_BY_TP ? TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL ? TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP ? TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit") : event==TRADE_EVENT_POSITION_REVERSED ? TextByLanguage("Разворот позиции","Position reversal") : event==TRADE_EVENT_POSITION_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : TextByLanguage("Нет торгового события","No trade event") ); } //+------------------------------------------------------------------+ //| Retorna o nome da ordem/posição/negócio | //+------------------------------------------------------------------+ string CEvent::TypeOrderDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : "Unknown" ); } //+------------------------------------------------------------------+ //| Retorna o nome da ordem pai | //+------------------------------------------------------------------+ string CEvent::TypeOrderBasedDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Retorna o nome da posição | //+------------------------------------------------------------------+ string CEvent::TypePositionDescription(void) const { ENUM_POSITION_TYPE type=PositionTypeByOrderType((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); return PositionTypeDescription(type); } //+------------------------------------------------------------------+ //| Retorna o nome do motivo do negócio/ordem/posição | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Запрос выполнен полностью","Request fully executed") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Запрос выполнен частично","Request partially executed") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals from balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+ //| Exibe as propriedades do evento no diário | //+------------------------------------------------------------------+ void CEvent::Print(const bool full_prop=false) { ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" ============="); int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
A classe do evento base abstrato está pronta. Agora nós precisamos criar cinco classes descendentes que serão um evento indicando seu tipo: colocando uma ordem pendente, excluindo uma ordem pendente, abrindo uma posição, encerrando uma posição e uma operação de saldo.
Crie uma classe descendente com o estado de evento "Colocando uma ordem pendente".
Na pasta Events da biblioteca, crie um novo arquivo da classe CEventOrderPlased chamado de EventOrderPlased.mqh com a classe base CEvent e adicione todo as inclusões e métodos necessários para isso:
//+------------------------------------------------------------------+ //| EventOrderPlased.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Evento de colocação de uma ordem pendente | //+------------------------------------------------------------------+ class CEventOrderPlased : public CEvent { public: //--- Construtor CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) {} //--- Propriedades da ordem do tipo (1) real e (2) inteiro suportadas virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Exibe uma breve mensagem sobre o evento no diário, (2) Envia o evento para o gráfico virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Passa o código do evento e o ticket de uma ordem ou negócio que acionou o evento para o construtor da classe e enviou o estado do evento "Colocando uma ordem pendente" EVENT_STATUS_MARKET_PENDING), o código do evento e o ticket de uma ordem ou negócio para a classe pai na lista de inicialização:
CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) {}
Nós já descrevemos os métodos que retornam as flags de um objeto que suportam certas propriedades - SupportProperty() na primeira parte da descrição da biblioteca. Tudo é o mesmo aqui:
//+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade inteira | //| passada, caso contrário retorna 'false' | //+------------------------------------------------------------------+ bool CEventOrderPlased::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_DEAL_EVENT || property==EVENT_PROP_TICKET_DEAL_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_POSITION_BY_ID || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade inteira | //| passada, real passada, caso contrário retorna 'false' | //+------------------------------------------------------------------+ bool CEventOrderPlased::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+
O objeto de evento pai CEvent apresenta o método Print() que exibe os dados completos em todas as propriedades do objeto de evento suportado
e o método virtual PrintShort(), permitindo exibir os dados necessários sobre evento no diário do terminal em duas linhas.
A implementação do método PrintShort() em cada classe descendente do objeto base do evento será individual, pois os eventos também são
diferentes em suas origens:
//+------------------------------------------------------------------+ //| Exibe uma breve mensagem de evento no diário | //+------------------------------------------------------------------+ void CEventOrderPlased::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypeOrderDescription()+" #"+(string)this.TicketOrderEvent(); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string txt=head+this.Symbol()+" "+vol+" "+type+price+sl+tp+magic; ::Print(txt); } //+------------------------------------------------------------------+
Aqui nós fazemos o seguinte:
- Criamos um cabeçalho de mensagem que consiste na descrição e hora do tipo de evento
- Se a ordem tiver StopLoss, é criado uma linha com sua descrição, caso contrário, a string permanecerá vazia
- Se a ordem tiver TakeProfit, é criado uma linha com sua descrição, caso contrário, a string permanecerá vazia
- Criamos uma linha indicando o volume da ordem
- Se a ordem tiver um número mágico, é criado uma linha com sua descrição, caso contrário, a string permanecerá vazia
- Criamos uma linha especificando o tipo de ordem e o ticket
- Criamos uma linha especificando o preço da ordem e o símbolo na qual a ordem é colocada
- Criamos uma linha completa de todas as descrições acima
- Exibimos a linha criada no diário
O método de envio de um evento personalizado para o gráfico é bem simples:
//+------------------------------------------------------------------+ //| Envia o evento para o gráfico | //+------------------------------------------------------------------+ void CEventOrderPlased::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+
Primeiro, uma breve mensagem sobre o evento é exibida no diário,
então o evento personalizado
EventChartCustom() é enviado para o gráfico
especificado no ID do gráfico
m_chart_id da classe de evento base CEvent.
Envia
o evento
m_trade_event para o ID do evento,
ticket da ordem — para o
parâmetro do tipo
long,
preço da ordem — para o parâmetro do tipo
double,
símbolo
da ordem — para o parâmetro do tipo
string.
A classe para exibir as mensagens permitindo que os usuários definam os níveis de mensagem para exibir apenas os dados necessários no diário deve ser desenvolvida no futuro. No atual estágio de desenvolvimento da biblioteca, todas as mensagens são exibidas por padrão.
Vamos considerar as listagem completa de outras classes de eventos.
Classe do evento "Remoção de ordem pendente":
//+------------------------------------------------------------------+ //| EventOrderRemoved.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Evento de colocação de ordem pendente | //+------------------------------------------------------------------+ class CEventOrderRemoved : public CEvent { public: //--- Construtor CEventOrderRemoved(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_PENDING,event_code,ticket) {} //--- Propriedades da ordem do tipo (1) real e (2) inteiro suportadas virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Exibe uma breve mensagem sobre o evento no diário, (2) Envia o evento para o gráfico virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade inteira | //| passada, inteiro passada, caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventOrderRemoved::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_DEAL_EVENT || property==EVENT_PROP_TICKET_DEAL_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade real passada, | //| caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventOrderRemoved::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return(property==EVENT_PROP_PROFIT ? false : true); } //+------------------------------------------------------------------+ //| Exibe uma breve mensagem de evento no diário | //+------------------------------------------------------------------+ void CEventOrderRemoved::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypeOrderDescription()+" #"+(string)this.TicketOrderEvent(); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string txt=head+this.Symbol()+" "+vol+" "+type+price+sl+tp+magic; ::Print(txt); } //+------------------------------------------------------------------+ //| Envia o evento para o gráfico | //+------------------------------------------------------------------+ void CEventOrderRemoved::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+
Classe de evento "Abertura de posição":
//+------------------------------------------------------------------+ //| EventPositionOpen.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Evento abertura de posição | //+------------------------------------------------------------------+ class CEventPositionOpen : public CEvent { public: //--- Construtor CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {} //--- Propriedades da ordem do tipo (1) real e (2) inteiro suportadas virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Exibe uma breve mensagem sobre o evento no diário, (2) Envia o evento para o gráfico virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade inteira | //| passada, inteiro passada, caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return(property==EVENT_PROP_POSITION_BY_ID ? false : true); } //+------------------------------------------------------------------+ //| Retorna 'true' se a ordem suportar a propriedade real passada, | //| caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+ //| Exibe uma breve mensagem de evento no diário | //+------------------------------------------------------------------+ void CEventPositionOpen::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string order=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED) ? " #"+(string)this.TicketOrderPosition() : ""); string activated=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED) ? TextByLanguage(" активацией ордера "," by ")+this.TypeOrderBasedDescription() : ""); string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypePositionDescription()+" #"+(string)this.PositionID(); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string txt=head+this.Symbol()+" "+vol+" "+type+activated+order+price+sl+tp+magic; ::Print(txt); } //+------------------------------------------------------------------+ //| Envia o evento para o gráfico | //+------------------------------------------------------------------+ void CEventPositionOpen::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+
Classe de evento "Encerramento da posição":
//+------------------------------------------------------------------+ //| EventPositionClose.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Evento de abertura de posição | //+------------------------------------------------------------------+ class CEventPositionClose : public CEvent { public: //--- Construtor CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) {} //--- Propriedades da ordem do tipo (1) real e (2) inteiro suportadas virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Exibe uma breve mensagem sobre o evento no diário, (2) Envia o evento para o gráfico virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade inteira | //| passada, inteiro passada, caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade real passada, | //| caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Exibe uma breve mensagem sobre o evento no diário | //+------------------------------------------------------------------+ void CEventPositionClose::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string opposite=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS) ? " by "+this.TypeOrderDescription()+" #"+(string)this.PositionByID() : ""); string vol=::DoubleToString(this.VolumeExecuted(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypePositionDescription()+" #"+(string)this.PositionID()+opposite; string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceClose(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string profit=TextByLanguage(", профит: ",", profit: ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); string txt=head+this.Symbol()+" "+vol+" "+type+price+magic+profit; ::Print(txt); } //+------------------------------------------------------------------+ //| Envia o evento para o gráfico | //+------------------------------------------------------------------+ void CEventPositionClose::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceClose(),this.Symbol()); } //+------------------------------------------------------------------+
Classe de evento "Operação de saldo":
//+------------------------------------------------------------------+ //| EventBalanceOperation.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Evento de abertura de posição | //+------------------------------------------------------------------+ class CEventBalanceOperation : public CEvent { public: //--- Construtor CEventBalanceOperation(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_BALANCE,event_code,ticket) {} //--- Propriedades da ordem do tipo (1) real e (2) inteiro suportadas virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property); //--- (1) Exibe uma breve mensagem sobre o evento no diário, (2) Envia o evento para o gráfico virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade inteira | //| passada, inteiro passada, caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_ORDER_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_EVENT || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_POSITION_BY_ID || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_MAGIC_ORDER || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade real passada, | //| caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return(property==EVENT_PROP_PROFIT ? true : false); } //+------------------------------------------------------------------+ //| Retorna 'true' se o evento suportar a propriedade string passada,| //| caso contrário, retorna 'false' | //+------------------------------------------------------------------+ bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_STRING property) { return false; } //+------------------------------------------------------------------+ //| Exibe uma breve mensagem sobre o evento no diário | //+------------------------------------------------------------------+ void CEventBalanceOperation::PrintShort(void) { string head="- "+this.StatusDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; ::Print(head+this.TypeEventDescription()+": "+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY)); } //+------------------------------------------------------------------+ //| Envia o evento para o gráfico | //+------------------------------------------------------------------+ void CEventBalanceOperation::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TypeEvent(),this.Profit(),::AccountInfoString(ACCOUNT_CURRENCY)); } //+------------------------------------------------------------------+
Como nós podemos ver nas listagens, as classes são diferentes apenas pelo número de propriedades suportadas, o estado enviado ao construtor da classe pai e os métodos PrintShort(), já que cada evento tem seus próprios recursos que devem ser refletidos no diário. Tudo isso pode ser entendido a partir das listagens dos métodos, e você pode analisá-las por conta própria, portanto, não faz sentido insistir nelas. Vamos passar para o desenvolvimento da classe de coleção de eventos.
Coleção de eventos de negociação
Na quarta parte da descrição da biblioteca, nós testamos os eventos de conta
definidos e sua exibição no diário e no EA. No entanto, nós conseguimos acompanhar apenas o evento mais recente. Além disso, toda a
funcionalidade estava localizada na classe base da biblioteca CEngine.
A decisão correta é colocar tudo em uma classe separada e processar todos os eventos que ocorrem nela.
Para conseguir isso, eu desenvolvi os objetos de evento. Agora nós precisamos fazer com que a classe manipule qualquer número de eventos
ocorridos simultaneamente. Afinal, pode haver uma situação em que as ordens pendentes são removidas ou colocadas, ou várias posições são
encerradas simultaneamente em um único loop.
O princípio que nós já testamos em tais situações nos daria apenas o evento mais recente dos vários realizados de uma só vez. Eu acho que
isso é incorreto. Portanto, vamos criar a classe que salva todos os eventos que ocorreram de uma só vez na lista de coleções de eventos. Além
disso, no futuro, será possível usar os métodos dessas classes para percorrer o histórico da conta e recriar tudo o que aconteceu nela desde
que ela foi aberta.
Na DoEasy\Collections, crie o novo arquivo da classe CEventsCollection chamado de EventsCollection.mqh. A classe CListObj deve ser criada como sendo a base.
Em seguida, preencha o modelo de classe recém-criado com todas as inclusões, membros e métodos necessários:
//+------------------------------------------------------------------+ //| EventsCollection.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\EventBalanceOperation.mqh" #include "..\Objects\Events\EventOrderPlaced.mqh" #include "..\Objects\Events\EventOrderRemoved.mqh" #include "..\Objects\Events\EventPositionOpen.mqh" #include "..\Objects\Events\EventPositionClose.mqh" //+------------------------------------------------------------------+ //| Colecão de eventos de conta | //+------------------------------------------------------------------+ class CEventsCollection : public CListObj { private: CListObj m_list_events; // Lista de eventos bool m_is_hedge; // Flag da conta hedge long m_chart_id; // ID do gráfico do programa de controle ENUM_TRADE_EVENT m_trade_event; // Evento da conta de negociação CEvent m_event_instance; // Objeto de evento para buscar pela propriedade //--- Cria um evento de negociação dependendo do estado da ordem void CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market); //--- Seleciona e retorna a lista de ordens pendentes do mercado CArrayObj* GetListMarketPendings(CArrayObj* list); //--- Seleciona e retorna a lista do histórico de (1) ordens pendentes removidos, (2) negócios, (3) todas as ordens de encerramento CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListCloseByOrders(CArrayObj* list); //--- Seleciona e retorna a lista de (1) todas as ordens da posição pelo seu ID, (2) todos os negócios da posição pelo seu ID //--- (3) todos os negócios de entrada no mercado pelo ID da posição, (4) todas os negócios de encerramento de mercado por ID da posição CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Retorna o volume total de todos os negócios (1) IN, (2) OUT da posição pelo seu ID double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Retorna a (1) primeira, (2) última e (3) a ordem de encerramento da lista de todas as ordens da posição, (4) uma ordem por ticket COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetOrderByTicket(CArrayObj* list,const ulong order_ticket); //--- Retorna a flag da presença do objeto de evento na lista de eventos bool IsPresentEventInList(CEvent* compared_event); public: //--- Seleciona os eventos da coleção com o horário dentro do intervalo de begin_time até end_time CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Retorna a lista completa de coleções de eventos "como ela está" CArrayObj *GetList(void) { return &this.m_list_events; } //--- Retorna a lista pelas propriedades de (1) inteiro, (2) real e (3) string selecionadas que atendem ao critério comparado CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Atualiza a lista de eventos void Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals); //--- Define o ID do gráfico do programa de controle void SetChartID(const long id) { this.m_chart_id=id; } //--- Retorna o último evento de negociação na conta ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- Redefine o último evento de negociação void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- Construtor CEventsCollection(void); }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_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=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Redefine o evento de negociação na lista de inicialização do
construtor da classe,
limpa a lista de coleções no corpo do construtor,
define sua classificação pelo horário do evento,
define o ID da lista de
coleções de eventos,
define a flag da conta hedge e
define o ID do gráfico do programa de controle como o gráfico atual.
Vamos considerar os métodos necessários para a classe funcionar.
Os seguintes membros da classe são declarados na seção privada da classe:
CListObj m_list_events; // Lista de eventos bool m_is_hedge; // Flag da conta Hedge long m_chart_id; // ID do gráfico do programa de controle ENUM_TRADE_EVENT m_trade_event; // Evento da conta de negociação CEvent m_event_instance; // Objeto de evento para buscar pela propriedade
A lista de eventos m_list_events
é baseada na CListObj. Ele armazenará os eventos que ocorrem na conta desde o lançamento do programa. Além disso, nós vamos usá-la para receber
o número necessário de vários eventos que ocorreram de uma só vez.
A flag da conta hedge m_is_hedge
é usada para salvar e receber o tipo da conta. O valor da flag (tipo de conta) define o bloco de manipulação dos eventos que ocorrem no gráfico
O
ID do gráfico do programa de controle m_chart_id
recebe os eventos personalizados que ocorrem na conta. O ID é enviado para os objetos de evento e volta ao gráfico. O ID pode ser definido no
programa de controle usando o método criado para essa finalidade.
O evento de negociação m_trade_event
armazena o último evento ocorrido na conta.
O objeto de evento m_event_instance
para busca por uma propriedade — um objeto de amostra especial para uso interno no método que retorna a lista de eventos com as datas especificadas
no intervalo de busca de start até end. Nós já analisamos um método similar na
terceira parte da descrição da biblioteca ao discutir como organizar a
busca nas listas por diferentes critérios.
Aqui na seção privada, você pode ver os métodos necessários para a operação da classe:
//--- Cria um evento de negociação dependendo do estado da ordem void CreateNewEvent(COrder* order,CArrayObj* list_history); //--- Seleciona e retorna a lista de ordens pendentes do mercado CArrayObj* GetListMarketPendings(CArrayObj* list); //--- Seleciona e retorna a lista do histórico de (1) ordens, (2) ordens pendentes removidas, //--- (3) negócios, (4) todas as ordens da posição pelo seu ID, (5) todos os negócios da posição pelo seu ID //--- (6) todos os negócios de entrada pelo ID da posição, (7) todos os negócios de encerramento pelo ID da posição //--- (7) todas as ordens de encerramento CArrayObj* GetListHistoryOrders(CArrayObj* list); CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllCloseByOrders(CArrayObj* list); //--- Retorna o volume total de todos os negócios (1) IN, (2) OUT pelo seu ID double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Retorna a (1) primeira, (2) última e (3) a ordem de encerramento da lista de todas as ordens da posição, (4) uma ordem por ticket COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetOrderByTicket(CArrayObj* list,const ulong order_ticket); //--- Retorna a flag da presença do objeto de evento na lista de eventos bool IsPresentEventInList(CEvent* compared_event);
O método CreateNewEvent() que cria um evento de negociação dependendo do estado da ordem é usado no método Refresh()
da classe principal. Nós vamos considerá-lo ao discutir o método Refresh().
Os métodos para receber as listas de vários tipos de ordens são bem simples — a seleção por uma propriedade especificada foi discutida na terceira parte da descrição da biblioteca. Aqui nós mencionaremos brevemente alguns métodos que consistem de várias iterações de seleção pelas propriedades necessárias.
O método de receber a lista de ordens pendentes do mercado:
//+------------------------------------------------------------------+ //| Seleciona apenas as ordens pendentes de mercado na lista | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListMarketPendings(CArrayObj* list) { if(list.Type()!=COLLECTION_MARKET_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком рыночной коллекции","Error. List is not a list of market collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL); return list_orders; } //+------------------------------------------------------------------+
O tipo da lista passada para o método é verificado primeiro. Se não for uma lista de coleção de mercado, a mensagem de erro é exibida e a lista
vazia é retornada.
Em seguida, as ordens com o estado "Ordem pendente de mercado" são selecionadas da lista passada para o método e a lista obtida é retornada.
Os métodos para receber as listas de ordens pendentes removidas,
negócios e ordens
de encerramento colocadas ao encerrar uma posição por uma oposta:
//+------------------------------------------------------------------+ //| Seleciona somente as ordens pendentes removidas da lista | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListHistoryPendings(CArrayObj* list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); return list_orders; } //+------------------------------------------------------------------+ //| Seleciona apenas os negócios da lista | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListDeals(CArrayObj* list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_deals=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL); return list_deals; } //+------------------------------------------------------------------+ //| Retorna a lista de todas as ordens CloseBy de encerramento da lista| //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListCloseByOrders(CArrayObj *list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL); return list_orders; } //+------------------------------------------------------------------+
Assim como ao retornar a lista de ordens pendentes ativas:
O tipo da lista é verificado e, se não for a coleção do histórico, a mensagem é exibida e NULL é retornado.
Em
seguida, as ordens com os estados "Ordem pendente removida" e "Negócio" são selecionados da lista passada para o método ou as ordens são
selecionadas pelo tipo ORDER_TYPE_CLOSE_BY, dependendo do método, e a lista obtida é retornada.
O método para obter uma lista de todas as ordens pertencentes a uma posição pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna a lista de todas as ordens da posição pelo seu ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id) { CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,NO_EQUAL); return list_orders; } //+------------------------------------------------------------------+
Primeiro, usando a lista passada para o método, forma-se uma lista separada
de todos os objetos com um ponteiro para o ID da posição passado para o método pelo seu parâmetro.
Em seguida, todos
os negócios são removidos da lista obtida, e a lista final é retornada ao programa de chamada. O resultado retornado pelo método pode
ser NULL, portanto, nós devemos verificar o que o método retornou no programa de chamada.
O método para obter uma lista de todas os negócios pertencentes a uma posição pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna a lista de todos os negócios da posição pelo seu ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsByPosID(CArrayObj *list,const ulong position_id) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_deals=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL); return list_deals; } //+------------------------------------------------------------------+
O tipo da lista é verificado primeiro e, se não for o a coleção do histórico, a mensagem é exibida e NULL é retornado.
Em seguida, usando a
lista passada para o método,
é formado uma lista separada de todos os objetos com um ponteiro para o ID da posição
passada para o método pelo seu parâmetro.
Depois disso, apenas
os negócios são deixados na lista obtida, e a lista final é retornada ao programa de chamada. O resultado retornado pelo método pode
ser NULL, portanto, nós devemos verificar o que o método retornou no programa de chamada.
O método para obter uma lista de todos os negócios de entrada no mercado pertencentes a uma posição pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna a lista de todos os negócios de entrada do mercado(IN) | //| pelo ID da posição | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsInByPosID(CArrayObj *list,const ulong position_id) { CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_IN,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Primeiro, usando a lista passada para o método, forma-se uma lista separada
de todos os negócios com um ponteiro para o ID da posição passado para o método pelo seu parâmetro.
Em seguida, apenas
os negócios do tipo DEAL_ENTRY_IN são deixados na lista obtida, e a lista final é retornada ao programa de chamada. O resultado
retornado pelo método pode ser NULL, portanto, nós devemos verificar o que o método retornou no programa de chamada.
O método para obter uma lista de todos os negócios de saída de mercado pertencentes a uma posição pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna a lista de todas os negócios de saída do mercado (OUT) | //| pelo ID da posição | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsOutByPosID(CArrayObj *list,const ulong position_id) { CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_OUT,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Primeiro, usando a lista passada para o método, forma-se uma lista separada
de todos os negócios com um ponteiro para o ID da posição passado para o método pelo seu parâmetro.
Em seguida, apenas
os negócios do tipo DEAL_ENTRY_OUT são deixados na lista obtida, e a lista final é retornada ao programa de chamada. O resultado
retornado pelo método pode ser NULL, portanto, nós devemos verificar o que o método retornou no programa de chamada.
O método retornando o volume total de todos os negócios de uma posição de entrada no mercado pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna o volume total de todas os negócios IN da posição | //| pelo seu ID | //+------------------------------------------------------------------+ double CEventsCollection::SummaryVolumeDealsInByPosID(CArrayObj *list,const ulong position_id) { double vol=0.0; CArrayObj* list_in=this.GetListAllDealsInByPosID(list,position_id); if(list_in==NULL) return 0; for(int i=0;i<list_in.Total();i++) { COrder* deal=list_in.At(i); if(deal==NULL) continue; vol+=deal.Volume(); } return vol; } //+------------------------------------------------------------------+
Primeiro, receber a lista de todas os negócios de entrada da posição, então soma-se os volumes de todas os negócios em um loop. O volume resultante é retornado para o programa de chamada. Se a lista passada para o método estiver vazia ou não for uma lista de coleção do histórico, o método retornará zero.
O método retornando o volume total de todos os negócios de saída da posição pelo seu ID:
//+--------------------------------------------------------------------+ //| Retorna o volume total de todos os negócios OUT da posição pelo seu| //| ID (a participação no fechamento por uma posição oposta é considerada)| //+--------------------------------------------------------------------+ double CEventsCollection::SummaryVolumeDealsOutByPosID(CArrayObj *list,const ulong position_id) { double vol=0.0; CArrayObj* list_out=this.GetListAllDealsOutByPosID(list,position_id); if(list_out!=NULL) { for(int i=0;i<list_out.Total();i++) { COrder* deal=list_out.At(i); if(deal==NULL) continue; vol+=deal.Volume(); } } CArrayObj* list_by=this.GetListCloseByOrders(list); if(list_by!=NULL) { for(int i=0;i<list_by.Total();i++) { COrder* order=list_by.At(i); if(order==NULL) continue; if(order.PositionID()==position_id || order.PositionByID()==position_id) { vol+=order.Volume(); } } } return vol; } //+------------------------------------------------------------------+
Se parte de uma posição, cujo ID foi passado para o método, participou no fechamento de outra posição (como uma posição oposta), ou parte da posição foi fechada por uma posição oposta, isso não é considerado nos negócios da posição. Em vez disso, ele é considerado no campo de propriedade ORDER_PROP_POSITION_BY_ID da última ordem de encerramento da posição. Portanto, esse método tem duas buscas para o volume de encerramento — por negócios e por ordens de encerramento.
Primeiro, recebemos a lista de todos os negócios da posição de saída do mercado,
então
soma-se os volumes de todos os negócios em um loop.
Em
seguida,
recebemos a lista de todos as ordens de encerramento presente
na lista do histórico, usamos o loop para
verificar o pertencimento da ordem selecionada para a posição cujo ID foi passado para o
método. Se a ordem selecionada participou do fechamento de uma posição, seu
volume é adicionado ao total.
O volume resultante é retornado para o programa de chamada. Se a lista passada para o método
estiver vazia ou não for uma lista de coleção do histórico, o método retornará zero.
O método retornando a primeira ordem de abertura pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna a primeira ordem da lista de todos as ordens da posição | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetFirstOrderFromList(CArrayObj* list,const ulong position_id) { CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list_orders.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Primeiro, recebemos a lista de todos as ordens da posição. A lista obtida é ordenada pelo horário de abertura e seu primeiro elemento é levado em consideração. Ele será usado como a primeira ordem da posição. A ordem obtida é retornada ao programa de chamada. Se as listas estiverem vazias, o método retornará NULL.
O método retornando a última ordem da posição pelo seu ID:
//+------------------------------------------------------------------+ //| Retorna a última ordem da lista de todos as ordens da posição | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetLastOrderFromList(CArrayObj* list,const ulong position_id) { CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list_orders.At(list_orders.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Primeiro, recebemos a lista de todos as ordens da posição. A lista obtida é ordenada pelo horário de abertura e seu último elemento é levado em consideração. Ele será usado como a última ordem da posição. A ordem obtida é retornada ao programa de chamada. Se as listas estiverem vazias, o método retornará NULL.
O método que retorna a última ordem de encerramento pelo seu ID (ordem do tipo ORDER_TYPE_CLOSE_BY):
//+------------------------------------------------------------------+ //| Retorna a última ordem de encerramento | //| da lista de todas as ordens da posição | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetCloseByOrderFromList(CArrayObj *list,const ulong position_id) { CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list_orders.At(list_orders.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Uma vez que os encerramentos parciais são possíveis ao encerrar uma posição pela sua oposta, e os volumes das duas posições opostas podem ser
desiguais, a ordem de fechamento pode não ser a única dentro das ordens de posição. Portanto, o método procura por tais ordens e retorna a
última delas — é a última ordem que acionou um evento.
Primeiro, recebemos a lista de todos as ordens da posição. Então, da lista obtida, recebemos a lista contendo apenas as ordens de encerramento (do tipo ORDER_TYPE_CLOSE_BY). A lista obtida de tal maneira é ordenada pelo horário de abertura e seu último elemento é levado em consideração. Ela será usada como a última ordem de encerramento. A ordem obtida é retornada ao programa de chamada. Se as listas estiverem vazias, o método retornará NULL.
Ao encerrar por uma posição oposta, pode haver situações em que a biblioteca vê dois eventos idênticos: duas posições são encerradas e
apenas uma delas tem uma ordem de encerramento, além disso, temos dois negócios. Portanto, para não duplicar o mesmo evento na coleção, nós
devemos primeiro verificar a presença do mesmo evento na lista de coleção de eventos e, se não estiver lá, adicionamos o evento à lista.
O método retornando uma ordem pelo ticket:
//+------------------------------------------------------------------+ //| Retorna a ordem pelo ticket | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetOrderByTicket(CArrayObj *list,const ulong order_ticket) { CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,NO_EQUAL); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TICKET,order_ticket,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; COrder* order=list_orders.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Primeiro, criamos apenas a lista de ordens, então ordenamos
a lista pelo ticket passado pelo parâmetro do método. Como
resultado, nós
retornamos NULL (quando não há
nenhuma ordem com tal ticket), ou o número do ticket.
O método que retorna a presença do evento na lista é usado para verificar se o evento está na lista:
//+------------------------------------------------------------------+ //| Retorna a flag da presença do objeto de evento na lista de eventos| //+------------------------------------------------------------------+ bool CEventsCollection::IsPresentEventInList(CEvent *compared_event) { int total=this.m_list_events.Total(); if(total==0) return false; for(int i=total-1;i>=0;i--) { CEvent* event=this.m_list_events.At(i); if(event==NULL) continue; if(event.IsEqual(compared_event)) return true; } return false; } //+------------------------------------------------------------------+
O ponteiro para o objeto de evento comparado é passado para o método. Se a lista de coleção estiver vazia, é retornado 'false' imediatamente o que significa que não existe tal evento na lista. Depois disso, o próximo evento é retirado da lista em um loop e é comparado com o evento passado para o método usando o método IsEqual() da clase abstrata CEvent. Se o método retornar 'true', esse objeto de evento estará presente na lista de coleções de eventos. Tendo completado o loop ou atingido o último método de string, significa que não há evento na lista, retornando 'false'.
Declare os métodos na seção pública da classe:
public: //--- Seleciona os eventos da coleção com o horário dentro do intervalo de begin_time até end_time CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Retorna a lista completa de coleções de eventos "como ela está" CArrayObj *GetList(void) { return &this.m_list_events; } //--- Retorna a lista pelas propriedades de (1) inteiro, (2) real e (3) string selecionadas que atendem ao critério comparado CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Atualiza a lista de eventos void Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals); //--- Define o ID do gráfico do programa de controle void SetChartID(const long id) { this.m_chart_id=id; } //--- Retorna o último evento de negociação na conta ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- Redefine o último evento de negociação void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- Construtor CEventsCollection(void);
Eu descrevi os métodos para receber a lista completa, as listas por um intervalo de datas e por propriedades inteiras, reais e de string selecionadas na terceira parte da descrição da biblioteca. Aqui, eu vou mostrar apenas a listagem desses métodos para que você possa analisá-los por conta própria.
O método para receber a lista de eventos no intervalo de datas especificado:
//+------------------------------------------------------------------+ //| Seleciona os eventos da coleção com a hora | //| dentro do intervalo de begin_time até end_time | //+------------------------------------------------------------------+ CArrayObj *CEventsCollection::GetListByTime(const datetime begin_time=0,const datetime end_time=0) { CArrayObj *list=new CArrayObj(); if(list==NULL) { ::Print(DFUN+TextByLanguage("Ошибка создания временного списка","Error creating temporary list")); return NULL; } datetime begin=begin_time,end=(end_time==0 ? END_TIME : end_time); if(begin_time>end_time) begin=0; list.FreeMode(false); ListStorage.Add(list); //--- this.m_event_instance.SetProperty(EVENT_PROP_TIME_EVENT,begin); int index_begin=this.m_list_events.SearchGreatOrEqual(&m_event_instance); if(index_begin==WRONG_VALUE) return list; this.m_event_instance.SetProperty(EVENT_PROP_TIME_EVENT,end); int index_end=this.m_list_events.SearchLessOrEqual(&m_event_instance); if(index_end==WRONG_VALUE) return list; for(int i=index_begin; i<=index_end; i++) list.Add(this.m_list_events.At(i)); return list; } //+------------------------------------------------------------------+
O método principal a ser chamado a partir do objeto da biblioteca base quando qualquer um dos eventos ocorre é o Refresh().
Atualmente, o método funciona em contas hedge para a MQL5.
O método recebe os ponteiros para as listas de coleções do histórico e de ordens de mercado, negócios e posições, bem como os dados sobre o
número de ordens recém-criadas ou removidas, posições abertas e encerradas e novos negócios.
Dependendo da lista alterada, o número necessário de ordens ou negócios é feito de acordo com o número de ordens/posições/negócios em um
loop, e é chamado para cada um deles o método CreateNewEvent() para criar os eventos e colocá-los na lista de coleções.
Assim, o novo método de criação do evento é chamado para qualquer evento ocorrido, enquanto o evento é colocado na lista de coleções e o
programa de chamada é notificado de todos os eventos enviando uma mensagem personalizada para o gráfico do programa de chamada.
A variável membro de classe m_trade_event recebe o valor do último evento ocorrido. O método público GetLastTradeEvent()
retorna o valor do último evento de negociação. Há também o método para redefinir o último evento de negociação (semelhante a
GetLastError() e ResetLastError()).
Além disso, há métodos que retornam a lista de coleções de eventos total, por um intervalo de horário e por critérios especificados. O
programa de chamada sempre sabe que um evento ou vários eventos ocorreram, e é possível solicitar a lista de todos esses eventos em uma
quantidade necessária e manipulá-lo de acordo com a lógica do programa interno.
Vamos considerar as listagens dos métodos Refresh() e CreateNewEvent().
O método de atualizar a lista de coleta de eventos:
//+------------------------------------------------------------------+ //| Atualiza a lista de eventos | //+------------------------------------------------------------------+ void CEventsCollection::Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals) { //--- Sai se as listas estiverem vazias if(list_history==NULL || list_market==NULL) return; //--- No caso de uma conta hedging if(this.m_is_hedge) { //--- Se o evento estiver no ambiente de mercado if(is_market_event) { //--- se o número de ordens pendentes colocadas aumentar if(new_market_pendings>0) { //--- Recebe a lista das ordens pendentes recém-colocadas CArrayObj* list=this.GetListMarketPendings(list_market); if(list!=NULL) { //--- Ordena a nova lista por horário de colocação da ordem list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); //--- Pega o número de ordens igual ao número de recém-colocadas do final da lista em um loop (os últimos N eventos) int total=list.Total(), n=new_market_pendings; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Recebe uma ordem da lista, se esta for uma ordem pendente, define um evento de negociação COrder* order=list.At(i); if(order!=NULL && order.Status()==ORDER_STATUS_MARKET_PENDING) this.CreateNewEvent(order,list_history,list_market); } } } } //--- Se o evento estiver no histórico da conta if(is_history_event) { //--- Se o número de ordens do histórico aumentou if(new_history_orders>0) { //--- Recebe somente a lista de ordens pendentes removidas CArrayObj* list=this.GetListHistoryPendings(list_history); if(list!=NULL) { //--- Ordena a nova lista por horário de remoção da ordem list.Sort(SORT_BY_ORDER_TIME_CLOSE_MSC); //--- Pega o número de ordens igual ao número de recém-removidas do final da lista em um loop (os últimos N eventos) int total=list.Total(), n=new_history_orders; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Recebe uma ordem da lista. Se esta for uma ordem pendente removida, define um evento de negociação COrder* order=list.At(i); if(order!=NULL && order.Status()==ORDER_STATUS_HISTORY_PENDING) this.CreateNewEvent(order,list_history,list_market); } } } //--- Se o número de negócios aumentou if(new_deals>0) { //--- Recebe apenas a lista de negócios CArrayObj* list=this.GetListDeals(list_history); if(list!=NULL) { //--- Ordena a nova lista por horário do negócio list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); //--- Pega o número de negócios igual ao número de novos negócios do final da lista em um loop (os últimos N eventos) int total=list.Total(), n=new_deals; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Recebe uma negócio da lista e define um evento de negociação COrder* order=list.At(i); if(order!=NULL) this.CreateNewEvent(order,list_history,list_market); } } } } } //--- No caso de uma conta netting else { } } //+------------------------------------------------------------------+
A listagem dos métodos simples contém todas as condições e ações necessárias quando essas condições são atendidas. Eu acredito que tudo
esteja bem transparente aqui. Atualmente, os eventos são tratados apenas em conta hedge.
Vamos considerar o método para criar um novo evento:
//+------------------------------------------------------------------+ //| Cria um evento de negociação, dependendo do estado da ordem | //+------------------------------------------------------------------+ void CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market) { int trade_event_code=TRADE_EVENT_FLAG_NO_EVENT; ENUM_ORDER_STATUS status=order.Status(); //--- Ordem pendente colocada if(status==ORDER_STATUS_MARKET_PENDING) { trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED; CEvent* event=new CEventOrderPlased(trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Horário do evento event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo do negócio do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Tipo da ordem do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Tipo da ordem do evento event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket da ordem do evento event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket da ordem event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Horário da ordem event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço de um evento que ocorreu event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Preço da ordem colocada event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Preço de encerramento da ordem event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Preço da ordem StopLoss event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Preço da ordem TakeProfit event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Volume solicitado event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()-order.VolumeCurrent()); // Volume executado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Ordem pendente removida if(status==ORDER_STATUS_HISTORY_PENDING) { trade_event_code=TRADE_EVENT_FLAG_ORDER_REMOVED; CEvent* event=new CEventOrderRemoved(trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( order.State()==ORDER_STATE_CANCELED ? EVENT_REASON_CANCEL : order.State()==ORDER_STATE_EXPIRED ? EVENT_REASON_EXPIRED : EVENT_REASON_DONE ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeCloseMSC()); // Horário do evento event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo da ordem do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Tipo de uma ordem que acionou um evento de negócio (a ordem da última posição) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Tipo de uma ordem que acionou um negócio da posição (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Hora de uma ordem, com base em qual negócio da posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço do evento event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Preço da ordem colocada event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Preço de encerramento da ordem event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Preço da ordem StopLoss event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Preço da ordem TakeProfit event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Volume solicitado event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()-order.VolumeCurrent()); // Volume executado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Posição aberta (__MQL4__) if(status==ORDER_STATUS_MARKET_POSITION) { trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; CEvent* event=new CEventPositionOpen(trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpen()); // Horário do evento event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo do negócio do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Tipo de uma ordem que acionou um evento de negócio (a ordem da última posição) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Tipo de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem/negócio/posição event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen()); // Horário de uma ordem, com base em qual negócio da posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço do evento event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Preço de abertura da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Preço de encerramento da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Preço do StopLoss da posição event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Preço do TakeProfit da posição event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Volume solicitado event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Volume executado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Novo negócio (__MQL5__) if(status==ORDER_STATUS_DEAL) { //--- Nova operação de saldo if((ENUM_DEAL_TYPE)order.TypeOrder()>DEAL_TYPE_SELL) { trade_event_code=TRADE_EVENT_FLAG_ACCOUNT_BALANCE; CEvent* event=new CEventBalanceOperation(trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( (ENUM_DEAL_TYPE)order.TypeOrder()==DEAL_TYPE_BALANCE ? (order.Profit()>0 ? EVENT_REASON_BALANCE_REFILL : EVENT_REASON_BALANCE_WITHDRAWAL) : (ENUM_EVENT_REASON)(order.TypeOrder()+REASON_EVENT_SHIFT) ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Horário do evento event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo do negócio do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Tipo de uma ordem que acionou um evento de negócio (a ordem da última posição) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Tipo de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem/negócio/posição event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Horário de uma ordem, com base em qual negócio da posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço do evento event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Preço de abertura da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Preço de encerramento da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Preço do StopLoss do negócio event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Preço do TakeProfit do negócio event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Volume solicitado event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Volume executado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação this.m_list_events.InsertSort(event); event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Se este não for uma operação de saldo else { //--- Entrada no mercado if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN) { trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; int reason=EVENT_REASON_DONE; //--- Busca por todas os negócios das posições na direção de sua abertura e conta o seu volume total double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID()); //--- Pega as primeiras e últimas ordens da posição da lista de todas as ordens da posição ulong order_ticket=order.GetProperty(ORDER_PROP_DEAL_ORDER); COrder* order_first=this.GetOrderByTicket(list_history,order_ticket); COrder* order_last=this.GetLastOrderFromList(list_history,order.PositionID()); //--- Se não houver a última ordem, a primeira e a última ordem da posição se coincidem if(order_last==NULL) order_last=order_first; if(order_first!=NULL) { //--- Se o volume da ordem for aberto parcialmente, isso é uma execução parcial if(this.SummaryVolumeDealsInByPosID(list_history,order.PositionID())<order_first.Volume()) { trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; reason=EVENT_REASON_DONE_PARTIALLY; } //--- Se uma ordem de abertura é uma pendente, a ordem pendente é ativada if(order_first.TypeOrder()>ORDER_TYPE_SELL && order_first.TypeOrder()<ORDER_TYPE_CLOSE_BY) { trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- Se uma ordem for executada parcialmente, define a execução parcial da ordem como um motivo do evento reason= (this.SummaryVolumeDealsInByPosID(list_history,order.PositionID())<order_first.Volume() ? EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : EVENT_REASON_ACTIVATED_PENDING ); } CEvent* event=new CEventPositionOpen(trade_event_code,order.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Horário do evento (horário de abertura de posição) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo do negócio do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Tipo de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Tipo de uma ordem que acionou um evento de negócio (a ordem da última posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem/negócio/posição event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Horário de uma ordem, com base em qual negócio da posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço do evento (preço de abertura da posição) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Preço de abertura da ordem (preço de abertura da posição) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Preço de encerramento da ordem (preço de encerramento da última ordem da posição) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // Preço do StopLoss (preço do StopLoss da posição) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // Preço do TakeProfit (preço do TakeProfit da posição) event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order_first.Volume()); // Volume solicitado event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,volume_in); // Volume executado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order_first.Volume()-volume_in); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Saída do mercado else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT) { trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE; //--- Pega as primeiras e últimas ordens da posição da lista de todas as ordens da posição COrder* order_first=this.GetFirstOrderFromList(list_history,order.PositionID()); COrder* order_last=this.GetLastOrderFromList(list_history,order.PositionID()); if(order_first!=NULL && order_last!=NULL) { //--- Busca por todas os negócios da posição na direção de sua abertura e fechamento e conta o seu volume total double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,order.PositionID()); //--- Calcula o volume atual da posição encerrada int dgl=(int)DigitsLots(order.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Se o volume da ordem for encerrada parcialmente, isso é uma execução parcial if(volume_current>0) { trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- Se a ordem de encerramento for executada parcialmente, define a execução parcial da ordem de encerramento como um motivo do evento if(order_last.VolumeCurrent()>0) { reason=EVENT_REASON_DONE_PARTIALLY; } //--- Se a flag de encerramento estiver definido como StopLoss para a ordem de encerramento de uma posição, o encerramento será executado pelo StopLoss //--- Se uma ordem de StopLoss for executada parcialmente, define a execução parcial da ordem StopLoss como a razão do evento if(order_last.IsCloseByStopLoss()) { trade_event_code+=TRADE_EVENT_FLAG_SL; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL); } //--- Se a flag de encerramento estiver definido como TakeProfit para a ordem de encerramento de uma posição, o encerramento será executado pelo TakeProfit //--- Se uma ordem de TakeProfit for executada parcialmente, define a execução parcial da ordem TakeProfit como a razão do evento else if(order_last.IsCloseByTakeProfit()) { trade_event_code+=TRADE_EVENT_FLAG_TP; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP); } //--- CEvent* event=new CEventPositionClose(trade_event_code,order.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Horário do evento (horário de encerramento da posição) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo do negócio do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Tipo de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Tipo de uma ordem que acionou um evento de negócio (a ordem da última posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem/negócio/posição event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Horário de uma ordem, com base em qual negócio da posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço do evento (preço de encerramento da posição) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Preço de abertura da ordem (preço de abertura da posição) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Preço de encerramento da ordem (preço de encerramento da última ordem da posição) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // Preço do StopLoss (preço do StopLoss da posição) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // Preço do TakeProfit (preço do TakeProfit da posição) event.SetProperty(EVENT_PROP_VOLUME_INITIAL,volume_in); // Volume inicial event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Volume encerrado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,volume_in-volume_out); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Posição oposta else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT_BY) { trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE_BY_POS; //--- Pega as primeiras e últimas ordens da posição da lista de todas as ordens da posição COrder* order_first=this.GetFirstOrderFromList(list_history,order.PositionID()); COrder* order_close=this.GetCloseByOrderFromList(list_history,order.PositionID()); if(order_first!=NULL && order_close!=NULL) { //--- Adiciona a flag para encerrar por uma posição oposta trade_event_code+=TRADE_EVENT_FLAG_BY_POS; //--- Busca por todas os negócios da posição na direção de sua abertura e fechamento e conta o seu volume total double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,order.PositionID());//+order_close.Volume(); //--- Calcula o volume atual da posição encerrada int dgl=(int)DigitsLots(order.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Busca por todos os negócios da posição oposta na direção de sua abertura e fechamento e conta o seu volume total double volume_opp_in=this.SummaryVolumeDealsInByPosID(list_history,order_close.PositionByID()); double volume_opp_out=this.SummaryVolumeDealsOutByPosID(list_history,order_close.PositionByID());//+order_close.Volume(); //--- Calcula o volume atual da posição oposta double volume_opp_current=::NormalizeDouble(volume_opp_in-volume_opp_out,dgl); //--- Se o volume da posição encerrada é encerrada parcialmente, isso é uma execução parcial if(volume_current>0 || order_close.VolumeCurrent()>0) { //--- Adiciona a flag de execução parcial trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- Se o volume da posição oposta é encerrada parcialmente, isso é uma execução parcial pela parte do volume da posição oposta reason=(volume_opp_current>0 ? EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY : EVENT_REASON_DONE_PARTIALLY_BY_POS); } //--- Se o volume da posição oposta é encerrada completamente, isso é um encerramento pela parte do volume da posição oposta else { if(volume_opp_current>0) { reason=EVENT_REASON_DONE_BY_POS_PARTIALLY; } } CEvent* event=new CEventPositionClose(trade_event_code,order.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Horário do evento event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Motivo do evento (da enumeração ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Tipo do negócio do evento event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket do negócio do evento event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_close.TypeOrder()); // Tipo de uma ordem que acionou um evento de negócio (a ordem da última posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_close.Ticket()); // Ticket de uma ordem, com base em qual evento de negociação está aberto (a ordem da última posição) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Horário de uma ordem, com base em qual negócio da posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Tipo de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket de uma ordem, com base em qual negócio de posição está aberto (a ordem da primeira posição) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // ID da posição event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_close.PositionByID()); // ID da posição oposta event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magic number da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preço do evento event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Preço de abertura da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Preço de encerramento da ordem/negócio/posição event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // Preço do StopLoss (preço do StopLoss da posição) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // Preço do TakeProfit (preço do TakeProfit da posição) event.SetProperty(EVENT_PROP_VOLUME_INITIAL,::NormalizeDouble(volume_in,dgl));// Volume inicial event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Volume encerrado event.SetProperty(EVENT_PROP_VOLUME_CURRENT,volume_current); // Volume restante (não executado) event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull()); // Lucro event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Símbolo da ordem //--- Define o ID do gráfico do programa de controle, decodifica o código do evento e define o tipo do evento event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Adiciona o objeto de evento se ele não estiver na lista if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Envia uma mensagem sobre o evento e define o valor do último evento de negociação event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Se o evento já estiver presente na lista, remove um novo objeto de evento e exibe uma mensagem de depuração else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Reversão else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_INOUT) { //--- Reversão da posição Print(DFUN,"Position reversal"); order.Print(); } } } } //+------------------------------------------------------------------+
O método acabou por ser bastante extenso. Portanto, todas as descrições das verificações necessárias e ações correspondentes são fornecidas diretamente na listagem.
O método verifica o estado de uma ordem passada e todos os componentes necessários do evento ocorrido, dependendo de seu tipo (ordem pendente colocada, ordem pendente removida, negócio). Um novo evento é criado e preenchido com os dados correspondentes à ordem e ao tipo de evento, enquanto o evento é colocado na coleção de eventos e, finalmente, uma mensagem sobre esse evento é enviada ao gráfico do programa de controle e a variável que armazena o tipo do último evento que ocorreu é preenchida.
A classe de coleção de eventos está pronta. Agora nós precisamos incluí-lo no objeto base da biblioteca.
Depois de criar a classe de coleção de eventos, algumas coisas que nós fizemos na quarta parte da classe de objeto base CEngine para monitorar os eventos são redundantes, portanto, o objeto base deve ser revisado.
- Remova a variável privada m_trade_event_code da classe armazenando o código de estado do evento de negociação.
- Remova os métodos privados:
- método SetTradeEvent() para decodificar o código do evento,
- método IsTradeEventFlag() retornando a presença da flag em um evento de negociação,
- os métodos WorkWithHedgeCollections() e WorkWithNettoCollections() para trabalhar com as coleções de hedging e netting
- e o método TradeEventCode() para retornar o código de evento de negociação
Adicione a inclusão do arquivo da classe de coleção de eventos de negociação
para o corpo da classe, declare o objeto da coleção de eventos,
adicione
o método TradeEventsControl() para trabalhar com os eventos
para a seção privada da classe, altere o nome do método GetListHistoryDeals() para GetListDeals()
na seção pública. Os negócios estão sempre localizadas na coleção do histórico, portanto, não é necessário mencionar explicitamente a
coleção no nome do método. Vamos mudar a implementação do método para redefinir o último evento de negociação: já que agora nós
recebemos o último evento da classe de coleção de eventos e o método de redefinir o último evento está presente dentro da classe, nós
simplesmente precisamos chamar o método de mesmo nome da classe de coleção do evento no método
ResetLastTradeEvent() da classe.
//+------------------------------------------------------------------+ //| Engine.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Services\TimerCounter.mqh" //+------------------------------------------------------------------+ //| Classe base da Biblioteca | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Coleção do histórico de ordens e negócios CMarketCollection m_market; // Coleção de ordens à mercado e negócios CEventsCollection m_events; // Colecão de eventos CArrayObj m_list_counters; // Lista dos contadores do timer bool m_first_start; // Flag da primeira execução bool m_is_hedge; // Flag da conta hedge bool m_is_market_trade_event; // Flag do evento de negociação da conta bool m_is_history_trade_event; // Flag do evento do histórico de negociação ENUM_TRADE_EVENT m_acc_trade_event; // Evento de negociação da conta //--- Retorna o índice do contador por id int CounterIndex(const int id) const; //--- Retorna (1) a primeira flag de ativação, (2) a presença da flag em um evento de negociação bool IsFirstStart(void); //--- Trabalhando com eventos void TradeEventsControl(void); //--- Retorna a última (1) ordem pendente, (2) ordem à mercado, (3) última posição, (4) posição pelo ticket COrder* GetLastMarketPending(void); COrder* GetLastMarketOrder(void); COrder* GetLastPosition(void); COrder* GetPosition(const ulong ticket); //--- Retorna a última (1) ordem pendente removida, (2) ordem do histórico, (3) ordem do histórico pelo seu ticket (ordem à mercado ou pendente) COrder* GetLastHistoryPending(void); COrder* GetLastHistoryOrder(void); COrder* GetHistoryOrder(const ulong ticket); //--- Retorna a (1) primeira e a (2) última ordem do histórico da lista de todos as ordens de posição, (3) o último negócio COrder* GetFirstOrderPosition(const ulong position_id); COrder* GetLastOrderPosition(const ulong position_id); COrder* GetLastDeal(void); public: //--- Retorna a lista de (1) posições, (2) ordens pendentes e (3) ordens à mercado CArrayObj* GetListMarketPosition(void); CArrayObj* GetListMarketPendings(void); CArrayObj* GetListMarketOrders(void); //--- Retorna a lista do histórico de (1) ordens, (2) ordens pendentes removidas, (3) negócios, (4) todas as ordens pelo id da posição CArrayObj* GetListHistoryOrders(void); CArrayObj* GetListHistoryPendings(void); CArrayObj* GetListDeals(void); CArrayObj* GetListAllOrdersByPosID(const ulong position_id); //--- Redefine o último evento de negociação void ResetLastTradeEvent(void) { this.m_events.ResetLastTradeEvent(); } //--- Retorna o (1) código do evento de negociação e (2) a flag da conta hedge ENUM_TRADE_EVENT LastTradeEvent(void) const { return this.m_acc_trade_event; } bool IsHedge(void) const { return this.m_is_hedge; } //--- Cria o timer da conta void CreateCounter(const int id,const ulong frequency,const ulong pause); //--- Timer void OnTimer(void); //--- Construtor/destrutor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
No construtor da classe CEngine, adicione a manipulação do resultado do desenvolvimento do timer em milissegundos. Se ele não for criado,
exiba uma mensagem apropriada no diário. Em seguida, nós vamos desenvolver a classe para manipular certos erros, definir as flags visíveis
por um programa baseado na biblioteca e processar as situações de erro.
//+------------------------------------------------------------------+ //| Construtor CEngine | //+------------------------------------------------------------------+ 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=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); } //+------------------------------------------------------------------+
No timer da classe, chame o método TradeEventsControl()
após o timer da coleção de ordens, negócios e posições "despausar".
//+------------------------------------------------------------------+ //| Timer da CEngine | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer das coleções do histórico de ordens, negócios, ordens de mercado e posições int index=this.CounterIndex(COLLECTION_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); //--- Se "despausado", trabalha com os eventos de coleções if(counter!=NULL && counter.IsTimeDone()) { this.TradeEventsControl(); } } } //+------------------------------------------------------------------+
Vamos melhorar o método retornando uma ordem do histórico pelo ticket. Como a lista de coleções do histórico pode conter ordens pendentes, ordens à mercado ativadas e ordens agindo como encerradas ao ser encerrada por uma posição oposta, nós precisamos considerar todos os tipos de ordens.
Para fazer isso, primeiro, busque por uma ordem pelo ticket na lista de ordens de
mercado e de encerramento. Se a lista estiver vazia, busque por uma ordem pendente removida com o mesmo ticket. Se a lista não contiver
a ordem, será retornado NULL. Caso contrário, o programa retorna o primeiro elemento da lista onde a ordem foi encontrada. Se não conseguir
receber a ordem da lista, será retornado NULL.
//+------------------------------------------------------------------+ //| Retorna a ordem do histórico pelo seu ticket | //+------------------------------------------------------------------+ COrder* CEngine::GetHistoryOrder(const ulong ticket) { CArrayObj* list=this.GetListHistoryOrders(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL || list.Total()==0) { list=this.GetListHistoryPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL) return NULL; } COrder* order=list.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Vamos implementar o método TradeEventsControl() para trabalhar com eventos da conta:
//+------------------------------------------------------------------+ //| Verifica os eventos de negociação | //+------------------------------------------------------------------+ void CEngine::TradeEventsControl(void) { //--- Inicializa o código e as flags dos eventos de negociação this.m_is_market_trade_event=false; this.m_is_history_trade_event=false; //--- Atualiza as listas this.m_market.Refresh(); this.m_history.Refresh(); //--- Ações durante a primeira execução if(this.IsFirstStart()) { this.m_acc_trade_event=TRADE_EVENT_NO_EVENT; return; } //--- Verifica as alterações no estado do mercado e no histórico da conta this.m_is_market_trade_event=this.m_market.IsTradeEvent(); this.m_is_history_trade_event=this.m_history.IsTradeEvent(); //--- Em caso de qualquer evento, envia as listas, as flags e o número de novas ordens e negócios para a coleção de eventos e atualize ela if(this.m_is_history_trade_event || this.m_is_market_trade_event) { this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(), this.m_is_history_trade_event,this.m_is_market_trade_event, this.m_history.NewOrders(),this.m_market.NewPendingOrders(), this.m_market.NewMarketOrders(),this.m_history.NewDeals()); //--- Obtém o último evento de negociação da conta this.m_acc_trade_event=this.m_events.GetLastTradeEvent(); } }
Esse método é muito menor em comparação com o seu predecessor WorkWithHedgeCollections() da quarta parte da descrição da biblioteca.
O método é simples e não requer explicações. O código contém todos os comentários, permitindo que você entenda sua lógica simples.
Aqui está uma lista completa da classe CEngine atualizada:
//+------------------------------------------------------------------+ //| Engine.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" //+------------------------------------------------------------------+ //| Arquivos de inclusão | //+------------------------------------------------------------------+ #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Services\TimerCounter.mqh" //+------------------------------------------------------------------+ //| Classe base da Biblioteca | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Coleção do histórico de ordens e negócios CMarketCollection m_market; // Coleção de ordens à mercado e negócios CEventsCollection m_events; // Coleção de eventos CArrayObj m_list_counters; // Lista dos contadores do timer bool m_first_start; // Flag da primeira execução bool m_is_hedge; // Flag da conta hedge bool m_is_market_trade_event; // Flag do evento de negociação da conta bool m_is_history_trade_event; // Flag do evento do histórico de negociação ENUM_TRADE_EVENT m_acc_trade_event; // Evento de negociação da conta //--- Retorna o índice do contador por id int CounterIndex(const int id) const; //--- Retorna (1) a primeira flag de ativação, (2) a presença da flag em um evento de negociação bool IsFirstStart(void); //--- Trabalhando com eventos void TradeEventsControl(void); //--- Retorna a última (1) ordem pendente, (2) ordem à mercado, (3) última posição, (4) posição pelo ticket COrder* GetLastMarketPending(void); COrder* GetLastMarketOrder(void); COrder* GetLastPosition(void); COrder* GetPosition(const ulong ticket); //--- Retorna a última (1) ordem pendente removida, (2) ordem do histórico, (3) ordem do histórico pelo seu ticket (ordem à mercado ou pendente) COrder* GetLastHistoryPending(void); COrder* GetLastHistoryOrder(void); COrder* GetHistoryOrder(const ulong ticket); //--- Retorna a (1) primeira e a (2) última ordem do histórico da lista de todos as ordens de posição, (3) o último negócio COrder* GetFirstOrderPosition(const ulong position_id); COrder* GetLastOrderPosition(const ulong position_id); COrder* GetLastDeal(void); public: //--- Retorna a lista de (1) posições, (2) ordens pendentes e (3) ordens à mercado CArrayObj* GetListMarketPosition(void); CArrayObj* GetListMarketPendings(void); CArrayObj* GetListMarketOrders(void); //--- Retorna a lista do histórico de (1) ordens, (2) ordens pendentes removidas, (3) negócios, (4) todas as ordens pelo id da posição CArrayObj* GetListHistoryOrders(void); CArrayObj* GetListHistoryPendings(void); CArrayObj* GetListDeals(void); CArrayObj* GetListAllOrdersByPosID(const ulong position_id); //--- Redefine o último evento de negociação void ResetLastTradeEvent(void) { this.m_events.ResetLastTradeEvent(); } //--- Retorna o (1) código do evento de negociação e (2) a flag da conta hedge ENUM_TRADE_EVENT LastTradeEvent(void) const { return this.m_acc_trade_event; } bool IsHedge(void) const { return this.m_is_hedge; } //--- Cria o timer da conta void CreateCounter(const int id,const ulong frequency,const ulong pause); //--- Timer void OnTimer(void); //--- Construtor/destrutor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+ //| Construtor CEngine | //+------------------------------------------------------------------+ 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=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); } //+------------------------------------------------------------------+ //| Destrutor CEngine | //+------------------------------------------------------------------+ CEngine::~CEngine() { ::EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer da CEngine | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer das coleções do histórico de ordens, negócios, ordens de mercado e posições int index=this.CounterIndex(COLLECTION_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); //--- Se "despausado", trabalha com os eventos de coleções if(counter!=NULL && counter.IsTimeDone()) { this.TradeEventsControl(); } } } //+------------------------------------------------------------------+ //| Cria o contador do timer | //+------------------------------------------------------------------+ void CEngine::CreateCounter(const int id,const ulong step,const ulong pause) { if(this.CounterIndex(id)>WRONG_VALUE) { ::Print(TextByLanguage("Ошибка. Уже создан счётчик с идентификатором ","Error. Already created counter with id "),(string)id); return; } m_list_counters.Sort(); CTimerCounter* counter=new CTimerCounter(id); if(counter==NULL) ::Print(TextByLanguage("Не удалось создать счётчик таймера ","Failed to create timer counter "),(string)id); counter.SetParams(step,pause); if(this.m_list_counters.Search(counter)==WRONG_VALUE) this.m_list_counters.Add(counter); else { string t1=TextByLanguage("Ошибка. Счётчик с идентификатором ","Error. Counter with ID ")+(string)id; string t2=TextByLanguage(", шагом ",", step ")+(string)step; string t3=TextByLanguage(" и паузой "," and pause ")+(string)pause; ::Print(t1+t2+t3+TextByLanguage(" уже существует"," already exists")); delete counter; } } //+------------------------------------------------------------------+ //| Retorna o índice do contador na lista pelo id | //+------------------------------------------------------------------+ int CEngine::CounterIndex(const int id) const { int total=this.m_list_counters.Total(); for(int i=0;i<total;i++) { CTimerCounter* counter=this.m_list_counters.At(i); if(counter==NULL) continue; if(counter.Type()==id) return i; } return WRONG_VALUE; } //+------------------------------------------------------------------+ //| Retorna a flag da primeira execução, redefine o sinalizador | //+------------------------------------------------------------------+ bool CEngine::IsFirstStart(void) { if(this.m_first_start) { this.m_first_start=false; return true; } return false; } //+------------------------------------------------------------------+ //| Verifica os eventos de negociação | //+------------------------------------------------------------------+ void CEngine::TradeEventsControl(void) { //--- Inicialize o código e as flags do evento de negociação this.m_is_market_trade_event=false; this.m_is_history_trade_event=false; //--- Atualiza as listas this.m_market.Refresh(); this.m_history.Refresh(); //--- Ações durante a primeira execução if(this.IsFirstStart()) { this.m_acc_trade_event=TRADE_EVENT_NO_EVENT; return; } //--- Verifica as alterações no estado de mercado e no histórico da conta this.m_is_market_trade_event=this.m_market.IsTradeEvent(); this.m_is_history_trade_event=this.m_history.IsTradeEvent(); //--- Em caso de qualquer evento, envia as listas, as flags e o número de novas ordens e negócios para a coleção de eventos e atualize ela if(this.m_is_history_trade_event || this.m_is_market_trade_event) { this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(), this.m_is_history_trade_event,this.m_is_market_trade_event, this.m_history.NewOrders(),this.m_market.NewPendingOrders(), this.m_market.NewMarketOrders(),this.m_history.NewDeals()); //--- Obtém o último evento de negociação da conta this.m_acc_trade_event=this.m_events.GetLastTradeEvent(); } } //+------------------------------------------------------------------+ //| Retorna a lista de posições | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListMarketPosition(void) { CArrayObj* list=this.m_market.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_POSITION,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a lista de ordens pendentes | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListMarketPendings(void) { CArrayObj* list=this.m_market.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a lista de ordens à mercado | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListMarketOrders(void) { CArrayObj* list=this.m_market.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_ORDER,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a lista de ordens do histórico | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListHistoryOrders(void) { CArrayObj* list=this.m_history.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_ORDER,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a lista de ordens pendentes removidas | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListHistoryPendings(void) { CArrayObj* list=this.m_history.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a lista de negócios | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListDeals(void) { CArrayObj* list=this.m_history.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a lista de todas as ordens da posição | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListAllOrdersByPosID(const ulong position_id) { CArrayObj* list=this.GetListHistoryOrders(); list=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL); return list; } //+------------------------------------------------------------------+ //| Retorna a última posição | //+------------------------------------------------------------------+ COrder* CEngine::GetLastPosition(void) { CArrayObj* list=this.GetListMarketPosition(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a posição pelo ticket | //+------------------------------------------------------------------+ COrder* CEngine::GetPosition(const ulong ticket) { CArrayObj* list=this.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TICKET); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna o último negócio | //+------------------------------------------------------------------+ COrder* CEngine::GetLastDeal(void) { CArrayObj* list=this.GetListDeals(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a última ordem pendente | //+------------------------------------------------------------------+ COrder* CEngine::GetLastMarketPending(void) { CArrayObj* list=this.GetListMarketPendings(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a última ordem pendente do histórico | //+------------------------------------------------------------------+ COrder* CEngine::GetLastHistoryPending(void) { CArrayObj* list=this.GetListHistoryPendings(); if(list==NULL) return NULL; list.Sort(#ifdef __MQL5__ SORT_BY_ORDER_TIME_OPEN_MSC #else SORT_BY_ORDER_TIME_CLOSE_MSC #endif); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a última ordem à mercado | //+------------------------------------------------------------------+ COrder* CEngine::GetLastMarketOrder(void) { CArrayObj* list=this.GetListMarketOrders(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a última ordem à mercado do histórico | //+------------------------------------------------------------------+ COrder* CEngine::GetLastHistoryOrder(void) { CArrayObj* list=this.GetListHistoryOrders(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a ordem do histórico pelo seu ticket | //+------------------------------------------------------------------+ COrder* CEngine::GetHistoryOrder(const ulong ticket) { CArrayObj* list=this.GetListHistoryOrders(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL || list.Total()==0) { list=this.GetListHistoryPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL) return NULL; } COrder* order=list.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a primeira ordem à mercado do histórico | //| da lista de todas as ordens da posição | //+------------------------------------------------------------------+ COrder* CEngine::GetFirstOrderPosition(const ulong position_id) { CArrayObj* list=this.GetListAllOrdersByPosID(position_id); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN); COrder* order=list.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Retorna a última ordem à mercado do histórico | //| da lista de todas as ordens da posição | //+------------------------------------------------------------------+ COrder* CEngine::GetLastOrderPosition(const ulong position_id) { CArrayObj* list=this.GetListAllOrdersByPosID(position_id); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Testando os processos de definição, manipulação e recebimento de eventos
Agora nós estamos prontos para trabalhar com os eventos. É hora de preparar o EA para testar e lidar com as descrições de eventos e enviá-las para o programa de controle.
Na pasta do terminal \MQL5\Experts\TestDoEasy, crie a pasta Part05 e copie o EA TestDoEasyPart04.mq5 da parte anterior para um novo nome: TestDoEasyPart05.mq5
Altere o seu manipulador de eventos OnChartEvent() para receber os eventos personalizados:
//+------------------------------------------------------------------+ //| Função ChartEvent | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(MQLInfoInteger(MQL_TESTER)) return; if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0) { PressButtonEvents(sparam); } if(id>=CHARTEVENT_CUSTOM) { ushort event=ushort(id-CHARTEVENT_CUSTOM); Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam); } } //+------------------------------------------------------------------+
Aqui, se o ID do evento exceder ou for igual ao ID do evento personalizado,
recebemos o código do evento passado da biblioteca pelos
descendentes da classe CEvent. Ao enviar um evento personalizado pela função
EventChartCustom() especificada no parâmetro da
função
custom_event_id (aquele em que nós escrevemos nosso evento), o valor da constante CHARTEVENT_CUSTOM (igual a 1000) da enumeração ENUM_CHART_EVENT
é adicionada ao valor do evento. Portanto, para recuperar o valor do evento, basta subtrair o valor CHARTEVENT_CUSTOM do ID do evento. Depois
disso, nós exibimos os dados do evento no diário do terminal.
Os seguintes dados são exibidos: o ID, descrição do evento na forma do valor da enumeração ENUM_TRADE_EVENT, valor lparam armazenando a
ordem ou o ticket da posição, o valor dparam armazenando o preço do evento e o valor sparam — símbolo de uma ordem ou uma posição participando do
evento ou nome da moeda da conta, caso o evento seja uma operação de saldo.
Por exemplo:
2019.04.06 03:19:54.442 OnChartEvent: id=1001, event=TRADE_EVENT_PENDING_ORDER_PLASED, lparam=375419507, dparam=1.14562, sparam=EURUSD
Além disso, nós precisamos corrigir o lote calculado para o encerramento parcial. Ele estava incorreto nas versões anteriores dos EAs de teste, uma vez que o valor do volume da posição não executado (VolumeCurrent()) foi usado para o cálculo do lote. Ele é sempre igual a zero no testador ao abrir uma posição, pois o testador não simula aberturas parciais. Assim, o valor mínimo do lote foi tomado para o encerramento, uma vez que a função de cálculo do lote sempre ajustou para zero o valor do lote menos aceitável.
Vamos encontrar as strings onde o lote para encerramento parcial é calculado e substituir VolumeCurrent() por Volume():
//--- Calcula o volume de encerramento e encerra a metade da posição de Compra pelo ticket trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); //--- Calcula o volume de encerramento e encerra a metade da posição de Venda pelo ticket trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
Apenas dois lugares no código — encerrando a metade da posição de Compra e encerrando a metade da posição de Venda.
Além disso, adicione o deslocamento do botão pelos eixos X e Y às entradas do EA para a localização mais conveniente dos conjuntos de botões no gráfico do testador visual (eu desloquei os botões para a direita para ver os tickets da ordem e posição no visualizador, pois eles poderiam estar ocultos pelos botões):
//--- variáveis de entrada input ulong InpMagic = 123; // Número mágico input double InpLots = 0.1; // Lotes input uint InpStopLoss = 50; // StopLoss em pontos input uint InpTakeProfit = 50; // TakeProfit em pontos input uint InpDistance = 50; // Distância das ordens pendentes (pontos) input uint InpDistanceSL = 50; // Distância das ordens StopLimit (pontos) input uint InpSlippage = 0; // Desvio em pontos input double InpWithdrawal = 10; // Retirada de fundos (no testador) input uint InpButtShiftX = 40; // Deslocamento X dos botões input uint InpButtShiftY = 10; // Deslocamento Y dos botões //--- variáveis globais
Vamos mudar um pouco o código da função de criação do botão:
//+------------------------------------------------------------------+ //| Cria o painel de botões | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=30,const int shift_y=0) { int h=18,w=84,offset=2; int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-3) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+
e implementar a chamada da função no manipulador OnInit() do EA:
//--- cria os botões if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- configurando os parâmetros de negociação
O código completo do EA de teste é fornecido abaixo:
//+------------------------------------------------------------------+ //| TestDoEasyPart05.mq5 | //| 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\Engine.mqh> #include <Trade\Trade.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL }; #define TOTAL_BUTT (17) //--- estruturas struct SDataButt { string name; string text; }; //--- variáveis de entrada input ulong InpMagic = 123; // Número mágico input double InpLots = 0.1; // Lotes input uint InpStopLoss = 50; // StopLoss em pontos input uint InpTakeProfit = 50; // TakeProfit em pontos input uint InpDistance = 50; // Distância das ordens pendentes (pontos) input uint InpDistanceSL = 50; // Distância das ordens StopLimit (pontos) input uint InpSlippage = 0; // Desvio em pontos input double InpWithdrawal = 10; // Retirada de fundos (no testador) input uint InpButtShiftX = 40; // Deslocamento X dos botões input uint InpButtShiftY = 10; // Deslocamento Y dos botões //--- variáveis globais CEngine engine; CTrade trade; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; //+------------------------------------------------------------------+ //| Função de inicialização do Expert | //+------------------------------------------------------------------+ int OnInit() { //--- Verifica o tipo da conta if(!engine.IsHedge()) { Alert(TextByLanguage("Ошибка. Счёт должен быть хеджевым","Error. Account must be hedge")); return INIT_FAILED; } //--- define as variáveis globais prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; //--- cria os botões if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- configurando os parâmetros de negociação trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Função de desinicialização do Expert | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- remove os objetos ObjectsDeleteAll(0,prefix); Comment(""); } //+------------------------------------------------------------------+ //| Função Tick do Expert | //+------------------------------------------------------------------+ void OnTick() { //--- static ENUM_TRADE_EVENT last_event=WRONG_VALUE; if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); int total=ObjectsTotal(0); for(int i=0;i<total;i++) { string obj_name=ObjectName(0,i); if(StringFind(obj_name,prefix+"BUTT_")<0) continue; PressButtonEvents(obj_name); } } if(engine.LastTradeEvent()!=last_event) { Comment("\nLast trade event: ",EnumToString(engine.LastTradeEvent())); last_event=engine.LastTradeEvent(); } } //+------------------------------------------------------------------+ //| Funçao timer | //+------------------------------------------------------------------+ void OnTimer() { if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); } //+------------------------------------------------------------------+ //| Função ChartEvent | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(MQLInfoInteger(MQL_TESTER)) return; if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0) { PressButtonEvents(sparam); } if(id>=CHARTEVENT_CUSTOM) { ushort event=ushort(id-CHARTEVENT_CUSTOM); Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam); } } //+------------------------------------------------------------------+ //| Cria o painel de botões | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=30,const int shift_y=0) { int h=18,w=84,offset=2; int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-3) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+ //| Cria o botão | //+------------------------------------------------------------------+ bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8) { if(ObjectFind(0,name)<0) { if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) { Print(DFUN,TextByLanguage("не удалось создать кнопку! Код ошибки=","Could not create button! Error code="),GetLastError()); return false; } ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n"); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray); return true; } return false; } //+------------------------------------------------------------------+ //| Retorna o estado do botão | //+------------------------------------------------------------------+ bool ButtonState(const string name) { return (bool)ObjectGetInteger(0,name,OBJPROP_STATE); } //+------------------------------------------------------------------+ //| Define o estado do botão | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); } //+------------------------------------------------------------------+ //| Transforma a enumeração em texto do botão | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); StringReplace(txt,"buy","Buy"); StringReplace(txt,"sell","Sell"); StringReplace(txt,"_limit"," Limit"); StringReplace(txt,"_stop"," Stop"); StringReplace(txt,"close_","Close "); StringReplace(txt,"2"," 1/2"); StringReplace(txt,"_by_"," by "); StringReplace(txt,"profit_","Profit "); StringReplace(txt,"delete_","Delete "); return txt; } //+------------------------------------------------------------------+ //| Manipa os pressionamentos de botões | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { //--- Converte o nome do botão em seu ID de string string button=StringSubstr(button_name,StringLen(prefix)); //--- Se o botão foi pressionado if(ButtonState(button_name)) { //--- Se o botão BUTT_BUY for pressionado: Abre uma posição de Compra if(button==EnumToString(BUTT_BUY)) { //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- Abre uma posição de Compra trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp); } //--- Se o botão BUTT_BUY_LIMIT for pressionado: Coloca a BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Obtém o preço correto de posicionamento da ordem em relação ao StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao nível de colocação da ordem, considerando o StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- Define a ordem BuyLimit trade.BuyLimit(lot,price_set,Symbol(),sl,tp); } //--- Se o botão BUTT_BUY_STOP for pressionado: Coloca a BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Obtém o preço correto de posicionamento da ordem em relação ao StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao nível de colocação da ordem, considerando o StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- Define a ordem BuyStop trade.BuyStop(lot,price_set,Symbol(),sl,tp); } //--- Se o botão BUTT_BUY_STOP_LIMIT for pressionado: Coloca a BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Obtém o preço correto do BuyStop relativo ao StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Calcula o preço da ordem BuyLimit relativo ao nível de colocação da BuyStop considerando o StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao nível de colocação da ordem, considerando o StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- Define a ordem BuyStopLimit trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- Se o botão BUTT_SELL for pressionado: Abre uma posição de Venda else if(button==EnumToString(BUTT_SELL)) { //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- Abre uma posição de Venda trade.Sell(lot,Symbol(),0,sl,tp); } //--- Se o botão BUTT_SELL_LIMIT for pressionado: Coloca uma SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Obtém o preço correto de posicionamento da ordem em relação ao StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao nível de colocação da ordem, considerando o StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- Coloca a ordem SellLimit trade.SellLimit(lot,price_set,Symbol(),sl,tp); } //--- Se o botão BUTT_SELL_STOP for pressionado: Coloca a SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Obtém o preço correto de posicionamento da ordem em relação ao StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao nível de colocação da ordem, considerando o StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- Coloca a ordem SellStop trade.SellStop(lot,price_set,Symbol(),sl,tp); } //--- Se o botão BUTT_SELL_STOP_LIMIT for pressionado: Coloca a SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Obtém o preço correto da ordem SellStop em relação ao StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Calcula o preço da ordem SellLimit relativo ao nível de SellStop considerando o StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- Obtém os preços corretos de StopLoss e TakeProfit em relação ao nível de colocação da ordem, considerando o StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- Coloca a ordem SellStopLimit trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- Se o botão BUTT_CLOSE_BUY for pressionado: Encerra a Compra com o lucro máximo else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de compra da lista list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Compra com o lucro máximo int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Obtém o ticket da posição de Compra e encerra a posição pelo ticket trade.PositionClose(position.Ticket()); } } } //--- Se o botão BUTT_CLOSE_BUY2 for pressionado: Encerra a metade da Compra com o lucro máximo else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de compra da lista list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Compra com o lucro máximo int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Calcula o volume de encerramento e encerra a metade da posição de Compra pelo ticket trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- Se o botão BUTT_CLOSE_BUY_BY_SELL for pressionado: Encerra a Compra com o lucro máximo pela Venda oposta com lucro máximo else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de compra da lista list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Compra com o lucro máximo int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Obtém a lista de todas as posições em aberto CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de Venda da lista list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Venda com o lucro máximo int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Seleciona a posição de Compra com o lucro máximo COrder* position_buy=list_buy.At(index_buy); //--- Seleciona a posição de Venda com o lucro máximo COrder* position_sell=list_sell.At(index_sell); if(position_buy!=NULL && position_sell!=NULL) { //--- Encerra a posição de Compra pela de Venda oposta trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket()); } } } //--- Se o botão BUTT_CLOSE_SELL for pressionado: Encerra a Venda com o lucro máximo else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de Venda da lista list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Venda com o lucro máximo int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Obtém o ticket de venda e encerra a posição pelo ticket trade.PositionClose(position.Ticket()); } } } //--- Se o botão BUTT_CLOSE_SELL2 for pressionado: Encerra metade da Venda com o lucro máximo else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de Venda da lista list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Venda com o lucro máximo int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Calcula o volume de encerramento e encerra a metade da posição de Venda pelo ticket trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- Se o botão BUTT_CLOSE_SELL_BY_BUY for pressionado: Encerra a Venda com o lucro máximo pela Compra oposta com o lucro máximo else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de Venda da lista list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Venda com o lucro máximo int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Obtém a lista de todas as posições em aberto CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Seleciona apenas as posições de compra da lista list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Ordena a lista por lucro considerando a comissão e swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Obtém o índice da posição de Compra com o lucro máximo int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Seleciona a posição de Venda com o lucro máximo COrder* position_sell=list_sell.At(index_sell); //--- Seleciona a posição de Compra com o lucro máximo COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- Encerra a posição de Venda pela de Compra oposta trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket()); } } } //--- Se o BUTT_CLOSE_ALL for pressionado: Encerra todas as posições começando com aquela com o menor lucro else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Obtém a lista de todas as posições em aberto CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- Ordena a lista por lucro considerando a comissão e swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- No loop da posição com o menor lucro for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- encerra cada posição pelo seu ticket trade.PositionClose(position.Ticket()); } } } //--- Se o botão BUTT_DELETE_PENDING for pressionado: Remove a primeira ordem pendente else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Obtém a lista de todas as ordens CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Ordena a lista por horário de colocação list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); int total=list.Total(); //--- No loop da posição com a maior quantidade de tempo for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- remove a ordem pelo ticket trade.OrderDelete(order.Ticket()); } } } //--- Se o botão BUTT_PROFIT_WITHDRAWAL for pressionado: Retira os fundos da conta if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- Se o programa é iniciado no testador if(MQLInfoInteger(MQL_TESTER)) { //--- Emula a retirada de fundos TesterWithdrawal(withdrawal); } } //--- Aguarda 1/10 de segundo Sleep(100); //--- "Despressiona" o botão e redesenha o gráfico ButtonState(button_name,false); ChartRedraw(); } } //+------------------------------------------------------------------+
Agora nós podemos compilar o EA e lançá-lo no testador. Ao clicar nos botões, são exibidas no diário do testador duas linhas de mensagens curtas sobre os eventos da conta que ocorrem.
As entradas do manipulador de eventos do EA não são exibidas no diário, pois elas funcionam fora do testador. Se clicar nos botões do EA em uma conta demo, três linhas serão exibidas no diário do terminal: duas linhas do método para exibir as mensagens curtas da classe CEvent e outra — do manipulador OnChartEvent() do EA.
Abaixo está uma amostra exibindo uma mensagem no diário quando uma ordem pendente é colocada e removida:
- Pending order placed: 2019.04.05 23:19:55.248 - EURUSD 0.10 Sell Limit #375419507 at price 1.14562 OnChartEvent: id=1001, event=TRADE_EVENT_PENDING_ORDER_PLASED, lparam=375419507, dparam=1.14562, sparam=EURUSD - Pending order removed: 2019.04.05 23:19:55.248 - EURUSD 0.10 Sell Limit #375419507 at price 1.14562 OnChartEvent: id=1002, event=TRADE_EVENT_PENDING_ORDER_REMOVED, lparam=375419507, dparam=1.14562, sparam=EURUSD
Qual é o próximo?
No próximo artigo, nós vamos começar a adicionar a funcionalidade para trabalhar com as contas netting da MetaTrader 5.
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.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/6211
- 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