Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte I). Conceito, gerenciamento de dados e primeiros resultados

22 abril 2019, 08:50
Artyom Trishkin
0
567

Conteúdo

Introdução

Ao analisar um grande número de estratégias de negociação, pedidos de desenvolvimento de aplicativos para os terminais MetaTrader 5 e MetaTrader 4 e vários sites sobre scripts, indicadores e robôs para MetaTrader, eu cheguei à conclusão de que toda essa diversidade é baseada principalmente nas mesmas funções elementares, ações e valores que aparecem regularmente em diferentes programas.

De fato, a lógica de qualquer programa pode ser dividida em muitas ações idênticas. Os resultados dessas ações são usados para construir a lógica dos aplicativos. Isso foi repetidamente confirmado pela uniformidade das perguntas feitas nos fóruns de MQL4/MQL5. Diferentes usuários fazem essencialmente as mesmas perguntas sobre os algoritmos e tarefas que eles resolvem.

Considerando tudo isso, eu decidi desenvolver uma grande biblioteca com funções integradas para solicitar e obter os dados necessários. Para usar o conjunto de dados da biblioteca proposta, os usuários só precisam recorrer ao método de requisição e resposta para obter uma quantidade enorme de dados completamente diferentes (em diferentes combinações e parâmetros de classificação) que a biblioteca coleta e armazena em seu banco de dados.

Conceito

Qualquer tipo de dado pode ser representado como um conjunto de objetos com as mesmas propriedades.
Por exemplo, uma série temporal pode ser representada como uma longa lista ordenada, cada célula subsequente, que armazena um objeto contendo um conjunto de propriedades do mesmo tipo que todos os outros conjuntos pertencentes à objetos semelhantes dentro da série temporal. Os dados desse objeto são representados pela estrutura MqlRates:

Estrutura para armazenar as informações sobre preços, volumes e spread.

struct MqlRates 
  { 
   datetime time;         // hora de início do período 
   double   open;         // preço de abertura 
   double   high;         // máxima do preço no período 
   double   low;          // mínima do preço no período 
   double   close;        // preço de fechamento 
   long     tick_volume;  // volume tick
   int      spread;       // spread 
   long     real_volume;  // volume negociado 
  };

Um conjunto de dados de ticks também pode ser retratado como uma lista ordenada em que um tick representado pela estrutura MqlTick é um objeto com um conjunto fixo de propriedades:

Estrutura para armazenar os últimos preços pelo símbolo. A estrutura é projetada para obter os dados mais requisitados sobre o preço atual em tempo hábil.

struct MqlTick 
  { 
   datetime     time;          // Horário da última atualização do preço 
   double       bid;           // Preço de Bid atual 
   double       ask;           // Preço de Ask atual 
   double       last;          // Preço atual do último negócio (Último) 
   ulong        volume;        // Volume do último preço atual 
   long         time_msc;      // Horário do último preço atualizado em milissegundos
   uint         flags;         // Tipos do Tick 
   double       volume_real;   // Volume para o último preço atual com maior precisão 
  };

Quaisquer outros dados necessários para analisar e preparar a lógica do programa também são organizados como listas de objetos simples.

Todos os objetos em uma lista terão o mesmo tipo de dados específico para esse tipo de objeto, independentemente de se tratar de uma lista de ordens, negócios ou ordens pendentes. Para cada objeto específico, nós desenvolveremos uma classe com o mínimo necessário de funcionalidades para armazenar, classificar e exibir dados.

Estrutura de dados da biblioteca

Como já mencionado, a biblioteca deve consistir da lista de objetos e fornecer a capacidade de selecionar qualquer um dos itens da lista por qualquer critério personalizado ou propriedade suportada por esse objeto. A biblioteca coletará os dados necessários para o seu próprio armazenamento e manuseio. Nenhuma intervenção humana é necessária. Os usuários só aplicarão os resultados de suas consultas à biblioteca.

Eu vou descrever todas as etapas do desenvolvimento da biblioteca, iniciando com os tópicos mais simples e adicionando gradualmente novas funcionalidades e dados à biblioteca já existente. A biblioteca deve ser desenvolvida no modo "ao vivo". As alterações e adições necessárias devem ser implementadas em cada artigo. Eu acredito que este estilo de apresentação é mais útil, pois envolve os leitores no desenvolvimento.

A estrutura mínima dos dados da biblioteca é um conjunto de objetos diferentes para descrever as propriedades dos dados necessários, enquanto as coletas de dados são listas que armazenam os objetos correspondentes.

Nós vamos usar a classe do array dinâmico de ponteiros para instâncias da classe CObject e suas classes derivadas da coleção de dados da biblioteca padrão para organizar as listas. Já que os objetos da classe base da biblioteca padrão CObject são necessários para armazenar em tal lista, nós iremos simplesmente herdar cada classe de nossos objetos da classe base CObject.

Primeira implementação

Primeiro, nós vamos desenvolver a biblioteca para contas hedge do terminal MetaTrader 5. Após preparar a funcionalidade mínima, nós ajustaremos sua operação para o MetaTrader 4. Em seguida, após implementar o histórico da conta, as posições atuais no mercado e o processamento de ordens pendentes, nós vamos adicionar a capacidade de trabalhar com as contas hedge do MetaTrader 5. Finalmente, nós vamos preencher a biblioteca com uma variedade de funções.

Vamos começar com o histórico da conta. Muitas estratégias aplicam os resultados dos negócios anteriores de uma forma ou de outra, por exemplo, resultados de negócios anteriores, os métodos de fechamento (stop loss, take profit), preço etc. Além disso, os resultados do último dia podem ser usados como ponto de partida para o trabalho atual. Os tipos de estratégias de negociação são ilimitados e nossa tarefa é fornecer acesso rápido a toda a diversidade.

Primeiro, vamos definir a terminologia para trabalhar com as coleções do histórico de ordens, negócios, ordens à mercado e posições. O sistema de ordens do MetaTrader 4 é diferente do MetaTrader 5. Enquanto o MetaTrader 4 possui ordens à mercado e pendentes, no MetaTrader 5, uma ordem é basicamente uma solicitação de negociação (ordem à mercado) gerando um negócio, enquanto o negócio, por sua vez, gera uma posição. Além disso, existem as ordens pendentes. Em outras palavras, nós temos pelo menos três objetos — uma ordem, um negócio e uma posição. Para evitar ser distraído por nomes e definições, vamos chamar a classe para armazenar os dados das ordens, negócios e posições de Order, sendo uma simples classe abstrata. Além disso, tudo deve ser classificado por tipos (ordens, negócios, etc.) em nossa lista (coleção do histórico de ordens mencionados no início do artigo).

A classe abstrata Order deve conter uma ordem ou um ticket de negociação, bem como todos os dados sobre os parâmetros da ordem ou do negócio e seu estado. O estado mostra exatamente o que o objeto é — uma ordem, um negócio ou uma posição.

Na <pasta de dados>\MQL5\Include, crie a pasta DoEasy onde os arquivos da biblioteca serão armazenados. Nesta fase, nós precisaremos de mais duas pastas na pasta DoEasy: a pasta Objects armazenará as classes de objetos, enquanto a Collections conterá as coleções de dados (listas de objetos).
Para encontrar a pasta de dados do terminal, vá para Arquivo -> Abrir pasta de dados (Ctrl + Shift + D).

Agora nós podemos criar a primeira classe (classe abstrata Order). Crie uma nova classe na pasta Objects e nomeie-a como COrder (1). Certifique-se de definir a classe CObject (2) da biblioteca padrão como base. Nesse caso, nossa nova classe de objeto é herdada da classe CObject e pode ser colocada na lista de objetos CArrayObj da biblioteca padrão.


Depois de clicar em Concluir, o arquivo Order.mqh (3) aparece na pasta Objects do diretório da biblioteca. Por enquanto, este é apenas uma parte do trabalho da classe:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Ao tentar compilar o código, nós recebemos cinco erros. Eles indicam a ausência da classe CObject da qual a classe COrder é derivada. Inclua o arquivo de classe CObject no código e compile-o novamente. Agora tudo está bem.

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

O objeto da classe é criado em um loop por ordens, negócios ou posições quando a próxima ordem, negócio ou posição é selecionada. Para inicializar imediatamente os campos do objeto, nós criaremos um construtor privado, os objetos do tipo status e ticket são passados para a identificação subsequente. Mas primeiro, vamos colocar o novo arquivo de inclusão Defines.mqh na pasta raiz do projeto, que armazena todas as enumerações necessárias para a biblioteca, bem como substituições de macro, constantes e estruturas.

Atualmente, nós precisamos de enumerações descrevendo o estado da ordem e enumerações descrevendo todos os parâmetros das ordens, negócios ou posições. Haverá três enumerações com os parâmetros da ordem: inteiro, real e string.

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Abstract order type (status)                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // Ordem pendente atual
   ORDER_STATUS_MARKET_ACTIVE,                              // Ordem à mercado ativa
   ORDER_STATUS_HISTORY_ORDER,                              // Histórico da ordem à mercado
   ORDER_STATUS_HISTORY_PENDING,                            // Ordem pendente removida
   ORDER_STATUS_BALANCE,                                    // Operação Saldo
   ORDER_STATUS_CREDIT,                                     // Operação Crédito
   ORDER_STATUS_DEAL,                                       // Negócio
   ORDER_STATUS_UNKNOWN                                     // Estado desconhecido
  };
//+------------------------------------------------------------------+
//| Proprieades do tipo inteiro de ordem, negócio, posição           |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Ticket da ordem
   ORDER_PROP_MAGIC,                                        // Magic number da ordem
   ORDER_PROP_TIME_OPEN,                                    // Horário de abertura
   ORDER_PROP_TIME_CLOSE,                                   // Horário de fechamento
   ORDER_PROP_TIME_EXP,                                     // Data de expiração da ordem (para ordens pendentes)
   ORDER_PROP_TYPE,                                         // Tipo da ordem e negócio
   ORDER_PROP_STATUS,                                       // Estado da ordem (da enumeração ENUM_ORDER_STATUS)
   ORDER_PROP_REASON,                                       // origem ou razão do negócio/ordem/posição
   ORDER_PROP_POSITION_ID,                                  // ID da posição
   ORDER_PROP_POSITION_BY_ID,                               // ID da posição oposta
   ORDER_PROP_DEAL_ORDER,                                   // Ordem, baseada no negócio executado
   ORDER_PROP_DEAL_ENTRY,                                   // Direção do negócio – IN, OUT ou IN/OUT
   ORDER_PROP_TIME_OPEN_MSC,                                // Horário de abertura em milissegundos
   ORDER_PROP_TIME_CLOSE_MSC,                               // Horário de fechamento em milissegundos (horário de execução ou remoção - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_UPDATE,                                  // Horário da alteração da posição em segundos
   ORDER_PROP_TIME_UPDATE_MSC,                              // Horário da alteração da posição em milissegundos
   ORDER_PROP_TICKET_FROM,                                  // ticket da ordem pai
   ORDER_PROP_TICKET_TO,                                    // ticket da ordem derivada
   ORDER_PROP_PROFIT_PT,                                    // Lucro em pontos
   ORDER_PROP_CLOSE_BY_SL,                                  // Tipo do fechamento por StopLoss
   ORDER_PROP_CLOSE_BY_TP,                                  // Tipo do fechamento por TakeProfit
   ORDER_PROP_DIRECTION,                                    // Direção (Buy, Sell)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (22)                    // Número total de propriedades do tipo integer
//+------------------------------------------------------------------+
//| Propriedades do tipo real de ordem, negócio, posição             |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // Preço de abertura (preço do negócio MQL5)
   ORDER_PROP_PRICE_CLOSE,                                  // Preço de fechamento
   ORDER_PROP_PROFIT,                                       // Lucro
   ORDER_PROP_COMMISSION,                                   // Comissão
   ORDER_PROP_SWAP,                                         // Swap
   ORDER_PROP_VOLUME,                                       // Volume
   ORDER_PROP_VOLUME_CURRENT,                               // Volume não executado
   ORDER_PROP_SL,                                           // Preço do StopLoss
   ORDER_PROP_TP,                                           // Preço do TakeProfit
   ORDER_PROP_PROFIT_FULL,                                  // Lucro+comissão+swap
   ORDER_PROP_PRICE_STOP_LIMIT,                             // Preço da ordem limitada quando a ordem StopLimit é ativada
  };
#define ORDER_PROP_DOUBLE_TOTAL     (11)                    // Número total de propriedades do tipo real
//+------------------------------------------------------------------+
//| Propriedades do tipo string de ordem, negócio, posição           |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // Símbolo da ordem
   ORDER_PROP_COMMENT,                                      // Comentário da ordem
   ORDER_PROP_EXT_ID                                        // ID da ordem em um sistema de negociação externo
  };
#define ORDER_PROP_STRING_TOTAL     (3)                     // Número total de propriedades do tipo string
//+------------------------------------------------------------------+

Algumas propriedades foram colocadas adicionalmente nas enumerações: ticket de ordem pai, ticket da ordem derivada, lucro em pontos, propriedades de fechamento por stop loss ou take profit, direção e lucro total — considerando comissão e swap. Esses dados são frequentemente usados na lógica da estratégia de negociação, portanto, eles devem ser armazenados nos campos da classe abstrata Order, bem como atualizados e recebidos em programas personalizados de maneira imediata.

Para unir todas as propriedades da ordem (inteiro, real e string), crie as substituições de macro contendo o número de parâmetros em cada uma das três enumerações para cada tipo de propriedade.
A numeração das enumerações de propriedades do tipo inteiro começa a partir de zero, enquanto a numeração de enumerações de outras propriedades começa com o número total de propriedades anteriores. Assim, nós sempre podemos obter o índice da propriedade que precisamos como uma diferença entre o número de uma propriedade solicitada e o número da propriedade inicial dessa enumeração.

Depois de criar o arquivo Defines.mqh, conecte-o ao arquivo atual da classe COrder e crie na seção private a variável membro da classe para armazenar o ticket da ordem e três arrays para armazenar as propriedades da ordem do tipo inteiro, real e string:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                 // Ticket da ordem/negócio selecionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Propriedades do tipo inteiro
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Propriedados do tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // Propriedades do tipo string
public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Para incluir o arquivo Defines.mqh, nós definimos um caminho relativo para o arquivo entre aspas, em vez de aplicar colchetes angulares (<>) que usamos ao incluir o CObject. Isso é feito para que, ao transferir a biblioteca para qualquer outro diretório, a conexão entre os arquivos não seja perdida e sempre se refira ao local do arquivo Defines.mqh relativo ao diretório atual.

Agora, vamos criar dois métodos na mesma seção private. O primeiro é retornar o local exato da propriedade necessária nos arrays de propriedade, enquanto o segundo é retornar um construtor protegido na seção protected correspondente. Deixe o construtor padrão para criar um objeto de ordem vazia sem inicializar suas propriedades e remover o destruidor (nós não precisamos dele, e ele será criado automaticamente durante a compilação):

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Classe abstrata Order                                            |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Ticket da ordem/negócio selecionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Proprieades do tipo integer
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Propriedades do tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Propriedades do tipo string
   
   //--- Retorna o índice do array onde a propriedade double está realmente localizada
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                  }
   //--- Retorna o índice do array onde a propriedade string está localizada
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Construtor padrão
                     COrder(void){;}
protected:                                                                     
   //--- Construtor paramétrico protegido                                
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Até agora, o construtor protegido não faz nada. Ele será usado para inicializar imediatamente todas as propriedades da ordem, cujo ticket é passado para o construtor da classe, ao criar um objeto. Já que a ordem já foi selecionada, nós podemos obter as propriedades necessárias de uma ordem selecionada e gravá-las nos arrays de propriedades do objeto. Nós faremos isso um pouco depois, após a criação dos métodos para receber e retornar os dados da ordem.

Como essa é uma biblioteca multi-plataforma, será mais conveniente criar métodos separados para a obtenção das propriedades da ordem.
Na seção protected, adicione as descrições dos métodos para receber as propriedades do tipo inteiro, real e string de uma ordem selecionada.

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Classe abstrata Order                                            |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Ticket da ordem/negócio selecionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Proprieades do tipo integer
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Propriedades do tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Propriedades do tipo string
   
   //--- Retorna o índice do array onde a propriedade double está localizada
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Retorna o índice do array onde a propriedade string está localizada
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Construtor padrão
                     COrder(void){;}
protected:
   //--- Construtor paramétrico protegido
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- Obtém e retorna as propriedades do tipo inteiro de uma ordem selecionada de seus parâmetros
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- Obtém e retorna as propriedades do tipo real de uma ordem selecionada de seus parâmetros: (1) preço de abertura, (2) preço de fechamento, (3) lucro,
   //--- (4) comissão, (5) swap, (6) volume, (7) volume não executado (8) preço de StopLoss, (9) preço de TakeProfit (10) preço da ordem StopLimit
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Obtém e retorna as propriedades do tipo string de uma ordem selecionada de seus parâmetros: (1) símbolo, (2) comentário, (3) ID em uma bolsa
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//---
  };
//+------------------------------------------------------------------+
//| Construtor paramétrico Closed                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Atualmente, foram declarados apenas os métodos para receber as propriedades. Elas ainda não estão implementados, embora a classe seja compilada sem erros. Os arratas de propriedade da ordem serão preenchidas no construtor de classe usando os métodos que acabamos de adicionar. Quando tudo estiver pronto, esses arrays nos permitirão obter no programa qualquer propriedade mediante solicitação.

O método virtual de comparação de objetos por propriedades especificadas é declarado na classe CObject da biblioteca padrão. No entanto, o método deve ser implementado nas subclasses. Portanto, vamos adicionar à classe de ordem abstrata o método para comparar objetos de acordo com qualquer uma de suas propriedades, assim como vários métodos públicos para acessar as propriedades da ordem e os métodos virtuais retornando as flags para suportar a propriedade específica do objeto order. Esses métodos devem ser implementados nos objetos descendentes da classe COrder. Isso será necessário posteriormente para selecionar as ordens da lista de coleções por qualquer uma de suas propriedades. Por padrão, se algum desses métodos não for implementado na classe descendente, será retornada a flag que indica o suporte da ordem para essa propriedade.

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Classe abstrata Order                                            |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Ticket da ordem/negócio selecionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Proprieades do tipo integer
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Propriedades do tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Propriedades do tipo string
   
   //--- Retorna o índice do array onde a propriedade double está localizada
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Retorna o índice do array onde a propriedade string está localizada
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Construtor padrão
                     COrder(void){;}
protected:
   //--- Construtor paramétrico protegido
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- Obtém e retorna as propriedades do tipo inteiro de uma ordem selecionada de seus parâmetros
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- Obtém e retorna as propriedades do tipo real de uma ordem selecionada de seus parâmetros: (1) preço de abertura, (2) preço de fechamento, (3) lucro,
   //--- (4) comissão, (5) swap, (6) volume, (7) volume não executado (8) preço de StopLoss, (9) preço de TakeProfit (10) preço da ordem StopLimit
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Obtém e retorna as propriedades do tipo string de uma ordem selecionada de seus parâmetros: (1) símbolo, (2) comentário, (3) ID em uma bolsa
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
public:
   //--- Retorna as propriedades do tipo (1) inteiro, (2) real e (3) string do array de propriedades
   long              GetProperty(ENUM_ORDER_PROP_INTEGER property)      const { return m_long_prop[property];                    }
   double            GetProperty(ENUM_ORDER_PROP_DOUBLE property)       const { return m_double_prop[this.IndexProp(property)];  }
   string            GetProperty(ENUM_ORDER_PROP_STRING property)       const { return m_string_prop[this.IndexProp(property)];  }
   
   //--- Retorna a flag da ordem que suporta a propriedade
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property)         { return true; }
   
   //--- Compara os objetos COrder por todas as propriedades possíveis
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//---
  };
//+------------------------------------------------------------------+
//| Construtor paramétrico Closed                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Implementando o método para comparar duas ordens por uma propriedade especificada:

//+------------------------------------------------------------------+
//| Comparar objetos COrder por todas as propriedades possíveis      |
//+------------------------------------------------------------------+
int COrder::Compare(const CObject *node,const int mode=0) const
  {
   const COrder *order_compared=node;
//--- compara as propriedades do tipo inteiro de duas ordens
   if(mode<ORDER_PROP_INTEGER_TOTAL)
     {
      long value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compara as propriedades do tipo real de duas ordens
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL)
     {
      double value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compara as propriedades do tipo string de duas ordens
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

O método passa o ponteiro para o objeto order cuja propriedade deve ser comparada com um determinado valor dado, bem como o próprio valor da enumeração de propriedades da ordem.
Se o valor da ordem exceder o valor comparado, o sistema retornará 1, se for menor que o valor comparado, o sistema retornará -1, caso contrário - 0. A lista de ordens em que a comparação é realizada deve ser classificada preliminarmente pela propriedade comparada.

Agora, vamos implementar os métodos para receber as propriedades da ordem e gravá-las nos arrays de propriedade. Estes são os métodos que foram previamente declarados na seção private. Como os métodos para receber as propriedades da ordem são multi-plataforma, vamos analisá-los usando o número mágico do EA como exemplo:

//+------------------------------------------------------------------+
//| Retorna o número mágico                                          |
//+------------------------------------------------------------------+
long COrder::OrderMagicNumber() const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_MAGIC);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_MAGIC);                 break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_MAGIC);   break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_MAGIC); break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

Se este é um código para MQL4, o número mágico é retornado pela função em MQL5 OrderMagicNumber(). De outra forma, verifica o estado da ordem. Dependendo do que nós estamos lidando, ele retorne uma posição, uma ordem ou um número mágico do negócio.

Os métodos de leitura e escrita restantes das propriedades de uma ordem/negócio/posição destacada são feitos da mesma maneira. Você pode analisá-los por conta própria.

Métodos para a obtenção das propriedades do tipo inteiro de ordem/negócio/posição:
//+------------------------------------------------------------------+
//| Retorna o ticket                                                 |
//+------------------------------------------------------------------+
long COrder::OrderTicket(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     :
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     :
      case ORDER_STATUS_DEAL              : res=(long)m_ticket;                                 break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna o ticket da ordem pai                                    |
//+------------------------------------------------------------------+
long COrder::OrderTicketFrom(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"from #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,6));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Retorna o ticket da ordem filho                                  |
//+------------------------------------------------------------------+
long COrder::OrderTicketTo(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"to #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,4));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Retorna o ID da posição                                          |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID);   break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna o ID da posição oposta                                   |
//+------------------------------------------------------------------+
long COrder::OrderPositionByID(void) const
  {
#ifdef __MQL4__
   return 0;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_BY_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna o horário de abertura em milissegundos                   |
//+------------------------------------------------------------------+
long COrder::OrderOpenTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderOpenTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TIME_MSC);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TIME_SETUP_MSC);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP_MSC); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);         break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna o horário de fechamento em milissegundos                 |
//+------------------------------------------------------------------+
long COrder::OrderCloseTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderCloseTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE_MSC);     break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);  break;
      default                             : res=0;                                                          break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna o tipo                                                   |
//+------------------------------------------------------------------+
long COrder::OrderType(void) const
  {
#ifdef __MQL4__
   return (long)::OrderType();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TYPE);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TYPE);    break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna o tipo por direção                                       |
//+------------------------------------------------------------------+
long COrder::OrderTypeByDirection(void) const
  {
   ENUM_ORDER_STATUS status=(ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);
   if(status==ORDER_STATUS_MARKET_ACTIVE)
     {
      return(this.OrderType()==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
     }
   if(status==ORDER_STATUS_MARKET_PENDING || status==ORDER_STATUS_HISTORY_PENDING)
     {
      return
        (
         this.OrderType()==ORDER_TYPE_BUY_LIMIT || 
         this.OrderType()==ORDER_TYPE_BUY_STOP 
         #ifdef __MQL5__                        ||
         this.OrderType()==ORDER_TYPE_BUY_STOP_LIMIT 
         #endif ? 
         ORDER_TYPE_BUY : 
         ORDER_TYPE_SELL
        );
     }
   if(status==ORDER_STATUS_HISTORY_ORDER)
     {
      return this.OrderType();
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Retorna o tipo da execução pelo resto                            |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o tipo de expiração da ordem                             |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Origem ou razão da ordem                                         |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Ordem, com base no negócio executado                             |
//+------------------------------------------------------------------+
long COrder::DealOrder(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_ORDER);         break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Direção do negócio IN, OUT, IN/OUT                               |
//+------------------------------------------------------------------+
long COrder::DealEntry(void) const
  {
#ifdef __MQL4__
   return ::OrderType();
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetInteger(m_ticket,DEAL_ENTRY);break;
      default                 : res=WRONG_VALUE;                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna a flag de fechamento da posição por StopLoss             |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByStopLoss(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[sl")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_SL : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_SL : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna a flag de fechamento da posição pelo TakeProfit          |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByTakeProfit(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[tp")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_TP : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_TP : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o horário de abertura                                    |
//+------------------------------------------------------------------+
datetime COrder::OrderOpenTime(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=(datetime)::PositionGetInteger(POSITION_TIME);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o horário de fechamento                                  |
//+------------------------------------------------------------------+
datetime COrder::OrderCloseTime(void) const
  {
#ifdef __MQL4__
   return ::OrderCloseTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE);  break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o horário de expiração                                   |
//+------------------------------------------------------------------+
datetime COrder::OrderExpiration(void) const
  {
#ifdef __MQL4__
   return ::OrderExpiration();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);                  break;
      case ORDER_STATUS_HISTORY_PENDING   : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_EXPIRATION);  break;
      default                             : res=0;                                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Horário da alteração da posição em segundos                      |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdate(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE); break;
      default                          : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Horário da alteração da posição em milissegundos                 |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdateMSC(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);break;
      default                          : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Métodos para a obtenção das propriedades do tipo real da ordem/negócio/posição:

//+------------------------------------------------------------------+
//| Retorna o preço de abertura                                      |
//+------------------------------------------------------------------+
double COrder::OrderOpenPrice(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenPrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_PRICE_OPEN);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_OPEN);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o preço de fechamento                                    |
//+------------------------------------------------------------------+
double COrder::OrderClosePrice(void) const
  {
#ifdef __MQL4__
   return ::OrderClosePrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o lucro                                                  |
//+------------------------------------------------------------------+
double COrder::OrderProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_PROFIT);        break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_PROFIT);break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna a comissão                                               |
//+------------------------------------------------------------------+
double COrder::OrderCommission(void) const
  {
#ifdef __MQL4__
   return ::OrderCommission();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetDouble(m_ticket,DEAL_COMMISSION);  break;
      default                 : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o swap                                                   |
//+------------------------------------------------------------------+
double COrder::OrderSwap(void) const
  {
#ifdef __MQL4__
   return ::OrderSwap();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_SWAP);          break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_SWAP);  break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o volume                                                 |
//+------------------------------------------------------------------+
double COrder::OrderVolume(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_VOLUME);                    break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_INITIAL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_INITIAL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_VOLUME);            break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o volume não executado                                   |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_CURRENT);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_CURRENT);  break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o preço do StopLoss                                      |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o preço do TakeProfit                                    |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o preço da ordem limitada                                |
//| quando a ordem StopLimit é ativada                               |
//+------------------------------------------------------------------+
double COrder::OrderPriceStopLimit(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_STOPLIMIT);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_STOPLIMIT); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Métodos para a obtenção das propriedades do tipo string da ordem/negócio/posição:

//+------------------------------------------------------------------+
//| Retorna o símbolo                                                |
//+------------------------------------------------------------------+
string COrder::OrderSymbol(void) const
  {
#ifdef __MQL4__
   return ::OrderSymbol();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_SYMBOL);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_SYMBOL);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_SYMBOL); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_SYMBOL);   break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o comentário                                             |
//+------------------------------------------------------------------+
string COrder::OrderComment(void) const
  {
#ifdef __MQL4__
   return ::OrderComment();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_COMMENT);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_COMMENT);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_COMMENT);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_COMMENT);  break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna o ID usado pela bolsa                                    |
//+------------------------------------------------------------------+
string COrder::OrderExternalID(void) const
  {
#ifdef __MQL4__
   return "";
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_EXTERNAL_ID);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_EXTERNAL_ID);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_EXTERNAL_ID);    break;
      default                             : res="";                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Nós declaramos e implementamos os métodos privados para receber as propriedades dos dados da ordem.

Agora está na hora de implementar o construtor da classe protegida, escrevendo nele os métodos que salvam todas as propriedades da ordem cujo ticket foi passado para o construtor.

Implementando o construtor de classe protegida:

//+------------------------------------------------------------------+
//| Construtor paramétrico Closed                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Salva as propriedades do tipo inteiro
   m_ticket=ticket;
   m_long_prop[ORDER_PROP_STATUS]                              = order_status;
   m_long_prop[ORDER_PROP_MAGIC]                               = this.OrderMagicNumber();
   m_long_prop[ORDER_PROP_TICKET]                              = this.OrderTicket();
   m_long_prop[ORDER_PROP_TIME_OPEN]                           = (long)(ulong)this.OrderOpenTime();
   m_long_prop[ORDER_PROP_TIME_CLOSE]                          = (long)(ulong)this.OrderCloseTime();
   m_long_prop[ORDER_PROP_TIME_EXP]                            = (long)(ulong)this.OrderExpiration();
   m_long_prop[ORDER_PROP_TYPE]                                = this.OrderType();
   m_long_prop[ORDER_PROP_DIRECTION]                           = this.OrderTypeByDirection();
   m_long_prop[ORDER_PROP_POSITION_ID]                         = this.OrderPositionID();
   m_long_prop[ORDER_PROP_REASON]                              = this.OrderReason();
   m_long_prop[ORDER_PROP_DEAL_ORDER]                          = this.DealOrder();
   m_long_prop[ORDER_PROP_DEAL_ENTRY]                          = this.DealEntry();
   m_long_prop[ORDER_PROP_POSITION_BY_ID]                      = this.OrderPositionByID();
   m_long_prop[ORDER_PROP_TIME_OPEN_MSC]                       = this.OrderOpenTimeMSC();
   m_long_prop[ORDER_PROP_TIME_CLOSE_MSC]                      = this.OrderCloseTimeMSC();
   m_long_prop[ORDER_PROP_TIME_UPDATE]                         = (long)(ulong)this.PositionTimeUpdate();
   m_long_prop[ORDER_PROP_TIME_UPDATE_MSC]                     = (long)(ulong)this.PositionTimeUpdateMSC();
   
//--- Salva as propriedades do tipo real
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]        = this.OrderOpenPrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]       = this.OrderClosePrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]            = this.OrderProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]        = this.OrderCommission();
   m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]              = this.OrderSwap();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]            = this.OrderVolume();
   m_double_prop[this.IndexProp(ORDER_PROP_SL)]                = this.OrderStopLoss();
   m_double_prop[this.IndexProp(ORDER_PROP_TP)]                = this.OrderTakeProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]    = this.OrderVolumeCurrent();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]  = this.OrderPriceStopLimit();
   
//--- Salva as propriedades do tipo string
   m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]            = this.OrderSymbol();
   m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]           = this.OrderComment();
   m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]            = this.OrderExternalID();
   
//--- Salva as propriedades adicionais do tipo inteiro
   m_long_prop[ORDER_PROP_PROFIT_PT]                           = this.ProfitInPoints();
   m_long_prop[ORDER_PROP_TICKET_FROM]                         = this.OrderTicketFrom();
   m_long_prop[ORDER_PROP_TICKET_TO]                           = this.OrderTicketTo();
   m_long_prop[ORDER_PROP_CLOSE_BY_SL]                         = this.OrderCloseByStopLoss();
   m_long_prop[ORDER_PROP_CLOSE_BY_TP]                         = this.OrderCloseByTakeProfit();
   
//--- Salva as propriedades adicionais do tipo real
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]       = this.ProfitFull();
  }
//+------------------------------------------------------------------+

Quando nós percorremos ao longo do histórico ou ordens/negócios/posições do mercado em um loop ou selecionamos uma nova ordem/negócio/posição, um novo objeto derivado da classe COrder é criado. O construtor da classe que apresenta o estado da ordem e o ticket é chamado no construtor da classe, recebendo apenas o ticket da ordem. Em seguida, os arrays da propriedade order são simplesmente preenchidos no construtor da classe COrder usando os métodos descritos acima.

Assim, cada nova ordem/negócio/posição deve ter sua propriedade exclusiva. Todos eles devem ser armazenados nas listas, enquanto o trabalho com essas listas é a principal atividade da biblioteca. As listas podem ser classificadas por qualquer uma das propriedades de ordem/negócio/posição. Além disso, é possível gerar novas listas a partir daqueles selecionados.

Nesta fase, a funcionalidade básica da classe abstrata COrder foi implementada. Esta é uma classe básica para armazenar vários tipos de ordens, negócios e posições. Todos os outros serão derivados para criar objetos divididos por tipos de ordens, negócios e posições.

A biblioteca foi criada para o acesso simplificado aos dados e fácil desenvolvimento de programas.
No estado atual, nós temos três métodos públicos para acessar as propriedades da ordem abstrata GetProperty(XXX). Você pode usá-los, mas isso não é muito conveniente, pois é necessário lembrar os nomes dos membros das enumerações que descrevem uma propriedade da ordem específica. Portanto, nós adicionaremos vários métodos públicos para obter os dados necessários. Esses métodos terão nomes razoáveis para que fique imediatamente claro qual propriedade pode ser obtida usando um método específico.

Estes métodos podem ser usados em programas personalizados para a leitura de propriedades de uma ordem, negócio ou posição selecionada
   //--- Retorna o (1) ticket, (2) ticket da ordem pai, (3) ticket da ordem derivada, (4) número mágico, (5) motivo da ordem (6) ID da posição
   //--- (7) ID da posição oposta, (8) flag de fechamento por StopLoss, (9) flag de fechamento por TakeProfit (10) horário de abertura, (11) horário de fechamento,
   //--- (12) horário de abertura em milissegundos, (13) horário de fechamento em milissegundos (14) data de expiração, (15) tipo, (16) estado, (17) direção
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   datetime          TimeOpen(void)                                     const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN);        }
   datetime          TimeClose(void)                                    const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE);       }
   datetime          TimeOpenMSC(void)                                  const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC);    }
   datetime          TimeCloseMSC(void)                                 const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC);   }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_TYPE   TypeOrder(void)                                    const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_TYPE);      }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   
   //--- Retorna (1) preço de abertura, (2) preço de fechamento, (3) lucro, (4) comissão, (5) swap, (6) volume, 
   //--- (7) volume não executado (8) StopLoss e (9) TakeProfit (10) preço da ordem StopLimit
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
   //--- Retorna o (1) símbolo, (2) comentário, (3) ID da bolsa
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

   //--- Obtém o lucro completo da ordem
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
   //--- Obtém o lucro da ordem em pontos
   int               ProfitInPoints(void) const;

Implementando o método de recebimento do lucro em pontos:

//+------------------------------------------------------------------+
//| Retorna o lucro da ordem em pontos                               |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   ENUM_ORDER_TYPE type=this.TypeOrder();
   string symbol=this.Symbol();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type>ORDER_TYPE_SELL || point==0) return 0;
   if(this.Status()==ORDER_STATUS_HISTORY_ORDER)
      return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0);
   else if(this.Status()==ORDER_STATUS_MARKET_ACTIVE)
     {
      if(type==ORDER_TYPE_BUY)
         return int((::SymbolInfoDouble(symbol,SYMBOL_BID)-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-::SymbolInfoDouble(symbol,SYMBOL_ASK))/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

E vamos adicionar os métodos públicos para descrever algumas propriedades do objeto da ordem para que você possa exibi-las sob demanda de uma maneira conveniente:

   //--- Obtém a descrição da propriedade do tipo (1) inteiro, (2) real e (3) string
   string            GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_STRING property);
   //--- Retorna o nome do estado da ordem
   string            StatusDescription(void) const;
   //--- Retorna o nome da ordem ou posição
   string            TypeDescription(void) const;
   //--- Retorna o nome da direção do negócio
   string            DealEntryDescription(void) const;
   //--- Retorna a direção da ordem/posição
   string            DirectionDescription(void) const;
   //--- Envia a descrição das propriedades da ordem para o diário (full_prop=true - todas as propriedades, false - apenas as suportadas)
   void              Print(const bool full_prop=false);

Antes de nós implementarmos esses métodos, vamos resolver ainda outro problema: a biblioteca e os programas baseados nela requerem várias funções de serviço. Por exemplo, para este caso, nós precisamos da função para exibir o horário em milissegundos e a função de receber mensagens em um dos dois idiomas. O idioma das mensagens depende do idioma do terminal.

Crie um novo arquivo de inclusão no diretório-raiz da biblioteca


e chame-o de DELib. Este será o arquivo da biblioteca de funções de serviço disponível para uso pelas próprias classes da biblioteca e pelos programas baseados na biblioteca.


Clique em Concluir para criar um arquivo de modelo:

//+------------------------------------------------------------------+
//|                                                        DELib.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"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Inclua o arquivo Defines.mqh nele e modifique o modelo de acordo com as nossas necessidades:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Necessário para mql4
//+------------------------------------------------------------------+
//| Arquivos de inclusão                                             |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Funções de serviço                                               |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

Como nós incluímos o Defines.mqh nesse arquivo, nós podemos incluir esse novo arquivo (em vez do Defines.mqh) no arquivo da classe COrder, de modo que ambos estejam disponíveis na biblioteca. Além disso, nós faremos isso em uma string, não em duas.
Substitua a diretiva include no arquivo Order.mqh
:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| Classe abstrata Order                                            |
//+------------------------------------------------------------------+

Vamos adicionar também a definição do idioma do país do usuário como uma substituição de macro no arquivo Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. | 
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Substituições de macro                                           |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"
//+------------------------------------------------------------------+

Assim, o usuário poderá definir seu idioma nativo para as mensagens exibidas se o idioma do terminal não for o inglês. No entanto, para conseguir isso, nós teremos que substituir todas as futuras mensagens que devemos inserir aqui com as exigidas pelo usuário.

Adicione a função para retornar as mensagens em um dos dois idiomas no arquivo DELIB.mqh:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Necessário para mql4
//+------------------------------------------------------------------+
//| Arquivos de inclusão                                             |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Funções de serviço                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Retorna o texto em um dos dois idiomas                           |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+

A função verifica o idioma do terminal e, se corresponder aos idiomas especificados na substituição de macro COUNTRY_LANG, é exibido o texto transmitido no primeiro parâmetro da função. Caso contrário, o texto contido no segundo parâmetro da função (Inglês) é exibido.

Vamos também adicionar a função de exibir o horário com os milissegundos:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Necessário para mql4
//+------------------------------------------------------------------+
//| Arquivo de inclusão                                              |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Funções de serviço                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Retorna o texto em um dos dois idiomas                           |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+
//| Retorna o horário com os milissegundos                           |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

Aqui tudo é simples: o horário definido em milissegundos é passado para a função. Para calcular o horário em segundos, nós precisamos dividir o valor passado para a função por 1000. Para calcular os milissegundos, pegue o resto desta divisão. Todos os valores recebidos são formatados como strings e retornados ao programa que realizou a chamada.

Às vezes, você pode querer obter o número de casas decimais em um lote de símbolos. Vamos inserir essa função em nosso arquivo de funções de serviço:

//+------------------------------------------------------------------+
//| Retorna o número de casas decimais em um lote de símbolos        |
//+------------------------------------------------------------------+
uint DigitsLots(const string symbol_name) 
  { 
   return (int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(10)));
  }
//+------------------------------------------------------------------+

Além das funções de serviço, nós precisaremos de três métodos para retornar a razão da ordem, direção e tipo de negócio para exibir as mensagens no diário. Adiciona os três métodos à seção protegida da classe COrder:

   //--- Retorna a (1) razão, (2) direção, (3) tipo do negócio
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;

E sua implementação:

//+------------------------------------------------------------------+
//| Razão                                                            |
//+------------------------------------------------------------------+
string COrder::GetReasonDescription(const long reason) const
  {
#ifdef __MQL4__
   return
     (
      this.IsCloseByStopLoss()            ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss")                  :
      this.IsCloseByTakeProfit()          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit")              :
      this.Reason()==ORDER_REASON_EXPERT  ?  TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program")   :
      TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
     );
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE        : 
         res=
           (
            reason==POSITION_REASON_CLIENT   ?  TextByLanguage("Позиция открыта из десктопного терминала","Position opened from desktop terminal") :
            reason==POSITION_REASON_MOBILE   ?  TextByLanguage("Позиция открыта из мобильного приложения","Position opened from mobile app") :
            reason==POSITION_REASON_WEB      ?  TextByLanguage("Позиция открыта из веб-платформы","Position opened from web platform") :
            reason==POSITION_REASON_EXPERT   ?  TextByLanguage("Позиция открыта из советника или скрипта","Position opened from EA or script") : ""
           );
         break;
      case ORDER_STATUS_MARKET_PENDING       :
      case ORDER_STATUS_HISTORY_PENDING      : 
      case ORDER_STATUS_HISTORY_ORDER        : 
         res=
           (
            reason==ORDER_REASON_CLIENT      ?  TextByLanguage("Ордер выставлен из десктопного терминала","Order set from desktop terminal") :
            reason==ORDER_REASON_MOBILE      ?  TextByLanguage("Ордер выставлен из мобильного приложения","Order set from mobile app") :
            reason==ORDER_REASON_WEB         ?  TextByLanguage("Ордер выставлен из веб-платформы","Oder set from web platform") :
            reason==ORDER_REASON_EXPERT      ?  TextByLanguage("Ордер выставлен советником или скриптом","Order set from EA or script") :
            reason==ORDER_REASON_SL          ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==ORDER_REASON_TP          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==ORDER_REASON_SO          ?  TextByLanguage("Ордер выставлен в результате наступления Stop Out","Due to Stop Out") : ""
           );
         break;
      case ORDER_STATUS_DEAL                 : 
         res=
           (
            reason==DEAL_REASON_CLIENT       ?  TextByLanguage("Сделка проведена из десктопного терминала","Deal carried out from desktop terminal") :
            reason==DEAL_REASON_MOBILE       ?  TextByLanguage("Сделка проведена из мобильного приложения","Deal carried out from mobile app") :
            reason==DEAL_REASON_WEB          ?  TextByLanguage("Сделка проведена из веб-платформы","Deal carried out from web platform") :
            reason==DEAL_REASON_EXPERT       ?  TextByLanguage("Сделка проведена из советника или скрипта","Deal carried out from EA or script") :
            reason==DEAL_REASON_SL           ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==DEAL_REASON_TP           ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==DEAL_REASON_SO           ?  TextByLanguage("Сделка проведена в результате наступления Stop Out","Due to Stop Out") :
            reason==DEAL_REASON_ROLLOVER     ?  TextByLanguage("Сделка проведена по причине переноса позиции","Due to position rollover") :
            reason==DEAL_REASON_VMARGIN      ?  TextByLanguage("Сделка проведена по причине начисления/списания вариационной маржи","Due to variation margin") :
            reason==DEAL_REASON_SPLIT        ?  TextByLanguage("Сделка проведена по причине сплита (понижения цены) инструмента","Due to split") : ""
           );
         break;
      default                                : res="";   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

A afiliação para MQL4 ou MQL5 foi verificada, a razão da ordem passada nas entradas é verificada de acordo com o estado da ordem e sua descrição é retornada.

//+------------------------------------------------------------------+
//| Descrição da direção do negócio                                  |
//+------------------------------------------------------------------+
string COrder::GetEntryDescription(const long deal_entry) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","Property not supported for pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","Property not supported for history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_entry==DEAL_ENTRY_IN     ?  TextByLanguage("Вход в рынок","Entry to the market") :
            deal_entry==DEAL_ENTRY_OUT    ?  TextByLanguage("Выход из рынка","Out from the market") :
            deal_entry==DEAL_ENTRY_INOUT  ?  TextByLanguage("Разворот","Reversal") :
            deal_entry==DEAL_ENTRY_OUT_BY ?  TextByLanguage("Закрытие встречной позицией","Closing by opposite position") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

A afiliação para MQL4 ou MQL5 é verificada, a direção do negócio passado nas entradas é verificada de acordo com o estado da ordem e sua descrição é retornada.

//+------------------------------------------------------------------+
//| Retorna o nome do tipo do negócio                                |
//+------------------------------------------------------------------+
string COrder::GetTypeDealDescription(const long deal_type) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","Property not supported for pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","Property not supported for history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_type==DEAL_TYPE_BUY                      ?  TextByLanguage("Сделка на покупку","Buy deal") :
            deal_type==DEAL_TYPE_SELL                     ?  TextByLanguage("Сделка на продажу","Sell deal") :
            deal_type==DEAL_TYPE_BALANCE                  ?  TextByLanguage("Начисление баланса","Balance accrual") :
            deal_type==DEAL_TYPE_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit accrual") :
            deal_type==DEAL_TYPE_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Extra charges") :
            deal_type==DEAL_TYPE_CORRECTION               ?  TextByLanguage("Корректирующая запись","Corrective entry") :
            deal_type==DEAL_TYPE_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonuses") :
            deal_type==DEAL_TYPE_COMMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional comissions") :
            deal_type==DEAL_TYPE_COMMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Commission accrued at the end of a trading day") :
            deal_type==DEAL_TYPE_COMMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Commission accrued at the end of a month") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Agency commission charged at the end of a trading day") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Agency commission charged at the end of a month") :
            deal_type==DEAL_TYPE_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Accrued interest on free funds") :
            deal_type==DEAL_TYPE_BUY_CANCELED             ?  TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") :
            deal_type==DEAL_TYPE_SELL_CANCELED            ?  TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") :
            deal_type==DEAL_DIVIDEND                      ?  TextByLanguage("Начисление дивиденда","Accrued dividends") :
            deal_type==DEAL_DIVIDEND_FRANKED              ?  TextByLanguage("Начисление франкированного дивиденда","Accrual of franked dividend") :
            deal_type==DEAL_TAX                           ?  TextByLanguage("Начисление налога","Tax accrual") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }

A afiliação para MQL4 ou MQL5 é verificada, o tipo de negócio transmitido nas entradas é verificado de acordo com o estado da ordem e sua descrição é retornada.

Implementando os métodos que descrevem as propriedades da ordem:

//+------------------------------------------------------------------+
//| Retorna a descrição da propriedade do tipo inteiro de uma ordem  |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property)
  {
   return
     (
   //--- Propriedades gerais
      property==ORDER_PROP_MAGIC             ?  TextByLanguage("Магик","Magic")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET            ?  TextByLanguage("Тикет","Ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_FROM       ?  TextByLanguage("Тикет родительского ордера","Ticket of parent order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_TO         ?  TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN         ?  TextByLanguage("Время открытия","Open time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_CLOSE        ?  TextByLanguage("Время закрытия","Close time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_EXP          ?  TextByLanguage("Дата экспирации","Expiration date")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Не задана",": Not set") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS))
         )  :
      property==ORDER_PROP_TYPE              ?  TextByLanguage("Тип","Type")+": "+this.TypeDescription()                   :
      property==ORDER_PROP_DIRECTION         ?  TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() :
      
      property==ORDER_PROP_REASON            ?  TextByLanguage("Причина","Reason")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetReasonDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_ID       ?  TextByLanguage("Идентификатор позиции","Position ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ORDER        ?  TextByLanguage("Сделка на основании ордера","Deal by order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  TextByLanguage("Направление сделки","Deal direction")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetEntryDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_BY_ID    ?  TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN_MSC     ?  TextByLanguage("Время открытия в милисекундах","Open time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_UPDATE       ?  TextByLanguage("Время изменения позиции","Position change time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0")
         )  :
      property==ORDER_PROP_TIME_UPDATE_MSC   ?  TextByLanguage("Время изменения позиции в милисекундах","Position change time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? (string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property)) : "0")
         )  :
   //--- Propriedade adicional
      property==ORDER_PROP_STATUS            ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  TextByLanguage("Прибыль в пунктах","Profit in points")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_CLOSE_BY_SL       ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==ORDER_PROP_CLOSE_BY_TP       ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Retorna a descrição da propriedade do tipo real da ordem         |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(ORDER_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(ORDER_PROP_SYMBOL));
   return
     (
      //--- Propriedades gerais
      property==ORDER_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Close price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Open price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_SL                ?  TextByLanguage("Цена StopLoss","StopLoss price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_TP                ?  TextByLanguage("Цена TakeProfit","TakeProfit price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_PROFIT            ?  TextByLanguage("Прибыль","Profit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_COMMISSION        ?  TextByLanguage("Комиссия","Comission")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_SWAP              ?  TextByLanguage("Своп","Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_VOLUME            ?  TextByLanguage("Объём","Volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_VOLUME_CURRENT    ?  TextByLanguage("Невыполненный объём","Unfulfilled volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_PRICE_STOP_LIMIT  ? 
         TextByLanguage("Цена постановки Limit ордера при активации StopLimit ордера","Price of placing Limit order when StopLimit order activated")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
          ) :
      //--- Propriedade adicional
      property==ORDER_PROP_PROFIT_FULL       ?  TextByLanguage("Прибыль+комиссия+своп","Profit+Comission+Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
          ) :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Retorna a descrição da propriedade do tipo string de uma ordem   |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_STRING property)
  {
   return
     (
      property==ORDER_PROP_SYMBOL         ?  TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""            :
      property==ORDER_PROP_COMMENT        ?  TextByLanguage("Комментарий","Comment")+
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"") :
      property==ORDER_PROP_EXT_ID         ?  TextByLanguage("Идентификатор на бирже","ID on exchange")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"")):
      ""
     );
  }
//+------------------------------------------------------------------+

Implementação dos métodos para retornar o nome do estado da ordem, nome da ordem ou posição, nome da direção do negócio, descrição do tipo de direção da ordem/posição e o método de exibição conveniente das propriedades da ordem no diário.

Primeiro, adicione outra substituição de macro para o arquivo Defines.mqh para a exibição conveniente do nome da função/método no diário:

//+------------------------------------------------------------------+
//| Substituições de macro                                           |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"            // Country language
#define DFUN           (__FUNCTION__+": ")   // "Function description"
//+------------------------------------------------------------------+

Agora, em vez de escrever as strings totalmente, nós podemos simplesmente escrever DFUN, e o compilador altera a descrição para as strings definidas na macro.

//+------------------------------------------------------------------+
//| Retorna o nome do estado da ordem                                |
//+------------------------------------------------------------------+
string COrder::StatusDescription(void) const
  {
   ENUM_ORDER_STATUS status=this.Status();
   ENUM_ORDER_TYPE   type=(ENUM_ORDER_TYPE)this.TypeOrder();
   return
     (
      status==ORDER_STATUS_BALANCE        ?  TextByLanguage("Балансная операция","Balance operation") :
      status==ORDER_STATUS_CREDIT         ?  TextByLanguage("Кредитная операция","Credits operation") :
      status==ORDER_STATUS_HISTORY_ORDER  || status==ORDER_STATUS_HISTORY_PENDING                     ?  
      (
       type>ORDER_TYPE_SELL ? TextByLanguage("Отложенный ордер","Pending order")                      :
       TextByLanguage("Ордер на ","The order to ")+(type==ORDER_TYPE_BUY ? TextByLanguage("покупку","buy") : TextByLanguage("продажу","sell"))
      )                                                                                               :
      status==ORDER_STATUS_DEAL           ?  TextByLanguage("Сделка","Deal")                          :
      status==ORDER_STATUS_MARKET_ACTIVE  ?  TextByLanguage("Позиция","Active position")              :
      status==ORDER_STATUS_MARKET_PENDING ?  TextByLanguage("Установленный отложенный ордер","Active pending order") :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Retorna o nome da ordem ou posição                               |
//+------------------------------------------------------------------+
string COrder::TypeDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.GetTypeDealDescription(this.TypeOrder());
   else switch(this.TypeOrder())
     {
      case ORDER_TYPE_BUY              :  return "Buy";
      case ORDER_TYPE_BUY_LIMIT        :  return "Buy Limit";
      case ORDER_TYPE_BUY_STOP         :  return "Buy Stop";
      case ORDER_TYPE_SELL             :  return "Sell";
      case ORDER_TYPE_SELL_LIMIT       :  return "Sell Limit";
      case ORDER_TYPE_SELL_STOP        :  return "Sell Stop";
      #ifdef __MQL4__
      case ORDER_TYPE_BALANCE          :  return TextByLanguage("Балансовая операция","Balance operation");
      case ORDER_TYPE_CREDIT           :  return TextByLanguage("Кредитная операция","Credit operation");
      #else 
      case ORDER_TYPE_BUY_STOP_LIMIT   :  return "Buy Stop Limit";
      case ORDER_TYPE_SELL_STOP_LIMIT  :  return "Sell Stop Limit";
      #endif 
      default                          :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| Retorna o nome da direção do negócio                             |
//+------------------------------------------------------------------+
string COrder::DealEntryDescription(void) const
  {
   return(this.Status()==ORDER_STATUS_DEAL ? this.GetEntryDescription(this.GetProperty(ORDER_PROP_DEAL_ENTRY)) : "");
  }
//+------------------------------------------------------------------+
//| Retorna o tipo da direção da ordem/posição                       |
//+------------------------------------------------------------------+
string COrder::DirectionDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.TypeDescription();
   switch(this.TypeByDirection())
     {
      case ORDER_TYPE_BUY  :  return "Buy";
      case ORDER_TYPE_SELL :  return "Sell";
      default              :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| Envia as propriedades da ordem para o diário                     |
//+------------------------------------------------------------------+
void COrder::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров: \"","Beginning of the parameter list: \""),this.StatusDescription(),"\" =============");
   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(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   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(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   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(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of the parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Agora que todos os métodos da classe abstrata COrder foram implementados, nós podemos ver o que realmente nós temos neste estágio.

Teste da classe abstrata COrder

Vamos testar e ver como os objetos base da COrder são criados e preenchidos com os dados. Mais adiante, nós vamos desenvolver os objetos por tipos baseados nele e armazená-los em coleções.

No diretório Experts ou subdiretório, crie uma nova pasta e chame-a de TestDoEasy. Em seguida, crie outra pasta dentro da recém criada: Part01. O arquivo de teste do EA está localizado nela.

Clique com o botão direito do mouse em Part01 no navegador do editor e selecione "Novo arquivo" no menu pop-up ou pressione Ctrl+N com a pasta Part01 destacada. A janela do Assistente MQL é aberta com a opção "Expert Advisor (template)" já selecionada, clique em Avançar e adicione o nome do novo arquivo TestDoEasyPart01 ao caminho do arquivo já especificado no campo de entrada do nome. Clique em Avançar até chegar ao final da operação do assistente. Nós não precisaremos de manipuladores de eventos adicionais para o teste, portanto, você poderá deixar todas as caixas de seleção vazias. Clique em Concluir para criar o novo modelo do EA:
//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.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"
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Função Tick do Expert                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

O teste é o mais simples possível, portanto não há necessidade de reinventar a roda. Então, nós simplesmente incluímos a classe abstrata COrder e a classe CArrayObj da biblioteca padrão e criamos a lista de objetos:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.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\Objects\Order.mqh>
#include <Arrays\ArrayObj.mqh>
//--- variáveis globais
CArrayObj      list_all_orders;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+

Em seguida, defina flag da lista ordenada para a lista de objetos em OnInit() e preencha com todo o histórico negócios e ordens em dois loops.

Em seguida, exiba os dados em cada um dos objetos da lista preenchida na aba Experts do terminal no loop:

//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   list_all_orders.Sort();
   list_all_orders.Clear();
   if(!HistorySelect(0,TimeCurrent()))
     {
      Print(DFUN,TextByLanguage(": Не удалось получить историю сделок и ордеров",": Failed to get history of deals and orders"));
      return INIT_FAILED;
     }
//--- Negócios
   int total_deals=HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(i);
      if(deal_ticket==0) continue;
      COrder *deal=new COrder(ORDER_STATUS_DEAL,deal_ticket);
      if(deal==NULL) continue;
      list_all_orders.InsertSort(deal);
     }
//--- Ordens
   int total_orders=HistoryOrdersTotal();
   for(int i=0; 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)
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_ORDER,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
      else
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_PENDING,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
     }
//--- Exibe na aba Expert
   int total=list_all_orders.Total();
   for(int i=0;i<total;i++)
     {
      COrder *order=list_all_orders.At(i);
      if(order==NULL)
         continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Se agora nós tentarmos compilar o EA, nós obtemos três erros:


Os erros indicam que nós não podemos acessar os métodos protegidos da classe a partir do lado externo. Afinal de contas, nós fizemos o construtor COrder na seção protegida da classe. Quando nós usamos a classe como base para desenvolver os objetos por tipos, tudo ficará bem, pois eles terão construtores abertos. Agora, a fim de realizar o teste, vamos simplesmente mover o construtor para a seção pública da classe COrder:


Agora tudo é compilado sem erros, e os dados de todas as ordens e negócios no histórico da conta de negociação são exibidos no diário do terminal.


Todas as propriedades de cada ordem/negócio, incluindo as não suportadas, são exibidas.


O fato é que nós desenvolvemos os métodos que retornam as flags para suportar as propriedades específicas por essa ordem para serem virtuais, para que sejam redefinidos nas classes derivadas. Essas classes derivadas são usadas para exibir os dados no diário. Nesse caso, todos devem ser exibidos corretamente. Se houver uma propriedade não suportada pela ordem, ela não será exibida no diário, uma vez que o método da classe COrder Print(const bool full_prop=false) tem a flag padrão para desativar a exibição das propriedades não suportadas no diário, enquanto os métodos virtuais SupportProperty() da classe simplesmente retornam 'true' para qualquer propriedade.

Qual é o próximo?

A primeira (e a menor) parte está pronta. Nós desenvolvemos um objeto básico para a coleta do histórico de ordens e negócios, bem como para a coleta de ordens e posições do mercado. Até agora não há valor prático, mas este é apenas o começo. Esse único objeto básico é tornar-se um elemento fundamental para o sistema armazenar e exibir os dados no sistema de ordens. Nosso próximo passo é desenvolver outros objetos e coleções necessárias usando os mesmos princípios. Eu também vou automatizar a coleta dos dados necessários de maneira constante.


Na próxima parte, eu vou desenvolver várias classes com base nessa classe abstrata. As classes descreverão os tipos específicos das ordens, negócios e posições. Eu desenvolverei a primeira coleção (coleção do histórico de ordens e negócios), bem como o objeto base e o principal da biblioteca — Engine. Eu também vou me livrar dos erros ao tentar compilar a biblioteca em MQL4 nas partes subsequentes da descrição do desenvolvimento da biblioteca.

Abaixo está um arquivo com todos os arquivos discutidos na primeira parte. Você pode baixar e analisá-los em detalhes. Deixe suas perguntas, comentários e sugestões nos comentários.

Por favor, note que a biblioteca está sendo desenvolvida em paralelo com a redação dos artigos. Portanto, as revisões necessárias são feitas para os métodos e variáveis da biblioteca, de artigo para artigo. A este respeito, pode haver pequenas inconsistências no conteúdo dos arquivos anexados e as descrições do artigo. Por exemplo, pode haver edições nos comentários, rearranjos dos membros da enumeração, etc. Isso não afeta o desempenho dos exemplos anexados.

Voltar ao conteúdo.


Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/5654

Arquivos anexados |
MQL5.zip (16.19 KB)
Criando um EA gradador multiplataforma Criando um EA gradador multiplataforma

Neste artigo, aprenderemos como escrever EAs que funcionam tanto no MetaTrader 4 quanto no MetaTrader 5. Para fazer isso, tentaremos escrever um que trabalhe com o princípio de criação de grades de ordens. Um gradador é um Expert Advisor cujo trabalho fundamental consiste em colocar simultaneamente e na mesma quantidade ordens limitadas tanto acima como abaixo do preço atual.

Colorindo os resultados da otimização de estratégias de negociação Colorindo os resultados da otimização de estratégias de negociação

Neste artigo nós vamos realizar um experimento: nós vamos colorir os resultados da otimização. A cor é determinada por três parâmetros: os níveis de vermelho, verde e azul (RGB). Existem outros métodos de codificação de cores, que também usam três parâmetros. Assim, três parâmetros de teste podem ser convertidos em uma cor, que representa visualmente os valores. Leia este artigo para descobrir se essa representação pode ser útil.

Extraindo dados estruturados de páginas HTML através de seletores CSS Extraindo dados estruturados de páginas HTML através de seletores CSS

O artigo descreve um método universal para analisar e converter dados de documentos HTML com base em seletores CSS. Em MQL estão disponíveis relatórios de negociação e de teste, calendários econômicos, sinais públicos e monitoramento de contas, fontes de cotações on-line adicionais.

Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte II). Coleção do histórico de ordens e negócios Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte II). Coleção do histórico de ordens e negócios

Na primeira parte, nós começamos a criar uma grande biblioteca multi-plataforma, simplificando o desenvolvimento de programas para as plataformas MetaTrader 5 e MetaTrader 4. Nós criamos o objeto abstrato COrder, que é um objeto base para o armazenamento de dados do histórico de ordens e negócios, bem como as ordens à mercado e posições. Agora nós vamos desenvolver todos os objetos necessários para o armazenamento de dados do histórico da conta em coleções.