English Русский 中文 Español Deutsch 日本語
MQL5 Cookbook: Implementando seu próprio Depth of Market (Book de Ofertas)

MQL5 Cookbook: Implementando seu próprio Depth of Market (Book de Ofertas)

MetaTrader 5Exemplos | 7 março 2016, 09:11
17 829 10
Vasiliy Sokolov
Vasiliy Sokolov

Índice



Introdução

A linguagem MQL5 está em constante evolução e oferece mais oportunidades para operações com a troca de informações a cada ano. Um desses tipos de dados de troca são informações sobre Depth of Market. É uma tabela especial que mostra os níveis de preços e volumes de ordens limitadas. O terminal MetaTrader 5 tem uma versão do Depth Mercado para a exibição de ordens limitadas, mas que nem sempre é suficiente, pois o seu Expert Advisor tem que ter um acesso simples e conveniente para o Depth of Market. Certamente, a linguagem MQL5 tem poucos recursos especiais para trabalhar com essas informações, mas são características de baixo nível que exigem cálculos matemáticos adicionais.

No entanto, todos os cálculos intermédios podem ser evitado. Tudo que você tem a fazer é escrever uma classe especial para trabalhar com Depth of Market. Todos os cálculos complexos serão realizados dentro do Depth of Market e da própria classe, que fornece formas convenientes para a operação com preços e níveis do DOM. Esta classe permite uma criação eficiente e simples do painel na forma de um indicador, que reflete imediatamente o estado atual de preços do Depth of Market:

Fig. 1. Depth of Market exibido como um painel

Este artigo demonsta aos usuários como utilizar o Depth of Market (DOM) de forma programática e descreve o princípio de funcionamento da classe MarketBook , que pode expandir a biblioteca padrão de classes do MQL5 e oferecer métodos convenientes de como usar o DOM.

Depois de ler o primeiro capítulo deste artigo, ficará claro que o Depth of Market regular oferecido pelo MetaTrader 5 tem capacidades impressionantes. Não vamos tentar duplicar todas essas múltiplas oportunidades no nosso indicador, assim como sua tarefa será completamente diferente. Com um exemplo prático de como criar facilmente um painel de negociação no Depth of Market, vamos mostrar que os princípios da programação orientada a objetos permitem a manipulação de estruturas de dados complexos de forma relativamente fácil. Vamos garantir que não vai ser difícil acessar o Depth of Market diretamente do seu Expert Advisor com MQL5 e, consequentemente, para visualizar a sua representação, pois é conveniente para nós.

 

Nota do Tradutor: No Brasil o Livro de Ofertas faz o papel do DOM (Profundidade do Mercado) e registra todas as ordens por nível de preço.


Capítulo 1. Depth of Market padrão no MetaTrader 5 e como usar os métodos


1.1. Depth of Market padrão no MetaTrader 5

MetaTrader 5 suporta negociação em bolsas centralizadas e fornece ferramentas padrão para a operação com o Depth of Market. Antes de mais nada, certamente é uma tabela de ordens limitadas, que recentemente foi atualizado com um modo avançado de representação da tabela de ordens limitadas. Para abrir o Depth of Market, é necessário conectar-se a uma das bolsas que suporta o MetaTrader 5 e selecione no menu de contexto "Visualizar" --> "Livro de Ofertas" --> "Nome do instrumento". Uma janela separada, que combina uma tabela de preços tick e uma tabela de ordens limitadas vai aparecer:

Fig. 2. Depth of Market padrão no MetaTrader 5

Depth of Market padrão no MetaTrader 5 pode se tornar numa ferramenta excepcional. Em particular, ela permite exibir o seguinte:

  • Ordens limitadas de Compra e Venda (Buy Limit e Sell Limit), os seus níveis de preços e de volume (formulário padrão do DOM clássico);
  • Níveis de spread e preços atuais ocupados por ordens limitadas (modo avançado);
  • gráfico de tick e volumes visualizados de Bid, Ask e das últimas negociações;
  • nível total de Compra e de Venda (exibido como duas linhas no gráfico de tick superior e inferior, respectivamente).

A lista fornecida confirma que as características do Depth of Market são mais do que impressionantes. Vamos descobrir como operar com os dados, obtendo acesso a eles por meio de programação. Primeiro de tudo, você precisa ter uma idéia sobre como funciona o Depth of Market estabelecido e qual é a chave para a sua organização de dados. Para mais informações, leia o artigo " PRINCÍPIOS DA PRECIFICAÇÃO DA BOLSA TOMANDO COMO EXEMPLO O MERCADO DE DERIVATIVOS DA BOLSA DE MOSCOU (MOEX) " no capítulo " 1.3. Relação entre Vendedores e Compradores. Livro de Ofertas da Bolsa de Valores. ". Nós não vamos passar muito tempo com esta descrição na tabela, assumindo que o leitor ja tenha o conhecimento suficiente sobre assunto.

 

1.2. Modelo de eventos para trabalhar com o Depth of Market

O Depth of Market tem uma tabela de dados muito dinâmica. Em mercados dinâmicos, movimentos rápidos da tabela de ordens limitadas mudam dezenas de vezes por segundo. Portanto, você deve tentar processar uma única informação que é realmente necessária para o processamento, caso contrário, a quantidade de dados transferidos e a carga do processador central para o processamento de dados pode ultrapassar todos os limites razoáveis. Esta é a razão pela qual MetaTrader 5 exige um modelo de evento especial que impede a aquisição e tratamento de dados que não serão realmente utilizados. Vamos ter um exame aprofundado desse modelo. 

Qualquer evento que ocorra no mercado, tais como a chegada de um novo tick ou a execução de uma transação comercial, pode ser processada chamando a função correspondente associado a ela. Por exemplo, com a chegada de um novo tick no MQL5, uma função especial OnTick() do manipulador de eventos é chamado. Redimensionar o gráfico ou a sua posição chama-se função OnChartEvent(). Este modelo de evento também se aplica as mudanças do Depth of Market. Por exemplo, se alguém coloca ordens limitadas de Venda ou Compra (Sell Limit ou Buy Limit) no Depth of Market (Livro de Ofertas), seu status mudará e chamará a função especial OnBookEvent().

Uma vez que existem dezenas ou mesmo centenas de símbolos diferentes disponíveis no terminal com o seu próprio Depth of Market, o número de chamadas da função OnBookEvent e o recurso pode ser enorme e intensivo. A fim de evitar isso, o terminal deve ser notificado previamente ao executar um indicador ou um Expert Advisor a partir do qual os instrumentos em particular são necessários para obter informações sobre cotações de segundo nível (informações fornecidas pelo Depth of Market também são chamadas desta forma). Uma função especial do sistema MarketBookAdd é usado para esses fins. Por exemplo, se quisermos obter informações sobre o Depth of Marked com base no instrumento Si-9,15 (contrato de futuros para USD/RUR com vencimento em setembro de 2015), nós precisamos escrever o seguinte código no nosso Expert Advisor ou um indicador da função OnInit:

void OnInit()
{
   MarketBookAdd("Si-9.15");
}

Com esta função, nos criamos a chamada "assinatura" e notificamos o terminal que o Expert Advisor ou indicador tem de ser informado no caso de mudança do Depth of Market com base no instrumento Si-9.15. As mudanças do Depth of Market para outros instrumentos não estará disponível para nós, o que reduzirá consideravelmente os recursos consumidos pelo programa.

A função oposta da função MarketBookAdd é o MarketBookRelease. Ao contrário, ela desfaz a ssinatura a partir das notificações de mudanças do Depth of Market. É aconselhável para um programador cancelar a inscrição na seção OnDeinit, fechando assim o acesso aos dados para fechar o Expert Advisor ou o indicador:

void OnDeinit(const int reason)
{
   MarketBookRelease("Si-9.15");
}

A função chamada MarketBookAdd implica, basicamente, num instrumento obrigatório no momento da mudança do Depth of Market, um evento especial de manipulação da função OnBookEvent() será chamada. Desta forma, um simples Expert Advisor ou um indicador operando com o Depth of Market irá conter três funções do sistema:

  • OnInit - Expert Advisor ou função de inicialização do indicador para se inscrever e receber uma atualização em caso de mudança do DOM para o instrumento necessário.
  • Oninit - desinicialização do Expert Advisor ou a função de indicador para cancelar a assinatura de recebimento de informações no evento do Depth of Market ao instrumento requerido.
  • OnBookEvent - função chamada após uma alteração no Depth of Market para sinalizar que o DOM mudou.

Nosso simples e primeiro Expert Advisor conterá essas três funções. Como visto no exemplo abaixo, para cada mudança do Depth of Market, uma mensagem sera exibída: "Depth of Market para Si-9.15 foi mudado" :

//+------------------------------------------------------------------+
//|                                                       Expert.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Inicializaçao da função Expert                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MarketBookAdd("Si-9.15");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Desinicialização da função Expert                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   MarketBookRelease("Si-9.15");
  }
//+------------------------------------------------------------------+
//| Função BookEvent                                                 |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---
   printf("Depth of Market for " + symbol +  " was changed"); 
  }
//+------------------------------------------------------------------+

Instruções chave no código são destacadas com o marcador amarelo.

 

1.3. Recebendo as cotações do segundo nível com funções MarketBookGet e estrutura MqlBookInfo

Agora que aprendemos a receber notificações para as mudanças do Depth of Market, é hora de aprender como acessar as informações do DOM usando a função MarketBookGet. Vamos examinar o seu protótipo e uso.

Como já mencionado, o Depth of Market é apresentado com uma tabela especial que consiste em duas partes para exibir ordens limitadas de Compra e Venda (Buy Limit e Sell Limit). Como qualquer outra tabela, ele é a maneira mais fácil de exibir Depth of Market sob a forma de array, onde o índice do array é o número da linha da tabela, e o valor do array é uma certa linha ou seqüência de dados que inclui o tipo de volume, preço e aplicação. Imaginemos também o Depth of Market como uma tabela que mostra o índice de cada linha:

Índice da linha Tipo de ordem Volume Preço
0 Sell Limit 18 56844
1  Sell Limit  1  56843
2  Sell Limit  21  56842
3  Buy Limit  9  56836
4  Buy Limit  5  56835
5  Buy Limit  15  56834

 Tabela 1. Depth of Market apresentados numa tabela

Para torná-lo mais fácil de navegar através da tabela, Ordens de Venda são marcadas com cor de rosa, e Ordens de Compra com cores azuis. A tabela do Depth of Market é basicamente um array bidimensional. A primeira dimensão indica um número de linha e a segunda dimensão - um dos três fatores da tabela (tipo de ordem - 0, volume de encomendas - 1 e preço ordem - 2). No entanto, para evitar o trabalho com arrays multidimensionais, uma estrutura especial MqlBookInfo é usada no MQL5. Ele inclui todos os valores necessarios. Desta forma, o índice do Depth of Market contém uma estrutura MqlBookInfo, que por sua vez carrega informações sobre o tipo de ordem, o seu volume e preço. Vamos dar uma definição a essa estrutura:

struct MqlBookInfo
  {
   ENUM_BOOK_TYPE   type;       // tipo de ordem a partir da enumeração ENUM_BOOK_TYPE
   double           price;      // ordem de preço
   long             volume;     // ordem de volume
  };

Agora, o método de trabalho com Depth of Market deve ser claro para nós. A função MarketBookGet retorna um array de estruturas MqlBookInfo. O índice do array indica a linha da tabela de preços, e a estrutura do índice contém informações sobre volume, preço e tipo de ordem. Sabendo disso, vamos tentar obter acesso a primeira ordem do DOM, e por esta razão vamos modificar um pouco a função OnBookEvent do nosso Expert Advisor a partir do exemplo anterior:

//+------------------------------------------------------------------+
//| Função BookEvent                                                 |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---
   //printf("Depth of Market " + symbol +  " changed"); 
   MqlBookInfo book[];
   MarketBookGet(symbol, book);
   if(ArraySize(book) == 0)
   {
      printf("Failed load market book price. Reason: " + (string)GetLastError());
      return;
   }
   string line = "Price: " + DoubleToString(book[0].price, Digits()) + "; ";
   line += "Volume: " + (string)book[0].volume + "; ";
   line += "Type: " + EnumToString(book[0].type);
   printf(line);
  }

Quando iniciar um Expert Advisor em qualquer um dos gráficos, nos iremos receber relatórios da primeira tabela do DOM e seus parâmetros:

2015.06.05 15:54:17.189 Expert (Si-9.15,H1)     Price: 56464; Volume: 56; Type: BOOK_TYPE_SELL
2015.06.05 15:54:17.078 Expert (Si-9.15,H1)     Price: 56464; Volume: 56; Type: BOOK_TYPE_SELL
2015.06.05 15:54:17.061 Expert (Si-9.15,H1)     Price: 56464; Volume: 56; Type: BOOK_TYPE_SELL
...

Olhando para o nosso quadro anterior, é fácil adivinhar que o nível disponível para o índice zero do DOM corresponde ao pior preço Ask (BOOK_TYPE_SELL). E ao contrário, o preço Bid mais baixo leva o último índice no array obtido. Os melhores preços Ask e Bid estão posicionados aproximadamente no meio do Depth of Market. A primeira desvantagem dos preços obtidos é que o cálculo no Depth of Market normalmente é realizado utilizando os melhores preços que são normalmente colocados no meio da tabela. Os menores preços Ask e Bid tem uma importância secundária. No futuro, ao analisar a classe CMarketBook resolveremos este problema, fornecendo indexadores específicos convenientes para trabalhar com o Depth of Market.

 


Capítulo 2. Facil acesso da classe CMarketBook e operação com o Depth of Market


2.1. Montando a classe CMarketInfoBook

No primeiro capítulo, me familiarizei com as funções do sistema para operar com o Depth of Markete descobri as características específicas do modelo de evento para organizar o acesso às cotações de segundo nível. Neste capítulo vamos criar uma classe especial CMarketBook para um uso conveniente de um Depth of Market padrão. Com base no conhecimento adquirido a partir do primeiro capítulo, podemos discutir as propriedades que a nossa classe deve ter para trabalhar com estes tipos de dados.

Então a primeira coisa que deve ser considerada na concepção desta classe é uma intensidade de recursos dos dados recebidos. O Depth of Market pode ser atualizado dezenas de vezes por segundo, além disso, ele contém dezenas de elementos do tipo MqlBookInfo. Portanto, a nossa classe deve trabalhar inicialmente apenas com um único instrumento. Ao processar vários Depth of Market de diferentes instrumentos, é necessário criar várias cópias de nossa classe com uma indicação de um instrumento específico:

CMarketBook("Si-9.15");            // Depth of Market para Si-9.15
CMarketBook("ED-9.15");            // Depth of Market para ED-9.15
CMarketBook("SBRF-9.15");          // Depth of Market para SBRF-9.15
CMarketBook("GAZP-9.15");          // Depth of Market para GAZP-9.15

O segundo aspecto que precisamos tomar cuidado é a organização do acesso fácil aos dados. Uma vez que as ordens limitadas geram um fluxo de níveis de preços de alta freqüência, é impossível copiar o Depth of Market para uma tabela orientada a um objeto seguro. Portanto, a nossa classe irá fornecer um acesso direto, embora não tão seguro, o array MqlBookInfo fornecido pela função do sistema MarketBookGet. Por exemplo, para acessar um índice zero do DOM com a nossa classe, deve ser escrito o seguinte:

CMarketBook BookOnSi("Si-9.15");
...
//+------------------------------------------------------------------+
//| BookEvent function                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---
   MqlBookInfo info = BookOnSi.MarketBook[0];
  }
//+------------------------------------------------------------------+

MarketBook é um array obtido diretamente da função MarketBookGet. No entanto, a vantagem de usar a nossa classe será baseada principalmente no fato de que, além do acesso direto a uma série de ordens limitadas, a nossa classe permitirá o acesso especificamente ao Depth of Market mais usado. Por exemplo, para obter o melhor preço Ask, é necessário apenas escrever o seguinte:

 duplo  best_ask = BookOnSi.InfoGetDouble (MBOOK_BEST_ASK_PRICE);

É mais conveniente do que calcular o melhor índice Ask em seu expert e, em seguida, receber o valor do preço com base neste índice. A partir do código acima, é óbvio que o CMarketBook, bem como muitas outras funções do sistema MQL5, tipo a SymbolInfoDouble ou a OrderHistoryInteger, use seu próprio conjunto de modificadores e os métodos InfoGetInteger e InfoGetDouble para acessar valores inteiros e duplas, respectivamente. Para obter as propriedades necessárias devemos especificar um determinado modificador desta propriedade. Vamos descrever minuciosamente os modificadores dessas propriedades:

//+------------------------------------------------------------------+
//| Especifica modificadores para propriedades do tipo integer       |
//| do DOM.                                                          |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_INFO_INTEGER
{
   MBOOK_BEST_ASK_INDEX,         // Índice do melhor preço Ask
   MBOOK_BEST_BID_INDEX,         // Índice do melhor preço Bid
   MBOOK_LAST_ASK_INDEX,         // Índice do pior preço Ask
   MBOOK_LAST_BID_INDEX,         // Índice do pior preço Bid
   MBOOK_DEPTH_ASK,              // Número dos niveis de vendas
   MBOOK_DEPTH_BID,              // Número dos niveis de compra
   MBOOK_DEPTH_TOTAL             // Número total dos níveis de DOM
};
//+------------------------------------------------------------------+
//| Especifica modificadores de propriedades do tipo de double       |
//| de DOM.                                                          |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_INFO_DOUBLE
{
   MBOOK_BEST_ASK_PRICE,         // Melhor preço Ask
   MBOOK_BEST_BID_PRICE,         // Melhor preço Bid
   MBOOK_LAST_ASK_PRICE,         // Pior preço Ask
   MBOOK_LAST_BID_PRICE,         // Pior preço Bid
   MBOOK_AVERAGE_SPREAD          // Spread médio entre Ask e Bid
};

Certamente, além dos métodos do grupo InfoGet..., a nossa classe contem o método Refresh ativando a atualização do Depth of Market. Devido ao fato de que a nossa classe requer a atualização de informações chamando o método Refresh(), usaremos uma atualização de recurso de classe intensiva apenas quando for necessário.

 

2.2. Cálculo do índice mais utilizado dos níveis do Depth of Market

A classe CMarketBook cobre o array MqlBookInfo. Seu principal objetivo é fornecer o acesso rápido e eficaz para a informação mais solicitada a partir deste array. Consequentemente, há apenas duas operações de uso intensivo de recursos básicos ativado pela classe:

  • cópia do array MqlBookInfo com a função do sistema MarketBookGet;
  • cálculo do índice de preços mais usados.

Nós não podemos acelerar a operação de sua função do sistema MarketBookGet, mas não é necessário, uma vez que todas as funções do sistema de linguagem MQL5 tem a otimização máxima. Mas nós podemos produzir cálculos mais rápido dos índices necessários. Mais uma vez, vamos nos referir aos modificadores de propriedade ENUM MBOOK_INFO_INTEGER e ENUM_MBOOK_INFO_DOUBLE. Como você pode ver, quase todas as propriedades disponíveis baseiam-se no cálculo de quatro índices:

  • Índice de melhor preço Ask; 
  • Índice de melhor preço Bid;
  • Índice de melhor preço Bid;
  • Índice de melhor preço Ask.

Também três propriedades de valores inteiros são utilizados:

  • número de níveis de preços de Venda ou o Depth of Market para Venda (profundidade Ask);
  • número de níveis de preços de Compra ou o Depth of Market para Compra (profundidade Bid);
  • total do Depth of Market igual ao número total de elementos em DOM.

É óbvio que o índice do pior preço Ask irá sempre ser igual a zero, uma vez que o array produzido usando a função MarketBookGet começa com o pior preço Ask. Encontrar o pior índice de preço Bid é trivial - ele sempre será obtido do último índice no array MqlInfoBook (gostaríamos de lembrá-lo, o último índice de elemento no array é menor que o número total dos elementos desse array por unidade):

Indice de pior preço Ask = 0

Índice de pior preço Bid = número total de elementos no Depth of Market - 1

Os índices de propriedades com valores inteiros também são fáceis de calcular. Assim, o Depth of Market total é sempre igual ao número de elementos no array MqlBookInfo. Depth of Market do lado Ask é o seguinte:

Profundidade Ask = índice de melhor preço Ask - índice de pior preço Ask + 1

Nós sempre adicionamos mais um, como a numeração da série começa com zero e para determinar o número de elementos, é necessário adicionar mais um para ter o melhor índice. No entanto, somando-se ao índice de melhor preço Ask, nós obteteremos o índice de melhor preço Bid. Por exemplo, se adicionarmos um a linha como o segundo número na Tabela 1, vamos mover a partir da ordem limitada de Venda (Sell Limit) ao preço de 56 842 para a melhor ordem limitada de Compra (Buy Limit) ao preço de 56 836. Também descobrirmos que o índice de pior preço Ask é sempre zero. Portanto, podemos reduzir a fórmula para calcular a profundidade Ask de acordo com:

Profundidade Ask = índice de melhor preço Bid

Cálculo da profundidade Bid é um pouco diferente. É óbvio que o número de ordens de Compra é igual ao número total de ordens, menos o número de ordens de Venda ou profundidade Ask. Uma vez que na fórmula anterior aprendemos que a profundidade Ask é igual ao índice do melhor preço Bid, não será difícil fazer a fórmula para determinar o número de ordens de compra ou a profundidade Bid:

Profundidade Bid = Depth of Market total - Índice de melhor preço Ask

O Depth of Market total é sempre igual ao total da profundidade Bid e Ask, e portanto, é igual ao número total de elementos do Depth of Market:

Depth of Market total = número total de elementos do Depth of Market

Analiticamente, nós encontramos quase todos os índices usados com freqüência. Usando as reduções matemáticas, substituímos o cálculo do índice com a indexação direta. Isto é muito importante na classe CMarketBook, uma vez que é necessário acessar o mais rápido possível as propriedades e os índices do Depth of Market.

Além dos índices reais, muitas vezes é necessário saber qual o nível de spread médio para usar o instrumento atual. Spread é a diferença entre os melhores preços Bid e Ask. A classe CMarketBook permite obter o valor médio deste parâmetro usando o método de Informações GetDouble chamando o modificador MBOOK_AVERAGE_SPREAD. CMarketBook calcula o spread atual no método Refresh, bem como o seu valor médio pela memorização do número que chama este método.

No entanto, ainda não encontramos os principais índices dos melhores preços Bid e Ask, por isso, passemos para a próxima seção.

 

2.3. Prevendo índices dos melhores preços Bid e Ask com base em valores anteriores destes índices

O cálculo dos melhores preços Ask e Bid é uma tarefa mais difícil que ainda precisamos aprimorar. Por exemplo, na Tabela 1 o índice do melhor preço Ask será indexado com o número 2 e o índice do melhor preço Bid será o índice número 3. O Depth of Market simplificado que consiste em apenas 6 níveis, é apresentado nesta tabela. Na verdade, o Depth of Market pode ser consideravelmente maior e conter até 64 níveis de Venda e Compra. Este é um valor significativo, considerando que a atualização do DOM pode ocorrer várias vezes por segundo.

A solução mais fácil aqui seria a utilização do método "divisão em dois". De fato, se tomarmos o número total de níveis na Tabela 1, que é 6, e dividi-los em dois, o valor obtido (3) é o índice do melhor preço Bid. Portanto, o seu índice anterior é o índice do melhor preço Ask (2). No entanto, este método apenas funciona se o número de níveis de Venda é igual ao número de níveis de Compra do DOM. Isto ocorre normalmente nos mercados líquidos, no entanto, sem liquidez o Depth of Market pode ser parcialmente cheio e num dos lados pode não haver todos os níveis.

Nossa classe CMarketBook tem que funcionar com qualquer mercado e com qualquer Depth of Market, de modo que o método de dividir em dois não seja adequado para nós. Para ilustrar a situação em que a divisão em dois método pode não funcionar, nós nos referimos a figura a seguir:

Fig. 3. O número de níveis de preço Bid nem sempre é igual ao número de níveis de preços Ask

Dois Depth of Market são mostrados na Figura 3. O primeiro deles é um contrato futuro do DOM para títulos de empréstimos federais de dois anos (OFZ2-9.15). O segundo é um contrato futuro de EUR/USD (ED-9.15). Isso mostra que para OFZ2-9.15 o número de níveis de preços de Compra é quatro, enquanto o número de níveis de preço de Venda é oito. Em um mercado mais líquido ED-9.15, a quantidade de ambos os níveis de Compra e Venda é de 12 para cada uma das partes. No caso do ED-9.15, o método de determinação dos índices pela dividisão por dois, teria funcionado, mas com o OFZ2 não funcionaria.

Uma forma mais confiável para encontrar o índice seria usar a iteração DOM até a primeira recorrência da ordem, com o tipo BOOK_TYPE_BUY. O índice anterior se tornaria automaticamente o índice do melhor preço Ask. Este é o método que tem uma classe CMarketBook. Nos referimos a ela através da ilustração acima:

void CMarketBook::SetBestAskAndBidIndex(void)
{
   if(!FindBestBid())
   {
      //Encontrar o melhor Ask através de uma pesquisa completa
      int bookSize = ArraySize(MarketBook);   
      for(int i = 0; i < bookSize; i++)
      {
         if((MarketBook[i].type == BOOK_TYPE_BUY) || (MarketBook[i].type == BOOK_TYPE_BUY_MARKET))
         {
            m_best_ask_index = i-1;
            FindBestBid();
            break;
         }
      }
   }
}

O principal objetivo deste método reside na iteração do DOM com o operador. Uma vez que uma primeira ordem do tipo BOOK_TYPE_BUY é encontrado no Depth of Market, os índices dos melhores preços Bid e Ask são fixados e a iteração é interrompida. A iteração completa do Depth of Market para cada atualização seria uma solução extremamente intensiva.

Em vez de ter a iteração para cada atualização, existe uma opção para lembrar os índices obtidos antes dos melhores preços Bid e Ask. De fato, o Depth of Market normalmente contém uma quantidade fixa de níveis de Compra e Venda. Portanto, não é necessária a iteração do Depth of Market todas as vezes a fim de encontrar novos índices. É o suficiente a referência aos índices encontrados anteriormente e entender se eles ainda são os índices dos melhores preços Bid e Ask. O método privado FindBestBid é usado para resolver tal problema. Vamos ver seu conteúdo:

//+------------------------------------------------------------------+
//| Achar rapidamente o melhor bid pelo melhor ask                   |
//+------------------------------------------------------------------+
bool CMarketBook::FindBestBid(void)
{
   m_best_bid_index = -1;
   bool isBestAsk = m_best_ask_index >= 0 && m_best_ask_index < m_depth_total &&
                    (MarketBook[m_best_ask_index].type == BOOK_TYPE_SELL ||
                    MarketBook[m_best_ask_index].type == BOOK_TYPE_SELL_MARKET);
   if(!isBestAsk)return false;
   int bestBid = m_best_ask_index+1;
   bool isBestBid = bestBid >= 0 && bestBid < m_depth_total &&
                    (MarketBook[bestBid].type == BOOK_TYPE_BUY ||
                    MarketBook[bestBid].type == BOOK_TYPE_BUY_MARKET);
   if(isBestBid)
   {
      m_best_bid_index = bestBid;
      return true;
   }
   return false;
}

É fácil operá-lo. Em primeiro lugar, o método confirma que o índice atual dos melhores preços Ask ainda está em conformidade com o índice do preço Ask. Em seguida, ele redefine o índice do melhor preço Bid e tenta encontrá-lo novamente, referindo-se ao elemento que segue o índice do melhor preço Ask:

 int  bestBid = m_best_ask_index +  1 ;

Se o elemento encontrado é de fato o melhor índice de preço Bid, então o estado anterior do DOM teve o mesmo número de níveis de Compra e Venda igual ao atual estado. Por causa disso, a iteração do DOM pode ser evitada, uma vez que no método SetBestAskAndBidIndex, o método FindBestBid é chamado antes da iteração. Assim, a iteração do DOM é executada apenas na primeira chamada da função, bem como em caso de alteração do número de níveis de Venda e/ou de Compra.

Embora o código fonte resultante saiu maior e mais complexo do que um simples Depth of Market, na verdade ele vai operar mais rápido. O benefício no desempenho será especialmente visível em grandes DOM de mercados líquidos. Instruções simples para verificar as condições são preenchidas muito rapidamente e a quantidade destas verificações é muito menor do que os loops para o operador. Portanto, o desempenho deste método destinado a encontrar os melhores índices de preços será maior do que a iteração normal.

 

2.4. Determinando o máximo de desvio com o método GetDeviationByVol

Muitas vezes o Depth of Market é usado por traders para determinar a liquidez do mercado atual, ou seja, o Depth of Market é usado como um instrumento adicional para controlar os riscos. Se a liquidez do mercado é baixa, entrar no mercado através de ordens de mercado podem causar um alto desvio. Desvio sempre implica em perdas adicionais que podem ser de valor significativo.

Para evitar tais situações, os métodos adicionais para controlar a entrada no mercado deverão ser utilizados. Para mais informações, por favor leia o artigo "Como proteger seu Expert Advisor Investindo na Bolsa de Moscou". Portanto, não vamos descrever esses métodos em detalhes, mas somente mencionar que ao conseguir acesso ao Depth of Market, podemos estimar o valor de um potencial desvio antes de entrar num mercado. O tamanho de um desvio depende de dois fatores:

  • liquidez corrente de lado Bid (ordens de venda) e o lado Ask (ordens de compra);
  • volume de uma oferta.

Ter acesso ao Depth of Market nos permite ver qual o volume e preços que nossa ordem será executada. Se sabemos o volume da nossa ordem, podemos calcular o preço médio ponderado para a entrada no mercado. A diferença entre este preço e o melhor preço Bid ou Ask (dependendo da direção de entrada) será o nosso desvio.

É impossível calcular a média ponderada do preço de entrada manualmente, uma vez que é necessário produzir um grande volume de cálculos num espaço muito curto de tempo (deixe-me lembrá-lo, que o estado do DOM podem mudar várias vezes por segundo). Assim, é natural que direcione essa tarefa a um Expert Advisor ou indicador.

A classe CMarketBook inclui um método especial para calcular essa característica - GetDeviationByVol. Uma vez que o volume de oferta afeta o tamanho do desvio, é necessário passar o volume esperado para ser executado no mercado com o método. Uma vez que este método utiliza de valores aritméticos inteiros de volumes, como adotado no mercado futuro da Bolsa de Moscou, o método toma o volume como um valor de tipo "long". Além disso, o método tem que saber para qual lado a liquidez do cálculo será realizada, portanto uma enumeração especial ENUM_MBOOK_SIDE é utilizada:

//+------------------------------------------------------------------+
//| Lado do MarketBook.                                              |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_SIDE
{
   MBOOK_ASK,                    // Lado do Ask
   MBOOK_BID                     // Lado do Bid
};

Agora vamos introduzir um código-fonte do método GetDeviationByVol:

//+------------------------------------------------------------------+
//| Obter valor do desvio por volume. Retorna -1.0 se desvio for     |
//| infinito (liquidez insuficiente)                                 |
//+------------------------------------------------------------------+
double CMarketBook::GetDeviationByVol(long vol, ENUM_MBOOK_SIDE side)
{
   int best_ask = InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   int last_ask = InfoGetInteger(MBOOK_LAST_ASK_INDEX); 
   int best_bid = InfoGetInteger(MBOOK_BEST_BID_INDEX);
   int last_bid = InfoGetInteger(MBOOK_LAST_BID_INDEX);
   double avrg_price = 0.0;
   long volume_exe = vol;
   if(side == MBOOK_ASK)
   {
      for(int i = best_ask; i >= last_ask; i--)
      {
         long currVol = MarketBook[i].volume < volume_exe ?
                        MarketBook[i].volume : volume_exe ;   
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe <= 0)break;
      }
   }
   else
   {
      for(int i = best_bid; i <= last_bid; i++)
      {
         long currVol = MarketBook[i].volume < volume_exe ?
                        MarketBook[i].volume : volume_exe ;   
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe <= 0)break;
      }
   }
   if(volume_exe > 0)
      return -1.0;
   avrg_price/= (double)vol;
   double deviation = 0.0;
   if(side == MBOOK_ASK)
      deviation = avrg_price - MarketBook[best_ask].price;
   else
      deviation = MarketBook[best_bid].price - avrg_price;
   return deviation;
}

Como você pode ver, o código tem um volume significativo, mas o princípio de seu cálculo não é complexo. Em primeiro lugar, a iteração do Depth of Market é realizada a partir do melhor para o pior preço. A iteração é executada para o lado relevante de cada direção. Durante a iteração do volume atual, é adicionado ao volume total. Se o volume total é chamado e se corresponde ao volume necessário, então ocorre a saída do loop. Em seguida, o preço médio de entrada para um determinado volume é calculado. E, finalmente, a diferença entre o preço médio de entrada e o melhor preço Bid/Ask são calculados. A diferença absoluta será um desvio estimado.

Este método é necessário para calcular a iteração direta do DOM. Embora seja iterado um em duas partes do DOM, e na maioria das vezes apenas parcialmente, no entanto, esse cálculo precisa de mais tempo do que o cálculo dos índices usados com freqüência do DOM. Portanto, este cálculo é implementado diretamente num método separado e produzido sob demanda, ou seja, apenas nestes casos que isso é necessário para obter essas informações de forma clara.

 

2.5. Exemplos para operar com a classe CMarketBook

Então, nós cobrimos os métodos básicos da classe CMarketBook e está na hora de praticarmos um pouco. Nosso exemplo de teste é bastante simples e compreensível, mesmo para iniciantes na programação. Vamos escrever um Expert Advisor de teste para a execução one-off de saída de informação baseado no DOM. Certamente, um script se encaixaria melhor para esses fins, mas o acesso ao Depth of Market através de um script não é possível e requer um Expert Advisor ou um indicador para que ser usado. O código fonte para o EA é dado abaixo:

//+------------------------------------------------------------------+
//|                                               TestMarketBook.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade\MarketBook.mqh>     // Inclue a classe CMarketBook
CMarketBook Book(Symbol());         // Inicializar a classe com o instrumento atual

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   PrintMbookInfo();
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//| função de início do programa Script                              |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Imprimir Informações MarketBook                                  |
//+------------------------------------------------------------------+
void PrintMbookInfo()
  {
   Book.Refresh();                  // Atualizar os status do Depth of Market.
   /* Obtém Estatísticas Básicas*/
   int total=Book.InfoGetInteger(MBOOK_DEPTH_TOTAL);
   int total_ask = Book.InfoGetInteger(MBOOK_DEPTH_ASK);
   int total_bid = Book.InfoGetInteger(MBOOK_DEPTH_BID);
   int best_ask = Book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   int best_bid = Book.InfoGetInteger(MBOOK_BEST_BID_INDEX);

   printf("DEPTH OF MARKET TOTAL: "+(string)total);
   printf("NÚMERO DE NÍVEIS DOS PREÇOS PARA VENDER: "+(string)total_ask);
   printf("NÚMERO DE NÍVEIS DOS PREÇOS PARA COMPRAR: "+(string)total_bid);
   printf("ÍNDICE DO MELHOR PREÇO ASK: "+(string)best_ask);
   printf("ÍNDICE DO MELHOR PREÇO BID: "+(string)best_bid);
   
   double best_ask_price = Book.InfoGetDouble(MBOOK_BEST_ASK_PRICE);
   double best_bid_price = Book.InfoGetDouble(MBOOK_BEST_BID_PRICE);
   double last_ask = Book.InfoGetDouble(MBOOK_LAST_ASK_PRICE);
   double last_bid = Book.InfoGetDouble(MBOOK_LAST_BID_PRICE);
   double avrg_spread = Book.InfoGetDouble(MBOOK_AVERAGE_SPREAD);
   
   printf("MELHOR PREÇO ASK: " + DoubleToString(best_ask_price, Digits()));
   printf("MELHOR PREÇO BID: " + DoubleToString(best_bid_price, Digits()));
   printf("PIOR PREÇO ASK: " + DoubleToString(last_ask, Digits()));
   printf("PIOR PREÇO BID: " + DoubleToString(last_bid, Digits()));
   printf("SPREAD MÉDIO: " + DoubleToString(avrg_spread, Digits()));
  }
//+------------------------------------------------------------------+

Ao executar este teste do Expert Advisor no gráfico OFZ2 temos o seguinte relatório:

2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   SPREAD MÉDIO: 70
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   PIOR PREÇO BID: 9831
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   PIOR PREÇO ASK: 9999
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   MELHOR PREÇO BID: 9840
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   MELHOR PREÇO ASK: 9910
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   ÍNDICE DO MELHOR PREÇO BID: 7
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   ÍNDICE DO MELHOR PREÇO ASK: 6
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   NÚMERO DE NÍVEIS DOS PREÇOS PARA COMPRAR: 2
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   NÚMERO DE NÍVEIS DOS PREÇOS PARA VENDER: 7
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   DEPTH OF MARKET TOTAL: 9

Vamos comparar o relatório obtido com a captura de tela do DOM para este instrumento:

Fig. 4. Depth of Market para OFZ2 ao executar um relatório teste

Confirmamos que os índices e preços recebidos cumpriram integralmente com o Depth of Market atual. 

 


Capítulo 3. Escrevendo seu próprio Depth of Market como um indicador de painel


3.1. Princípios gerais de construção do painel Depth of Market. Criando um indicador

Com acesso à classe CMarketBook, é relativamente simples criar um painel especial que exibe o Depth of Market atual diretamente no gráfico. Vamos criar o nosso painel com base no indicador do usuário. Foi escolhido um indicador como base devido ao fato que pode haver um número ilimitado de indicadores, enquanto apenas um Expert Advisor pode ser executado em cada gráfico. Se tomarmos um Expert Advisor como base do painel, então não será possível negociarmos com um expert no mesmo gráfico, o que seria inconveniente.

Nós forneceremos o nosso Depth of Market com a habilidade de aparecer e se ocultar no gráfico, pois ele é implementado num painel padrão de negociação para cada gráfico. Nós usaremos o mesmo botão para mostrar ou esconder:

 

Fig. 5. Painel padrão de negociação no MetaTrader 5

Nosso Depth of Market será colocado no canto superior esquerdo do gráfico, no mesmo lugar onde o painel de negociação está localizado. Isto para que ao lançar o indicador Depth of Market e um Expert Advisor no gráfico, não seja bloqueado o respectivo ícone do EA localizado no canto superior direito, por isto colocamos nosso painel na esquerda.

Ao criar um indicador, é necessário usar uma de duas funções do sistema OnCalculate. Já que o nosso painel não usa as informações recebidas a partir dessas funções, vamos deixar estes métodos vazios. Além disso, o indicador não vai usar qualquer série gráfica, então neste caso as propriedade do indicator_plots será igual a zero.

A função do sistema OnBookEvent será a principal função que nosso indicador vai usar, assim teremos que assinar o símbolo gráfico atual, a fim de receber informações sobre as mudanças do Depth of Market. Vamos assinar usando a função MarketBookAdd já conhecidos por nós.

O painel do Depth of Market será implementado na forma de uma classe especial CBookPanel. Agora, sem entrar em mais detalhes sobre essa classe, iremos fornecer o início do código do arquivo do indicador:

//+------------------------------------------------------------------+
//|                                                   MarketBook.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0
#include <Trade\MarketBook.mqh>
#include "MBookPanel.mqh"

CBookPanel Panel;
//+------------------------------------------------------------------+
//| função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   MarketBookAdd(Symbol());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Mudança de evento do MarketBook                                  |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
   Panel.Refresh();
  }
//+------------------------------------------------------------------+
//| Eventos do grafic                                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // identificador de evento  
                  const long& lparam,   // parâmetro de evento de tipo long
                  const double& dparam, // parâmetro de evento do tipo double
                  const string& sparam) // parâmetro de evento do tipo String
  {
   Panel.Event(id,lparam,dparam,sparam);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| indicador personalizado da função de iteração                    |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---

//--- valor de retorno de prev_calculated para a próxima chamada
   return(rates_total);
  }
//+------------------------------------------------------------------+

Agora a classe CBookPanel contém apenas os elementos básicos para seu funcionamento: a seta que você clica para o Depth of Market aparecer e a etiqueta"MarketBook" para assinar o nosso futuro DOM. Ao executar o nosso indicador, o gráfico ficará da seguinte forma:

 

Fig. 6. Localização do futuro painel MarketBook no gráfico

Cada elemento desta classe é também uma classe independente derivada de uma classe básica CNode. Esta classe contém os métodos básicos, como mostrar e ocultar, que pode ser substituído em classes descendentes. A classe CNode também gera um nome exclusivo para cada instância, o que torna mais conveniente usar as funções padrão para a criação de objetos gráficos e definir suas propriedades.

 

3.2. Processamento de eventos com cliques e criando a forma do Depth of Market

Atualmente, nosso indicador não reage ao clicar na seta, por isso vamos continuar o trabalho. A primeira coisa que devemos fazer para o seu painel é inserir o manipulador de eventos OnChartEvent. Vamos chamar esse método de Evento. Levará os parâmetros obtidos para o OnChartEvent. Além disso, vamos estender a classe de base CNode fornecendo o array CArrayObj, que conterá outros elementos gráficos do tipo CNode. No futuro ele vai ajudar a criar muitos elementos do mesmo tipo - células do Depth of Market.

Agora providenciaremos um código-fonte da classe CBookPanel e sua classe parental CNode: 

//+------------------------------------------------------------------+
//|                                                   MBookPanel.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Trade\MarketBook.mqh>
#include "Node.mqh"
#include "MBookText.mqh"
#include "MBookFon.mqh"
//+------------------------------------------------------------------+
//| Classe CBookPanel                                                |
//+------------------------------------------------------------------+
class CBookPanel : CNode
  {
private:
   CMarketBook       m_book;
   bool              m_showed;
   CBookText         m_text;
public:
   CBookPanel();
   ~CBookPanel();
   void              Refresh();
   virtual void Event(int id, long lparam, double dparam, string sparam);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CBookPanel::CBookPanel()
{
   m_elements.Add(new CBookFon(GetPointer(m_book)));
   ObjectCreate(ChartID(), m_name, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XDISTANCE, 70);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, -3);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_COLOR, clrBlack);
   ObjectSetString(ChartID(), m_name, OBJPROP_FONT, "Webdings");
   ObjectSetString(ChartID(), m_name, OBJPROP_TEXT, CharToString(0x36));
}
CBookPanel::~CBookPanel(void)
{
   OnHide();
   m_text.Hide();
   ObjectDelete(ChartID(), m_name);
}

CBookPanel::Refresh(void)
{

}

CBookPanel::Event(int id, long lparam, double dparam, string sparam)
{
   switch(id)
   {
      case CHARTEVENT_OBJECT_CLICK:
      {
         if(sparam != m_name)return;
         if(!m_showed)OnShow();        
         else OnHide();
         m_showed = !m_showed;
      }
   }
}
//+------------------------------------------------------------------+

Método Refresh que faz a atualização do estado do DOM ainda não está completo. Vamos criar um pouco mais tarde. A funcionalidade atual já mostra o primeiro protótipo do nosso Depth of Market. Até agora, ao clicar na seta, apenas a cobertura cinza padrão é exibida. Ao clicar novamente, ele desaparece:

 

Fig. 7. Aparência do futuro Depth of Market

O indicador Depth of Market ainda não parece muito convincente, mas vamos continuar a melhorá-lo.

 

3.3. Células do Depth of Market.

Células para criar a base para Depth of Market. Cada célula é elemento de uma tabela que contém informações sobre volume ou preço. Além disso, as células podem ser diferenciadas por cor: para ordens limitadas de Compra (Buy Limit) está pintado de azul, para ordens limitadas de Venda (Sell Limit) - rosa. O número de células pode ser diferente para cada Depth of Market, portanto, todas as células precisam ser criadas dinâmicamente sob demanda e armazenadas em um recipiente de dados especial CArrayObj. Todas as células, independente do que elas mostram, têm o mesmo tamanho e tipo, a classe que implementa vários tipos de células será o mesmo para todos os outros tipos.

Para as células que apresentam o volume e para as células que apresentam o preço, será usada uma classe especial chamada CBookCeil. O tipo de célula será especificado ao criar um objeto desta classe, de modo que cada instância de classe vai saber que tipo de informação a partir do Depth of Market será necessário mostrar e que cor o deve ser pintado o fundo. CBookCeil usará dois gráficos primitivos: texto da etiqueta OBJ_TEXT_LABEL e a etiqueta retangular OBJ_RECTANGLE. O primeiro irá exibir o texto, o segundo - a atual célula do Depth of Market.

Aqui está um código fonte da classe CBookCeil:

//+------------------------------------------------------------------+
//|                                                   MBookPanel.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include "Node.mqh"
#include <Trade\MarketBook.mqh>
#include "Node.mqh"
#include "MBookText.mqh"

#define BOOK_PRICE 0
#define BOOK_VOLUME 1

class CBookCeil : public CNode
{
private:
   long  m_ydist;
   long  m_xdist;
   int   m_index;
   int m_ceil_type;
   CBookText m_text;
   CMarketBook* m_book;
public:
   CBookCeil(int type, long x_dist, long y_dist, int index_mbook, CMarketBook* book);
   virtual void Show();
   virtual void Hide();
   virtual void Refresh();
   
};

CBookCeil::CBookCeil(int type, long x_dist, long y_dist, int index_mbook, CMarketBook* book)
{
   m_ydist = y_dist;
   m_xdist = x_dist;
   m_index = index_mbook;
   m_book = book;
   m_ceil_type = type;
}

void CBookCeil::Show()
{
   ObjectCreate(ChartID(), m_name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XDISTANCE, m_xdist);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_FONTSIZE, 9);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   m_text.Show();
   m_text.SetXDist(m_xdist+10);
   m_text.SetYDist(m_ydist+2);
   Refresh();
}

void CBookCeil::Refresh(void)
{
   ENUM_BOOK_TYPE type = m_book.MarketBook[m_index].type;
   if(type == BOOK_TYPE_BUY || type == BOOK_TYPE_BUY_MARKET)
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrCornflowerBlue);
   else if(type == BOOK_TYPE_SELL || type == BOOK_TYPE_SELL_MARKET)
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrPink);
   else
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrWhite);
   MqlBookInfo info = m_book.MarketBook[m_index];
   if(m_ceil_type == BOOK_PRICE)
      m_text.SetText(DoubleToString(info.price, Digits()));
   else if(m_ceil_type == BOOK_VOLUME)
      m_text.SetText((string)info.volume);
}

void CBookCeil::Hide(void)
{
   OnHide();
   m_text.Hide();
   ObjectDelete(ChartID(),m_name);
}

A principal operação desta classe é realizada utilizando os métodos Show e Refresh. Este último, dependendo do tipo de célula transmitida, coloque uma cor relevante e apresente o volume ou preço na mesma. Para criar uma célula, você deve especificar o seu tipo, localização no eixo X, localização no eixo Y, índice DOM que corresponde a essa célula, e o Depth of Market de onde o celular irá receber as informações.

Um método especial privado CreateCeils criará células numa classe que implementa um substrato do DOM. Aqui está o código fonte:

void CBookFon::CreateCeils()
{
   int total = m_book.InfoGetInteger(MBOOK_DEPTH_TOTAL);
   for(int i = 0; i < total; i++)
   {
      CBookCeil* Ceil = new CBookCeil(0, 12, i*15+20, i, m_book);
      CBookCeil* CeilVol = new CBookCeil(1, 63, i*15+20, i, m_book);
      m_elements.Add(Ceil);
      m_elements.Add(CeilVol);
      Ceil.Show();
      CeilVol.Show();
   }
}

Ele será chamado clicando na seta que expande o Depth of Market.

Agora tudo está pronto para criar a nossa nova versão do Depth of Market. Depois de fazer alterações e compilar o projeto, o nosso indicador adquiriu uma nova forma:

 

Fig. 8. A primeira versão do Depth of Market como um indicador

3.4. Exibindo o volume do histograma no Depth of Market

O Depth of Market obtido já executa a função básica - que mostra os níveis de negociação, o volume e preços de ordens limitadas de Venda e Compra (Sell Limit e Buy Limit). Assim, a cada mudança de valores do Depth of Market também muda os valores nas células correspondentes. No entanto, visualmente, não é fácil manter o controle dos volumes na tabela obtida. Por exemplo, um volume padrão do DOM dentro do MetaTrader 5 é mostrado no fundo do histograma, que mostra a magnitude relativa do volume atual em relação ao volume máximo do Depth of Market. Além disso, não faria mal implementar funcionalidade semelhante ao nosso Depth of Market.

Existem diferentes formas de resolver este problema. A solução mais fácil seria fazer todos os cálculos necessários diretamente na classe CBookCeil. Portanto é necessário escrever o seguinte no seu método Refresh:

void CBookCeil::Refresh(void)
{
   ...
   MqlBookInfo info = m_book.MarketBook[m_index];
   ...
   //Update Depth of Market histogram
   int begin = m_book.InfoGetInteger(MBOOK_LAST_ASK_INDEX);
   int end = m_book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   long max_volume = 0;
   if(m_ceil_type != BOOK_VOLUME)return;
   for(int i = begin; i < end; i++)
   {
      if(m_book.MarketBook[i].volume > max_volume)
         max_volume = m_book.MarketBook[i].volume;
   }
   double delta = 1.0;
   if(max_volume > 0)
      delta = (info.volume/(double)max_volume);
   long size = (long)(delta * 50.0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XSIZE, size);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
}

No método completo de iteração do DOM existe um volume máximo, então o volume atual é dividido pela máximo. O compartilhamento obtido é multiplicado pela largura máxima de uma célula da tabela do volume (isto é representado por uma constante de 50 pixels). A largura obtida de uma tela será o histograma necessário:

 

Fig. 9. Depth of Market com volumes de histograma

No entanto, o problema com este código é que a iteração do DOM é feito em cada célula, cada vez que é atualizada. O Depth of Market possui 40 elementos, que significa possuir 800 iterações do loop por cada atualização do Depth of Market. Cada célula itera o Depth of Market apenas no seu próprio lado, portanto a iteração dentro de cada célula é composta por vinte iterações (Depth of Market dividido ao meio). Embora os computadores modernos conseguem lidar com essa tarefa, é um método de operação muito ineficiente, especialmente tendo em conta o fato de que é necessário trabalhar com o Depth of Market usando algoritmos mais rápidos e mais eficientes.

 

3.5. Cálculo rápido dos volumes máximos do Depth of Market, otimização de iteração

Infelizmente, é impossível eliminar uma iteração cheia do Depth of Market. Após cada Depth of Market atualizar, o volume máximo e seu nível de preços pode mudar drasticamente. No entanto, podemos tentar minimizar o número de iterações. Para este efeito, você deve aprender a fazer a iteração do DOM não mais do que uma vez entre duas chamadas de atualização. A segunda coisa que você precisa fazer é minimizar o número de chamadas de iteração completas. Para este efeito, você deve usar o cálculo diferido ou, em outras palavras, realizar este cálculo apenas com a demanda explícita. Vamos transferir todos os cálculos diretamente à classe CMarketBook do Depth of Market e escrever um cálculo especial para a subclasse CBookCalculation localizada no interior do CMarketBook. Por favor, veja o código fonte abaixo: 

class CMarketBook;

class CBookCalculation
{
private:
   int m_max_ask_index;         // Índice do máximo volume Ask
   long m_max_ask_volume;       // Índice do máximo preço Ask
   
   int m_max_bid_index;         // Índice do máximo volume Bid
   long m_max_bid_volume;       // Índice do máximo preço Bid
   
   long m_sum_ask_volume;       // Volume total do preço Ask no DOM
   long m_sum_bid_volume;       // Preço total do preço Bid no DOM
   
   bool m_calculation;          // flag que indica todos os cálculos a serem executados
   CMarketBook* m_book;         // Indicador Depth of market
   
   void Calculation(void)
   {
      // PARA LADO ASK 
      int begin = (int)m_book.InfoGetInteger(MBOOK_LAST_ASK_INDEX);
      int end = (int)m_book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
      for(int i = begin; i < end; i++)
      {
         if(m_book.MarketBook[i].volume > m_max_ask_volume)
         {
            m_max_ask_index = i;
            m_max_ask_volume = m_book.MarketBook[i].volume;
         }
         m_sum_ask_volume += m_book.MarketBook[i].volume;
      }
      // PARA LADO BID
      begin = (int)m_book.InfoGetInteger(MBOOK_BEST_BID_INDEX);
      end = (int)m_book.InfoGetInteger(MBOOK_LAST_BID_INDEX);
      for(int i = begin; i < end; i++)
      {
         if(m_book.MarketBook[i].volume > m_max_bid_volume)
         {
            m_max_bid_index = i;
            m_max_bid_volume = m_book.MarketBook[i].volume;
         }
         m_sum_bid_volume += m_book.MarketBook[i].volume;
      }
      m_calculation = true;
   }
   
public:
   CBookCalculation(CMarketBook* book)
   {
      Reset();
      m_book = book;
   }
   
   void Reset()
   {
      m_max_ask_volume = 0.0;
      m_max_bid_volume = 0.0;
      m_max_ask_index = -1;
      m_max_bid_index = -1;
      m_sum_ask_volume = 0;
      m_sum_bid_volume = 0;
      m_calculation = false;
   }
   int GetMaxVolAskIndex()
   {
      if(!m_calculation)
         Calculation();
      return m_max_ask_index;
   }
   
   long GetMaxVolAsk()
   {
      if(!m_calculation)
         Calculation();
      return m_max_ask_volume;
   }
   int GetMaxVolBidIndex()
   {
      if(!m_calculation)
         Calculation();
      return m_max_bid_index;
   }
   
   long GetMaxVolBid()
   {
      if(!m_calculation)
         Calculation();
      return m_max_bid_volume;
   }
   long GetAskVolTotal()
   {
      if(!m_calculation)
         Calculation();
      return m_sum_ask_volume;
   }
   long GetBidVolTotal()
   {
      if(!m_calculation)
         Calculation();
      return m_sum_bid_volume;
   }
};

Todas as iterações do Depth of Market e cálculos de recursos intensivos estão escondidos dentro do método privado Calculate. Ele é chamado apenas se o cálculo da flag m_calculate é reposto como uma falsa condição. A redefinição da flag acontece apenas no método Reset. Uma vez que esta classe é projetada exclusivamente para operar dentro da classe MarketBook, só esta classe tem acesso a ela.

Depois de atualizar o Depth of Market,o método Refresh da classe CMarketBook redefine o estado do módulo de cálculo chamando o método Reset. Devido a completa iteração do Depth of Market, ele não ocorre mais de uma vez entre as suas duas evoluções. Uma execução pendente tambem é usada. Em outras palavras, o método Calculate de classe CBookCalcultae é chamado apenas quando há um claro apelo de um dos seis métodos disponíveis publicamente.

Além de encontrar o volume, a classe que executa uma iteração completa do Depth of Market teve campos adicionados, contendo a quantidade total de ordens limitadas de Venda e Compra (Sell e Buy Limit). Não é necessário um tempo adicional para calcular estes parâmetros, uma vez que o ciclo total do array é calculado.

Agora, em vez de uma iteração constante do Depth of Market, a iteração inteligente sob demanda é usada. Isso reduz muito os recursos utilizados, tornando a operação com o Depth of Market extremamente eficiente e rápida.

 

3.6. Últimos toques: um histograma de volume e uma linha divisória

Estamos quase concluindo nossa tarefa de criar um indicador. A necessidade prática para encontrar o volume máximo nos ajudou a criar um método eficaz de cálculo econômico dos indicadores necessários. Se, no futuro, quisermos adicionar novos parâmetros calculados para o nosso Depth of Market, será fácil fazê-lo. Para este efeito, bastara estender nossa classe CBookCalculate com métodos relevantes e digitar os modificadores apropriados para as enumerações ENUM_MBOOK_INFO_INTEGER e ENUM_MBOOK_INFO_DOUBLE.

Agora vamos fazer uso do trabalho pronto e reescrever o método Refresh para cada célula:

void CBookCeil::Refresh(void)
{
   ENUM_BOOK_TYPE type = m_book.MarketBook[m_index].type;
   long max_volume = 0;
   if(type == BOOK_TYPE_BUY || type == BOOK_TYPE_BUY_MARKET)
   {
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrCornflowerBlue);
      max_volume = m_book.InfoGetInteger(MBOOK_MAX_BID_VOLUME);
   }
   else if(type == BOOK_TYPE_SELL || type == BOOK_TYPE_SELL_MARKET)
   {
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrPink);
      max_volume = m_book.InfoGetInteger(MBOOK_MAX_ASK_VOLUME); //O volume foi calculado anteriormente, a iteração recorrente não funcionara
   }
   else
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrWhite);
   MqlBookInfo info = m_book.MarketBook[m_index];
   if(m_ceil_type == BOOK_PRICE)
      m_text.SetText(DoubleToString(info.price, Digits()));
   else if(m_ceil_type == BOOK_VOLUME)
      m_text.SetText((string)info.volume);
   if(m_ceil_type != BOOK_VOLUME)return;
   double delta = 1.0;
   if(max_volume > 0)
      delta = (info.volume/(double)max_volume);
   long size = (long)(delta * 50.0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XSIZE, size);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
}

Visualmente nosso painel do indicador funciona da mesma maneira como na versão anterior, mas na realidade, a velocidade de cálculo do histograma tem aumentado significativamente. Isto é o que a arte da programação faz - cria algoritmos eficientes e fáceis de usar, escondendo a complexidade da sua implementação dentro de métodos privados dos módulos correspondentes (classes).

Com o aparecimento do volume de histograma, tornou-se muito vaga a linha divisória entre os volumes dos preços Bid e Ask. Portanto, vamos criar essa linha adicionando na classe CBookPanel uma subclasse especial CBookLine, para implementar esse recurso:

class CBookLine : public CNode
{
private:
   long m_ydist;
public:
   CBookLine(long y){m_ydist = y;}
   virtual void Show()
   {
      ObjectCreate(ChartID(),     m_name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_XDISTANCE, 13);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_YSIZE, 3);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_XSIZE, 108);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_COLOR, clrBlack);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrBlack);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   }
};

Esta é uma classe muito simples que, basicamente, apenas determina a sua posição. A posição da linha no eixo Y deve ser calculada no momento de sua criação no método Show. Sabendo o índice do melhor preço Ask, é relativamente fácil de fazer isso:

long best_bid = m_book.InfoGetInteger(MBOOK_BEST_BID_INDEX);
long y = best_bid*15+19;

Neste caso, o índice da célula best_bid é multiplicado pela largura de cada célula (15 pixels), e uma constante adicional de 19 pixels é adicionada a ela.

Nosso Depth of Market foi finalmente construído com a aparência e funcionalidade mínima para tornar o seu uso uma experiência agradável. Certamente, ainda pode ser feito muito mais. Se desejar, o nosso indicador pode ser feito muito mais próximo em termos de funcionalidade ao Depth of Market padrão do terminal MetaTrader 5.

Mas o principal objetivo deste artigo não é este. O painel Depth of Market foi criado com um único propósito de mostrar as possibilidades da classe CMarketBook. Ele ajudou a fazer esta classe mais rápida, melhor e mais funcional e, portanto, atingindo plenamente o seu objetivo. Vamos mostrar a você um pequeno vídeo, que irá revelar todo o nosso trabalho feito até agora. Abaixo está o nosso painel DOM dinâmico:


3.7. Adicionando as propriedades do DOM com informações sobre o número total de ordens limitadas para o instrumento de negociação

Uma característica distinta da Bolsa de Moscou é uma transmissão de informações sobre o número total de ordens limitadas em tempo real. Este artigo destaca a operação do Depth of Market como tal, sem ser dirigido a qualquer mercado específico. Apesar de ser específica esta informação (peculiar a uma plataforma de negociação específica), está disponível a nível de sistema no terminal. Além disso, expande os dados fornecidos pelo Depth of Market. Assim sendo, decidiu-se extender a enumeração dos modificadores de propriedade e incluir o suporte a essas propriedades diretamente à classe CMarketBook do Depth of Market.

A Bolsa de Valores de Moscou fornece as seguintes informações em tempo real:

  • número de ordens limitadas de Venda (Sell Limit) colocadas pelo instrumento no momento atual;
  • número de todas as ordens limitadas de Compra (Buy Limit) colocadas pelo instrumento no momento atual;
  • volume total de todas as ordens limitadas de Venda (Sell Limit) colocadas pelo instrumento no momento atual;
  • quantidade total de todas as ordens limitadas de Compra (Buy Limit) colocadas pelo instrumento no momento atual;
  • número de posições abertas e contratos em aberto (apenas para os mercados futuros).

O contrato aberto não está diretamente relacionado ao número de ordens limitadas no mercado (ou seja, com a sua liquidez atual), no entanto esta informação é muitas vezes necessária em conjunto com as informações sobre ordens limitadas, de modo que o acesso a este contrato através da classe CMarketBook também parece apropriada. Para acessar a essas informações, você deve usar as funções SymbolInfoInteger e SymbolInfoDouble. Para que os dados sejam acessíveis a partir de um único local, vamos expandir nossa classe Depth of Market através da introdução da enumeração e mudanças nas funções InfoSetInteger e InfoGetDouble:

long CMarketBook::InfoGetInteger(ENUM_MBOOK_INFO_INTEGER property)
{
   switch(property)
   {
      ...
      case MBOOK_BUY_ORDERS:
         return SymbolInfoInteger(m_symbol, SYMBOL_SESSION_BUY_ORDERS);
      case MBOOK_SELL_ORDERS:
         return SymbolInfoInteger(m_symbol, SYMBOL_SESSION_SELL_ORDERS);
      ...
   }
   return 0;
}

 

double CMarketBook::InfoGetDouble(ENUM_MBOOK_INFO_DOUBLE property)
{
   switch(property)
   {
      ...
      case MBOOK_BUY_ORDERS_VOLUME:
         return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_BUY_ORDERS_VOLUME);
      case MBOOK_SELL_ORDERS_VOLUME:
         return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_SELL_ORDERS_VOLUME);
      case MBOOK_OPEN_INTEREST:
         return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_INTEREST);
   }
   return 0.0;  
}

Como você pode ver, o código é bastante simples. Na verdade, ele duplica a funcionalidade padrão MQL. Mas o ponto de adicioná-lo à classe CMarketBook é fornecida aos usuários com um módulo conveniente e centralizado para acesso as informações sobre as ordens limitadas e os seus níveis de preços.



Capítulo 4. Documentação para a classe CMarketBook

Nós completamos a descrição e a criação para a operação da classe CMarketBook do Depth of Market. O quarto capítulo contém a documentação para os seus métodos públicos. Utilizando estes documentos, a operação de classe torna-se simples e direta, mesmo para programadores inexperientes. Além disso, este capítulo é conveniente para ser usado como um pequeno guia para o trabalho com a classe.

4.1. Métodos de obtenção de informações básicas do Depth of Market e sua operação

Metodo Refresh()

Ele atualiza a condição do Depth of Market. Para cada chamada de eventos do sistema OnBookEvent (o Depth of Market mudou), também é necessário chamar esse método.

void        Refresh(void);

Uso

Encontre o exemplo de uso na seção relevante do quarto capítulo.

 

Método InfoGetInteger()

Retorna uma das propriedades do Depth of Market correspondente ao modificador ENUM_MBOOK_INFO_INTEGER. Uma lista completa das funções suportadas pode ser encontrada na descrição ENUM_MBOOK_INFO_INTEGER.

long        InfoGetInteger(ENUM_MBOOK_INFO_INTEGER property);

Valor retornado

Estabelecimento de valores inteiros do Depth of Market do tipo long. Em caso de falha retorna -1.

Uso

Encontre o exemplo de uso na seção relevante do quarto capítulo. 

 

Método InfoGetDouble()

Ele retorna uma das propriedades do Depth of Market correspondente ao modificador ENUM_MBOOK_INFO_DOUBLE. Uma lista completa das funções suportadas pode ser encontrada na descrição da lista ENUM_MBOOK_INFO_DOUBLE.

double      InfoGetDouble(ENUM_MBOOK_INFO_DOUBLE property);

Valor retornado

Propriedade do Depth of Market do tipo double. Em caso de falha, ele retorna -1,0.

Uso

Encontre o exemplo de uso na seção relevante do quarto capítulo.  

 

Método IsAvailable()

Retorna verdadeiro, se a informação sobre o Depth of Market estiver disponível e falso caso contrário. Este método deve ser chamado antes de operar com a classe Depth of Market para verificar a possibilidade em trabalhar com este tipo de informação.

bool        IsAvailable(void);

Valor retornado

Verdadeiro, se o Depth of Market estiver disponível para outras operações e falso caso contrário.

 

Método SetMarketBookSymbol()

Define o símbolo solicitado para trabalhar com o Depth of Market. Também é possível definir um símbolo do Depth of Market ao criar uma instância da classe CMarketBook, mostrando claramente o nome do símbolo usado no construtor.

bool        SetMarketBookSymbol(string symbol);

Valor retornado

Verdadeiro, se "símbolo" estiver disponível para negociação, falso caso contrário.

 

Método GetMarketBookSymbol()

Ele retorna o símbolo de um instrumento, cuja operação com o Depth of Market da instância atual de classe é definido. 

string      GetMarketBookSymbol(void);

Valor retornado

Nome do instrumento onde o Depth of Market mostra a instância atual da classe. NULO, se um instrumento não for selecionado ou não estiver disponível. 

 

Método GetDeviationByVol()

Retorna o valor de um potencial desvio na entrada do mercado com uma ordem a mercado. Este valor tem um caráter estimado e o desvio obtido pode ser diferente do que foi calculado por esta função, caso tenha mudado o estado do Depth of Market no ponto de entrada. Este recurso apresenta uma avaliação bastante precisa do desvio que terá lugar na entrada no mercado, podendo ser usado como uma fonte de informação adicional.

O método tem dois parâmetros: o valor da oferta e iteração propostos, indicando o tipo de liquidez usado para fechar uma oferta. Por exemplo, a liquidez das ordens limitadas de Venda (Sell Limit) será usada para Compra e, neste caso, o tipo de MBOOK_ASK terá de ser especificado como "side". Para vender, ao contrário, o MBOOK_BID terá de ser indicado. Para obter mais informações sobre qual o lado do DOM, por favor, leia a descrição da enumeração ENUM_BOOK_SIDE.

double     GetDeviationByVol(long vol, ENUM_MBOOK_SIDE side);

parâmetros:

  • [in] vol - volume da oferta proposta;
  • [in] side - lado do Depth of Market, que será usado para fazer uma oferta.  

Valor retornado

A quantidade de desvio em potencial nos pontos do instrumento.

 

4.2. Enumerações e modificadores da classe CMarketBook

Enumeração do ENUM MBOOK_SIDE

A enumeração ENUM_BOOK_SIDE contém modificadores que indicam o tipo de liquidez. Campos de enumeração e descrição estão listados abaixo:

CampoDescrição
MBOOK_ASK Indica a liquidez fornecida por ordens Sell Limit.
MBOOK_BID Indica a liquidez fornecida por ordens Buy Limit.

Nota: 

Cada ordem de mercado pode ser executada por ordens limitadas. Dependendo da direção da ordem, são usados ordens limitadas de Compra ou Venda (Buy ou Sell Limit). A desvantagem de uma oferta de Compra será uma ou poucas ordens limitadas de Venda (Sell Limit). A desvantagem de uma oferta de Venda será uma ou poucas ordens limitadas de Compra (Buy Limit). Desta forma, o modificador indica uma das duas partes do Depth of Market: o lado (side) da Compra ou da Venda. O modificador é usado pela função GetDeviationByVol, para uma operação da qual você é obrigado a saber, qual lado da liquidez será utilizado pela expectativa de oferta do mercado.

 

Enumeração do ENUM MBOOK_INFO_INTEGER

Enumeração ENUM MBOOK_INFO INTEIRO contém modificadores de propriedade, que têm de ser obtidos usando o método InfoGetInteger. Campos de enumeração e descrição estão listados abaixo:

CampoDescrição
MBOOK_BEST_ASK_INDEX Índice de melhor preço Ask.
MBOOK_BEST_BID_INDEX Índice de melhor preço Bid.
MBOOK_LAST_ASK_INDEX Índice de pior ou último preço Ask.
MBOOK_LAST_BID_INDEX Índice de melhor ou último preço Bid.
MBOOK_DEPTH_ASK Depth of Market do lado Ask ou o seu número total de níveis de negociação.
MBOOK_DEPTH_BID Depth of Market do lado Bid ou o seu número total de níveis de negociação.
MBOOK_DEPTH_TOTAL Depth of Market total ou o número de níveis de negociação de Compra e Venda.
MBOOK_MAX_ASK_VOLUME Máximo volume Ask.
MBOOK_MAX_ASK_VOLUME_INDEX Índice de nível do máximo volume Ask.
MBOOK_MAX_BID_VOLUME Máximo volume Bid
MBOOK_MAX_BID_VOLUME_INDEX Índice de nível do máximo nível Bid
MBOOK_ASK_VOLUME_TOTAL O volume total de ordens limitadas de Venda (Sell Limit) disponível no Depth of Market
MBOOK_BID_VOLUME_TOTAL  O volume total de ordens limitadas de Compra (Buy Limit) disponíveis no Depth of Market atual
MBOOK_BUY_ORDERS O volume total de ordens limitadas de Compra (Buy Limit) atualmente disponíveis no mercado de ações
MBOOK_SELL_ORDERS O volume total de ordens limitadas de Venda (Sell Limit) atualmente disponíveis no mercado de ações

 

Enumeração do ENUM MBOOK_INFO_DOUBLE

Enumeração ENUM_MBOOK_INFO_DOUBLE contém modificadores de propriedade, que têm de ser obtidos usando o método InfoGetDouble. Campos de enumeração e descrição estão listados abaixo:

CampoDescrição
MBOOK_BEST_ASK_PRICE Melhor preço Ask
MBOOK_BEST_BID_PRICE Melhor preço Bid
MBOOK_LAST_ASK_PRICE Pior ou último preço Ask
MBOOK_LAST_BID_PRICE Pior ou último preço Bid
MBOOK_AVERAGE_SPREAD Diferença média entre o melhor Bid e Ask, ou um spread
MBOOK_OPEN_INTEREST  Contrato aberto
MBOOK_BUY_ORDERS_VOLUME Número de ordens de Compra
MBOOK_SELL_ORDERS_VOLUME  Números de ordem de venda

 

4.3. Exemplo de utilização da classe CMarketBook

Este exemplo contém um código-fonte sob a forma de um Expert Advisor que exibe informações básicas do Depth of Market no ponto de partida:

//+------------------------------------------------------------------+
//|                                               TestMarketBook.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade\MarketBook.mqh>     // Incluir classe CMarketBook
CMarketBook Book(Symbol());         // Inicializar classe com o instrumento atual

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   PrintMbookInfo();
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//| função de início do programa Script                              |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Exibe info MarketBook                                            |
//+------------------------------------------------------------------+
void PrintMbookInfo()
  {
   Book.Refresh();                                                   // Atualiza o status do Depth of Market.
//--- Obter estatísticas dos valores inteiros principais
   int total=(int)Book.InfoGetInteger(MBOOK_DEPTH_TOTAL);            // Obtém o Depth of Market total
   int total_ask = (int)Book.InfoGetInteger(MBOOK_DEPTH_ASK);        // Obtém a quantidade de níveis de preço Ask
   int total_bid = (int)Book.InfoGetInteger(MBOOK_DEPTH_BID);        // Obtém a quantidade de níveis de preço Bid 
   int best_ask = (int)Book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);    // Obtém índice do melhor preço Ask
   int best_bid = (int)Book.InfoGetInteger(MBOOK_BEST_BID_INDEX);    // Obtém índice do melhor preço Bid

//--- Exibe estatísticas básicas
   printf("DEPTH OF MARKET TOTAL: "+(string)total);
   printf("NÚMERO DE NÍVEIS DOS PREÇOS PARA VENDER: "+(string)total_ask);
   printf("NÚMERO DE NÍVEIS DOS PREÇOS PARA COMPRAR: "+(string)total_bid);
   printf("ÍNDICE DO MELHOR PREÇO ASK: "+(string)best_ask);
   printf("ÍNDICE DO MELHOR PREÇO BID: "+(string)best_bid);
   
//--- Obter principais estatísticas double
   double best_ask_price = Book.InfoGetDouble(MBOOK_BEST_ASK_PRICE); // Obtém melhor preço Ask
   double best_bid_price = Book.InfoGetDouble(MBOOK_BEST_BID_PRICE); // Obtém melhor preço Bid
   double last_ask = Book.InfoGetDouble(MBOOK_LAST_ASK_PRICE);       // Obtém pior preço Ask
   double last_bid = Book.InfoGetDouble(MBOOK_LAST_BID_PRICE);       // Obtém pior preço Bid
   double avrg_spread = Book.InfoGetDouble(MBOOK_AVERAGE_SPREAD);    // Obtém o spread médio durante o funcionamento do Depth of Market
   
//--- preços de exibição e spread
   printf("MELHOR PREÇO ASK: " + DoubleToString(best_ask_price, Digits()));
   printf("MELHOR PREÇO BID: " + DoubleToString(best_bid_price, Digits()));
   printf("PIOR PREÇO ASK: " + DoubleToString(last_ask, Digits()));
   printf("PIOR PREÇO BID: " + DoubleToString(last_bid, Digits()));
   printf("SPREAD MÉDIO: " + DoubleToString(avrg_spread, Digits()));
  }
//+------------------------------------------------------------------+

 

Conclusão

O artigo acabou por ser bastante dinâmico. Analisamos o Depth of Market de uma perspectiva técnica e propusemos uma classe container de alto desempenho para trabalhar com ele. Como exemplo, nós criamos um indicador Depth of Market com base nessa classe container, que pode ser exibido totalmente no gráfico de preço do instrumento.

Nosso indicador Depth of Market é muito básico e ainda carece de uma série de coisas. No entanto, o principal objetivo foi alcançado - temos a certeza que com a classe CMarketBook desenvolvida, podemos construir Expert Advisors complexos de forma relativamente rápida e indicadores para a análise da liquidez em andamento com o instrumento. Ao projetar a classe CMarketBook, muita atenção foi dada ao desempenho, uma vez que o Depth of Market tem uma tabela muito dinâmica, alterando centenas de vezes por minuto.

A classe descrita no artigo pode se tornar uma base sólida para o seu scalper ou um sistema de alta-frequência. Sinta-se a vontade para adicionar qualquer funcionalidade específica ao seu sistema. Para fazer isso, basta criar sua classe Depth of Market derivada do CMarketBook e escrever os métodos de extensão que você vai precisar. Esperamos que essas propriedades básicas fornecidas pelo Depth of Market tornem o seu trabalho mais fácil e mais confiável.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1793

Arquivos anexados |
MQL5.zip (202.33 KB)
Últimos Comentários | Ir para discussão (10)
Igor Marcio Nunes Da Silva
Igor Marcio Nunes Da Silva | 25 jun 2021 em 20:12
Marcos Issler:
O arquivo .ZIP possui um erro no arquivo marketbook.mqh, o qual se pode baixar o correto nesse link. https://www.mql5.com/en/articles/1683

a classe desse artigo nao compila

Igor Marcio Nunes Da Silva
Igor Marcio Nunes Da Silva | 25 jun 2021 em 20:17
nao precisa pegar classe nenhuma em outro artigo, somente abra o arquivo marketbook,mqh e comente as linhas 92,93 e 97.
dalmo hollen
dalmo hollen | 5 out 2021 em 06:21
Igor Marcio Nunes Da Silva #:
nao precisa pegar classe nenhuma em outro artigo, somente abra o arquivo marketbook,mqh e comente as linhas 92,93 e 97.
não conseguir ver um print na caixa de ferramenta>experts pra poder entender o que acontece, só copiei o código e não deu erro na compilação. alguma dica?
Sidjkcleto
Sidjkcleto | 1 jun 2022 em 18:23

@Sidjkcleto


Não consigo reduzir o tamanho do MarketBook.

Sidnei Da Silva Santos Junior
Sidnei Da Silva Santos Junior | 18 jan 2023 em 12:53
Parece que tem classe dentro de "Indicators"... eu reorganizei a localização dos arquivos, coloquei cada uma em suas pastas e deu tudo certo.
Agora a plataforma MetaTrader 5 possui um sistema de cobertura de registro de posições Agora a plataforma MetaTrader 5 possui um sistema de cobertura de registro de posições
Para ampliar as possibilidades dos traders de retail-Forex, foi adicionado à plataforma a cobertura (segundo sistema de registro). Agora, segundo o instrumento, você pode ter várias posições, incluindo posições opostas. Isto permite implementar estratégias de negociação com o assim chamado bloqueio, por outras palavras, se o preço estiver contra o trader, ele terá a possibilidade de abrir uma posição na direção oposta.
Desenhando Resistência e Níveis de Suporte Com MQL5 Desenhando Resistência e Níveis de Suporte Com MQL5
Este artigo descreve um método para encontrar quatro pontos extremos, onde baseado neles, se desenha os níveis de suporte e de resistência. Para encontrar o extremos num gráfico de um par de moedas, foi usado o indicador RSI. Para dar um exemplo, nós fornecemos um código de indicador que exibe os níveis de suporte e resistência.
MQL5 para iniciantes: Proteção antivandalismo de objetos gráficos MQL5 para iniciantes: Proteção antivandalismo de objetos gráficos
O que o seu programa deve fazer, se os painéis de controle gráfico foram removidos ou modificados por alguém? Neste artigo, vamos mostrar a você o porquê de não ter objetos no gráfico "sem dono" e como não perder o controle sobre eles, se forem renomeados ou excluídos após o aplicativo ser deletado.
Indicador para Gráfico de Spindles Indicador para Gráfico de Spindles
O artigo apresenta a plotagem do gráfico de spindles e seu uso em estratégias de negociação e experts. Primeiro vamos discutir a aparência do gráfico, plotagem e conexão com o gráfico de velas japonesas. Em seguida, analisaremos a implementação do indicador no código fonte na linguagem MQL5. Vamos testar o expert com base no indicador e formular uma estratégia de negociação.