• Registre-se

Ordens, posições e negócios no MetaTrader 5

10 janeiro 2014, 14:27
MetaQuotes
0
9 554

Termos comerciais

O maior objetivo de um comerciante é extrair lucros através dos meios de operações de negociação nos mercados financeiros. Esse artigo descreve os termos e processos da plataforma de negociação MetaTrader 5, o conhecimento necessário para uma compreensão adequada do desenvolvimento das funções comerciais da linguagem MQL5.

  • Ordens — são as solicitações de operação de comércio, recebidas pelo servidor de negociação, formuladas em conformidade com os requisitos da plataforma MetaTrader 5. Se a solicitação estiver incorreta, ela não vai aparecer na plataforma de negociação na forma de uma ordem. Ordens podem ser de execução imediata, assim como comprar ou vender um determinado volume, no preço atual de mercado em um instrumento financeiro específico. Outros tipos de ordens - são ordens pendentes, que contêm uma ordem para executar uma operação comercial sob a presença de determinada condição. Ordens pendentes também podem conter uma restrição de tempo em suas ações - a data de validade da ordem.

    Ordens e posições no terminal MetaTrader 5

    As ordens estabelecidas (pendentes), que estão à espera de condições para sua execução ou cancelamento, são mostradas na guia "Comércio" no terminal. Essas ordens podem ser modificadas ou canceladas. O estabelecimento, cancelamento e modificação de ordens é feito usando a função OrderSend(). Se a ordem foi cancelada ou atingiu o tempo de expiração, ou se a mesma foi executada, ela é movida para o histórico de ordens. Ordens executadas e canceladas são mostradas na aba "Histórico" do terminal do cliente. Ordens do histórico não estão disponíveis para modificação.

  • Negócios - são o resultado de uma execução de uma ordem (um comando para confirmar o negócio). Cada negócio é baseado em uma ordem particular, mas uma única ordem pode gerar uma série de negócios. Por exemplo, uma ordem para comprar 10 lotes pode ser executada por vários negócios sucessivos com preenchimento parcial. Os negócios são sempre armazenados no histórico de negociação e não podem ser modificados. No terminal, os negócios são mostrados na guia "Histórico".

    Transações no terminal MetaTrader 5

  • Posições são os contratos, comprados ou vendidos em um instrumento financeiro. Uma posição longa (Longa) é formada como resultado de compras na expectativa de um aumento de preços, uma posição curta (Curta) é o resultado da venda de bens, na expectativa de diminuição do preço no futuro. Para cada conta, para cada instrumento financeiro, pode haver somente uma posição. Para cada símbolo, em qualquer momento, pode haver somente uma posição - longa ou curta.

    Histórico de ordens no terminal MetaTrader 5

    O volume de posição pode aumentar, como resultado de uma nova operação de negociação na mesma direção. Isto significa que o volume das posições longas será aumentado após a nova compra (Negócio de compra) e diminuído após a venda (Negócio de venda). A posição é encerrada se o volume dos comprometimentos se tornar igual a zero como resultado da operação de transação. Esta operação é chamada de posição de fechamento.

Observação: Ordens ativas e posições são sempre mostradas no guia "Comércio" e os negócios e ordens do histórico sempre refletem na aba "Histórico". A ordem ativa da guia "Comércio" não deve ser confundida com o histórico de ordens da aba "Histórico".

Como o terminal recebe e armazena as informações de comércio do servidor

O terminal armazena o histórico de comercialização em uma base especial e recebe somente o histórico que falta de ofertas e pedidos concluídos na conta de comercialização, em cada conexão com o servidor de negociação. Isso é feito para economizar em tráfego. Ao fechar o terminal do cliente MetaTrader 5 ou alterar a conta corrente ativa, todo o histórico é registrado no disco rígido e lido na próxima vez em que o terminal for lançado.

Todos os bancos de dados são gravados no disco em um formato criptografado e a chave de criptografia depende do computador em que o terminal está instalado. Isso protege o usuário do terminal contra o acesso não autorizado aos seus dados, em caso de cópia.

Durante a conexão com a conta, o terminal carrega a base da conta salva, com o histórico da conta e envia ao servidor de negociação um pedido para sincronizar seu próprio banco de dados do histórico com o histórico da conta no servidor de negociação. Além disso, após uma conexão bem-sucedida na conta, o servidor de negociação envia ao terminal um relatório sobre os eventos comerciais em andamento, relacionados a esta conta.

Eventos comerciais são as seguintes alterações na conta:

  • saque e operações de saldo;
  • custo de comissões, trocas e impostos;
  • colocação, exclusão e modificação de ordens;
  • execução de negócios com base em ordens;
  • abertura e encerramento de posições;
  • mudança no volume e na direção de posições.

No caso de encerramento da conexão com o servidor de negociação, o terminal tenta periodicamente se reconectar. Após a reconexão com o servidor, o terminal solicita todas as mudanças recentes do histórico de negociação para manter a integridade dos dados em seu próprio histórico de banco de dados.

O histórico de comercialização, exibido na aba "Histórico" do terminal, é retirado da base do terminal histórico, e as mudanças do período, mostradas no histórico do terminal, podem portanto aumentar a abrangência do histórico armazenado neste banco de dados. Diminuição do período de exibição do histórico não leva a uma remoção física do histórico da base do terminal.

Instalação do intervalo do histórico de comercialização apresentado

Isto significa que a instalação de um intervalo mais curto do histórico exibido não reduz a intensidade do histórico de comercialização armazenado. Mas, se especificarmos um intervalo mais amplo para a exibição na aba "Histórico", então tal ação poderia levar a um pedido, a partir do servidor de comercialização, de um histórico mais aprofundado, se própria base do terminal ainda não tiver o dados solicitados para este período.

O regime geral de interação entre o terminal e o servidor de comercialização MetaTrader 5 é demonstrado na seguinte figura:


O terminal do cliente envia um pedido de sincronização para sua própria base do histórico de comercialização durante o início do terminal, durante o restabelecimento da ligação com o servidor após uma falha da conexão, durante a transferência de uma conta para outra e durante a solicitação direta do histórico de comercialização que está faltando.

Por sua vez, o servidor de comercialização independentemente, sem nenhuma solicitação do terminal, envia mensagens de clientes sobre os eventos de comércio acontecendo na conta: as modificações do estado de ordens e posições, a realização de negócios com base em ordens, a cobrança de comissões, saldo e retirada de dinheiro, e assim por diante.


Acesso ao histórico de comercialização do programa MQL5

O terminal pode operar simultaneamente com um conjunto de indicadores, manuscritos e EAs e todos esses programas podem solicitar as informações que precisam sobre comércio, ordens, negócios e posições. O trabalho direto do programa MQL5 com o banco de dados do terminal é excluído devido às considerações de estabilidade global, segurança e performance.

Cada programa MQL5 por solicitação recebe pelo seu trabalho um "modelo" do ambiente de comércio em seu cache. Cache é uma área especial de memória para um acesso rápido aos dados. Por exemplo, antes de começar o processamento da ordem, a ordem necessária deve ser obtida no cache do programa MQL5. Todos os outros trabalhos, quando se referirem à ordem, serão feitos com a cópia em cache dessa ordem.

O trabalho com as posições, negócios e ordens a partir do histórico é realizado de uma maneira semelhante. O regime geral de obtenção de informações de comércio do programa MQL5 é mostrado na figura:


Antes que os dados sobre o histórico de comercialização se tornem disponíveis para o processamento do programa MQL5, eles devem ser solicitados a partir do banco de dados do terminal. Após a solicitação, os dados obtidos serão colocados na própria memória do programa MQL5.

Observação: os dados no cache não são automaticamente sincronizados com banco de dados do terminal, portanto, eles devem ser constantemente atualizados para que se mantenha um estado apropriado dos dados no cache.

Existe uma possibilidade de consequências se o cache for usado de forma inadequada.

  • se os dados solicitados não puderem ser obtidos, o cache estará vazio e não irá conter os dados necessários.
  • se os dados no cache exigirem atualizações, mas a atualização não foi solicitada, trabalhar com esses dados pode levar a resultados imprevisíveis. Por exemplo, os dados da posição atual não foram atualizados e o programa não sabe nada sobre a posição aberta para o dado símbolo e sobre a crescente perda para ele

A função para trabalhar com o cache

O histórico de comercialização pode conter milhares de ordens executadas e negócios que não são necessários para o trabalho atual do programa MQL5. Portanto, o trabalho com o cache é baseado no princípio das solicitações o cache sempre contém as informações que foram carregadas na última conexão com o banco de dados do terminal. Se você necessita obter todo o histórico de ordens e negócios, precisa solicitá-lo explicitamente especificando o intervalo desejado.

Para cada tipo de informação, um cache independente é formado. Os dados sobre as ordens são armazenados na ordem do cache, as informações sobre as posições são armazenadas na posição do cache, os dados sobre negócios e ordens são armazenados nas respectivas instâncias do histórico do cache.

Antes de solicitar as informações do cache, ele precisa ser preenchido.

Observação: Qualquer pedido para preencher o cache, previamente o limpa, independentemente do resultado da execução dos pedidos.


As funções de comércio podem ser separados em duas categorias: as funções para o preenchimento do cache e as funções para ler a informação a partir do cache.

A função de preenchimento do cache

Para o processamento do histórico de comercialização, ele deve primeiro ser obtido e localizado no cache apropriado. Funções que formam um cache podem ser divididas em dois subgrupos.

A função para preencher o cache de comércio (ordens ativas e posições):

  • O OrderSelect(ticket) - copia a ordem ativa pelo seu bilhete (a partir da base terminal) no cache da ordem atual para a nova ordem de suas propriedades usando a função OrderGetDouble(), OrderGetInteger() e OrderGetString();
  • OOrderGetTicket(index) -copia, a partir da base terminal da ordem ativa, através de seu índice na base terminal da lista de ordens, para o cachê das ordens atuais para solicitação adicional das propriedades usando as funções OrderGetDouble(), OrderGetInteger() e OrderGetString(). O número total de ordens da base terminal pode ser obtido usando a função OrdersTotal();
  • O PositionSelect(símbolo) copia a posição aberta pelo nome do símbolo (a partir da base terminal) para o cache das outras solicitações de suas propriedades usando a função PositionGetDouble(), PositionGetInteger() e PositionGetString();
  • O PositionGetSymbol(index) -copia a posição aberta através de seu índice na lista de posição (a partir da base terminal) da base terminal para o cache das outras solicitações de suas propriedades usando as funções PositionGetDouble(), PositionGetInteger() e PositionGetString(). O número total de posições da base terminal pode ser obtido usando a função PositionsTotal().

A função de preenchimento do histórico do cache:

  • O HistoryOrderSelect(ticket) copia o histórico da ordem pelo seu bilhete para o cache de histórico de ordens (a partir da base terminal) para as demais chamadas para suas propriedades usando as funções HistoryOrderGetDouble(), HistoryOrderGetInteger() e HistoryOrderGetString();
  • O HistoryDealSelect(ticket) copia o negócio pelo seu bilhete para o cache de negócios (a partir da base terminal) para as demais chamadas para suas propriedades usando as funções HistoryDealGetDouble(), HistoryDealGetInteger() e HistoryDealGetString();

Precisamos considerar separadamente as duas funções que afetam o disponível, no cache, histórico do comércio em geral:

  • O HistorySelect(início, fim) preenche o histórico do cache com negócios e ordens para o intervalo de tempo específico do servidor. Os valores dependem dos resultados da execução desta função, que são devolvidos a partir do HistoryDealsTotal() e HistoryOrdersTotal();
  • O HistorySelectByPosition (position_ID) - preenche o histórico do cache com negócios e ordens, tendo a posição de identificador especificada. O resultado da execução dessa função também afeta o HistoryDealsTotal() e HistoryOrdersTotal().


OrderSelect e OrderGetTicket

As funções gerais do OrderSelect(ticket) e OrderGetTicket() funcionam da mesma maneira, - elas preenchem o cache de ordens ativas com uma única ordem. O OrderSelect(ticket) é destinado para o caso em que uma ordem de bilhete é conhecida de antemão. O OrderGetTicket(), em conjunto com o OrdersTotal(), permite a análise de todas as ordens disponíveis no terminal de base de ordens.

Após uma chamada para qualquer uma destas funções, o cache das ordens ativas contém a informação de uma única ordem, se a ordem for selecionada com sucesso. Do contrário, não há nada no cache de pedidos ativos. O resultado da execução da função OrdersTotal() não muda - sempre devolve o número real de pedidos ativos na base do terminal, independentemente do cache estar cheio.

PositionSelect e PositionGetSymbol

Assim como para as ordens, estas duas funções também funcionam da mesma maneira para as posições - elas preenchem o cache de posições com uma única posição. O PositionGetSymbol(index) requer o número na lista de posições da base e o PositionSelect(símbolo) preenche o cache baseado no nome do símbolo, em que a posição é aberta. O nome do símbolo, por sua vez, pode ser obtido através função PositionGetSymbol(index).

Após a realização de qualquer uma destas funções, o cache de posições contém apenas dados sobre uma posição, se a função for executada com sucesso. Caso contrário, não há nada no cache de posições. O resultado da execução da função PositionsTotal() não depende se o cache está preenchido - ele sempre devolve o número real de posições abertas no terminal de base para todos os símbolos.

HistoryOrderSelect

O HistoryOrderSelect(ticket) escolhe no cache a ordem histórica a partir da base do terminal pelo seu bilhete. A função destina-se a ser utilizada quando o bilhete da ordem necessário é conhecido com antecedência.

Se a execução for bem sucedida, o cache irá conter uma única ordem, e a função HistoryOrdersTotal() retorna uma única unidade. Caso contrário, o cache de ordens históricas estará vazio e a função HistoryOrdersTotal () retornará a zero.


HistoryDealSelect

O HistoryOrderSelect(ticket) escolhe no cache a ordem histórica a partir da base do terminal pelo seu bilhete. A função destina-se a ser utilizada quando o bilhete de negócio necessário é conhecido com antecedência.

Se a execução for bem sucedida, o cache irá conter um único negócio, e a função HistoryDealsTotal() retorna uma única unidade. Caso contrário, o cache de negócios históricos estará vazio e a função HistoryDealsTotal() retornará a zero.

A função para a obtenção de informações a partir do cache

Antes de solicitar informações sobre as propriedades da posição, negócio ou ordem, é necessário atualizar o cache correspondente do programa MQL5. Isto é devido ao fato de que as informações solicitadas já podem ter sido atualizadas e isso significa que a cópia, armazenada no cache já está desatualizada.

  • Ordens
    A fim de obter informações sobre ordens ativas, elas devem primeiro ser copiadas para o cache de pedidos ativos de uma das duas funções: OrderGetTicket() ou OrderSelect(). É para a ordem, a qual está armazenada no cache, para que os valores de propriedade serão entregues quando as funções correspondentes são chamadas de:
    1. OrderGetDouble(type_property)
    2. OrderGetInteger(type_property)
    3. OrderGetString(type_property)

Estas funções adquirem todos os dados a partir do cache, portanto, a fim de garantir a obtenção de dados precisos para a ordem, recomenda-se chamar a função que preenche o cache.

  • Posições

    Para obter informações sobre a posição, ela deve ser previamente selecionada e copiada para a memória cache utilizando uma das duas funções: PositionGetSymbol ou PositionSelect. É a partir deste cache que os valores de propriedade de posição são dados, quando as funções correspondentes são chamadas de:

    1. PositionGetDouble(type_property)
    2. PositionGetInteger(type_property)
    3. PositionGetString(type_property)

Visto que estas funções adquirem todos os dados a partir do cache, a fim de garantir a obtenção de dados precisos para a ordem, recomenda-se chamar a função que preenche o cache de posições.

  • Histórico de ordens

    Para obter informações sobre uma ordem a partir do histórico, é necessário primeiro criar o cache de histórico de ordens usando uma das três funções: HistorySelect(start, end), HistorySelectByPosition() ou HistoryOrderSelect(ticket). Se a execução for bem sucedida, o cache irá armazenar o número de ordens, retornado pela função HistoryOrdersTotal(). O acesso às propriedades dessas ordens é realizado por cada um dos elementos no bilhete, utilizando a função apropriada:

    1. HistoryOrderGetDouble(ticket_order, type_property)
    2. HistoryOrderGetInteger(ticket_order, type_property)
    3. HistoryOrderGetString(ticket_order, type_property)

O bilhete do histórico da ordem pode ser encontrado utilizando a função HistoryOrderGetTicket(index), pelo seu índice no cache de ordens históricas. A fim de ter um recibo garantido de dados precisos sobre o ordem, recomenda-se chamar a função que preenche o cache de histórico de ordens.

  • Negócios

    Para obter informações sobre um negócio específico a partir do histórico, é necessário primeiro criar o cache de histórico de negócios usando uma das três funções: HistorySelect (start, end), HistorySelectByPosition() ou HistoryDealSelect (ticket). Se a implementação da função for bem sucedida, o cache irá armazenar o negócio no valor retornado pela função HistoryDealsTotal(). O acesso às propriedades desses negócios é realizado, com base no bilhete, utilizando as funções apropriadas:

    1. HistoryDealGetDouble(ticket_deals, type_property)
    2. HistoryDealGetInteger(ticket_deals, type_property)
    3. HistoryDealGetString(ticket_deals, type_property)

O bilhete do histórico dos negócios pode ser obtido utilizando a função HistoryDealGetTicket(index), pelo seu índice no cache do histórico de negócios. A fim de ter um recibo garantido de dados precisos sobre o negócio, recomenda-se chamar a função que preenche o cache de histórico de negócios.

A função para a obtenção do bilhete a partir do histórico do cache

O HistoryOrderGetTicket (index) retorna o bilhete da ordem histórica, pelo seu índice do cache do histórico de ordens (não pela base do terminal!) O bilhete obtido pode ser usado na função HistoryOrderSelect (ticket) que limpa o cache e o preenche novamente com uma única ordem, no caso de sucesso. Lembre-se que o valor devolvido do HistoryOrdersTotal() depende do número de ordens no cache.

O HistoryDealGetTicket(index) retorna o bilhete do negócio a partir do seu índice do cache de negócios. O bilhete do negócio pode ser usado pela função HistoryDealSelect(ticket), a qual limpa o cache e o preenche novamente com um único negócio, no caso de sucesso. O valor retornado pela função HistoryDealsTotal () depende do número de negócios no cache.

Observação: Antes de chamar as funções HistoryOrderGetTicket (index) e HistoryDealGetTicket (index), você precisa preencher o histórico do cache com ordens históricas e negócios em um volume suficiente. Para fazer isso, use uma das funções: HistorySelect (start, end), HistorySelectByPosition (position_ID), HistoryOrderSelect (ticket) e HistoryDealSelect (ticket).

Obtenção de informações através de ordens ativas

Verificar as atuais ordens ativas é um procedimento padrão. Se for necessário obter informações sobre uma ordem específica, então, conhecer o seu bilhete pode ser feito usando a função OrderSelect(ticket).

bool selected=OrderSelect(ticket);
if(selected)
  {
   double price_open=OrderGetDouble(ORDER_PRICE_OPEN);
   datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP);
   string symbol=OrderGetString(ORDER_SYMBOL);
   PrintFormat("Ордер #%d for %s was set at %s",ticket,symbol,TimeToString(time_setup));
  }
else
  {
   PrintFormat("Error selecting order with ticket %d. Error %d",ticket, GetLastError());
  }

No exemplo acima, assume-se que o bilhete da ordem é conhecido antecipadamente, por exemplo, é obtido a partir do global variable. Em casos gerais, entretanto, a informação do bilhete está ausente e, portanto, precisamos nos voltar para a ajuda da função OrderGetTicket(index), a qual também seleciona uma ordem e a coloca no cache, mas somente o número da ordem, na lista de ordens atuais, precisa ser especificado como o parâmetro.

O algoritmo geral para trabalhar com ordens (análogo com negócios e posições) é o seguinte:

  1. Obter o número total de ordens, usando a função OrdersTotal();
  2. Organizar o circuito através de uma pesquisa de todas as ordens, pelo seus índices na lista;
  3. Copiar um por um, cada ordem para o cache, usando a função OrderGetTicket();
  4. Obter os dados corretos da ordem do cache usando as funções OrderGetDouble(), OrderGetInteger() e OrderGetString(). Se necessário, analisar os dados obtidos e tomar as devidas ações.

Aqui está um breve exemplo de um algoritmo:

input long my_magic=555;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtain the total number of orders
   int orders=OrdersTotal();
//--- scan the list of orders
   for(int i=0;i<orders;i++)
     {
      ResetLastError();
      //--- copy into the cache, the order by its number in the list
      ulong ticket=OrderGetTicket(i);
      if(ticket!=0)// if the order was successfully copied into the cache, work with it
        {
         double price_open  =OrderGetDouble(ORDER_PRICE_OPEN);
         datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP);
         string symbol      =OrderGetString(ORDER_SYMBOL);
         long magic_number  =OrderGetInteger(ORDER_MAGIC);
         if(magic_number==my_magic)
           {
            //  process the order with the specified ORDER_MAGIC
           }
         PrintFormat("Order #%d for %s was set out %s, ORDER_MAGIC=%d",ticket,symbol,TimeToString(time_setup),magic_number);
        }
      else         // call OrderGetTicket() was completed unsuccessfully
        {
         PrintFormat("Error when obtaining an order from the list to the cache. Error code: %d",GetLastError());
        }
     }
  }

Obtenção de informação em posições abertas

O monitoramento constante de posições abertas não é apenas um procedimento padrão, mas certamente deve ser implantado em cada ocorrência. Para obtenção de informação sobre posições específicas, é suficiente saber o nome do instrumento pelo qual ele é aberto. Para fazer isto, use a função PositionSelect(símbolo). Para aqueles casos no qual o EA está operando em somente um símbolo (no símbolo do gráfico, ao qual está ligado), o nome do símbolo pode ser obtido a partir da função Symbol() ou de uma variável predefinida _Symbol.

//--- we will look for the position by the symbol of the chart, on which the EA is working
   string symbol=Symbol();
//--- attempt to get the position
   bool selected=PositionSelect(symbol);
   if(selected) // if the position is selected
     {
      long pos_id            =PositionGetInteger(POSITION_IDENTIFIER);
      double price           =PositionGetDouble(POSITION_PRICE_OPEN);
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      long pos_magic         =PositionGetInteger(POSITION_MAGIC);
      string comment         =PositionGetString(POSITION_COMMENT);
      PrintFormat("Position #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                 pos_id, symbol, pos_magic, price,EnumToString(type), comment);
     }

   else        // if selecting the position was unsuccessful
     {
      PrintFormat("Unsuccessful selection of the position by the symbol %s. Error",symbol,GetLastError());
     }
  }

De uma maneira geral, a informação no símbolo pode ser obtida usando a função PositionGetSymbol (index), a qual seleciona uma posição e a coloca no cache. Como um parâmetro, é necessário especificar o índice de posição na lista de posições abertas. Isso é feito da melhor forma através de uma pesquisa de todas as posições no circuito.

O algoritmo geral para trabalhar com as posições:

  1. Obter o número total de posições usando a função PositionsTotal();
  2. Organizar o circuito através de uma pesquisa de todas as posições pelo seus índices na lista;
  3. Copiar um por um, cada posição para o cache, usando a função PositionGetSymbol();
  4. Obter os dados necessários da posição do cache, usando as funções PositionGetDouble(), PositionGetInteger() e PositionGetString(). Se necessário, analisar os dados obtidos e tomar as devidas ações.

Um exemplo de tal algoritmo:

#property script_show_inputs

input long my_magic=555;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtain the total number of positions
   int positions=PositionsTotal();
//--- scan the list of orders
   for(int i=0;i<positions;i++)
     {
      ResetLastError();
      //--- copy into the cache, the position by its number in the list
      string symbol=PositionGetSymbol(i); //  obtain the name of the symbol by which the position was opened
      if(symbol!="") // the position was copied into the cache, work with it
        {
         long pos_id            =PositionGetInteger(POSITION_IDENTIFIER);
         double price           =PositionGetDouble(POSITION_PRICE_OPEN);
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         long pos_magic         =PositionGetInteger(POSITION_MAGIC);
         string comment         =PositionGetString(POSITION_COMMENT);
         if(pos_magic==my_magic)
           {
           //  process the position with a specified POSITION_MAGIC
           }
         PrintFormat("Position #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                     pos_id,symbol,pos_magic,price,EnumToString(type),comment);
        }
      else           // call to PositionGetSymbol() was unsuccessful
        {
         PrintFormat("Error when receiving into the cache the position with index %d."+
                     " Error code: %d", i, GetLastError());
        }
     }
  }

Regras para trabalhar com o histórico do cache

Frequentemente, o código para trabalhar com o histórico do cache é escrito pelo programador de tal maneira que ele funciona bem apenas se o histórico conter 5-10 negócios e ordens. Um exemplo típico de uma abordagem errada - carregar todo o histórico de comercialização para o cache e processá-lo em um circuito, buscando através de todas as ordens e negócios:

//---
   datetime start=0;           // initial time set to 1970 year
   datetime end=TimeCurrent();  // the ending time set to the current server time
//--- request into the cache of the program the entire trading history
   HistorySelect(start,end);
//--- obtain the number of all of the orders in the history
   int history_orders=HistoryOrdersTotal();
//--- now scan through all of the orders
   for(int i=0;i<history_orders;i++)
     {
     //  processing each order in the history
     }   
 
    ...
       
//--- obtain the number of all deals in the history
   int deals=HistoryDealsTotal();
//--- now scan through all of the deals
   for(int i=0;i<deals;i++)
     {
     //  process each deal in the history
     }

A tentativa de lidar com todo o histórico de comercialização, na maioria dos casos, está errada. Quando o número de negócios/ordens processadas fica em torno de milhares e dezenas de milhares, o trabalho do programa diminui drasticamente.

Observação: sempre consulte cuidadosamente todos os casos da chamada da função HistorySelect()! Carregamento sem consideração e excessivo de todo o histórico de comercialização disponível no cache do programa MQL5 degrada seu desempenho.

Isto é principalmente importante para teste - o usuário descobre repentinamente que verificador se torna pensativo e começa a procurar as razões para isso no terminal do cliente. Portanto, primeiramente sempre pense em otimizar o código do programa MQL5 (EA e indicadores, que são chamados a partir da EA). Não confie no fato de que o computador é feito de ferro e possui muitos centros.

Para o trabalho adequado da EA e o indicador online, isso é muito importante. Um código não-otimizado de programa pode paralisar o trabalho de até mesmo o mais poderoso computador.

O algoritmo correto para trabalhar com o histórico de comercialização:

  1. Determine a necessidade de solicitar o histórico de comercialização para o cache. Se isso não for necessário, então não execute as seguintes ações;
  2. Determine a data final do histórico de comercialização (talvez o histórico até o momento não seja necessário);
  3. Calcule a data inicial do histórico de comercialização, a partir da data final. Normalmente, os AEs exigem o histórico de comercialização, não mais aprofundado do que um único dia ou semana;
  4. Obtenha os bilhetes do histórico de negócios e ordens para a obtenção das propriedades, através dos bilhetes conhecidos:
    • HistoryOrderGetDouble()
    • HistoryOrderGetInteger()
    • HistoryOrderGetString()
    • HistoryDealGetDouble()
    • HistoryDealGetInteger()
    • HistoryDealGetString()
  5. Se os bilhetes não são conhecidos, e se for necessário, organize um ciclo através de triagem;
  6. No circuito, obter o bilhete para cada negócio/ordem a partir do cache do histórico de comercialização, pelo índice (HistoryOrderGetTicket(index) e HistoryDealGetTicket (index));
  7. Obter as propriedades necessárias de ordens e negócios pelo bilhete conhecido (ver ponto 4).

Um exemplo de um código deste algoritmo:

//--- the variable, which is set in true only during the change in the trading history
   bool TradeHistoryChanged=false;
//--- here we check for the changes in the history and put out the TradeHistoryChanged=true if needed
//... the needed code

//--- check  if there are changes in the trading history or not
   if(!TradeHistoryChanged) return;

//--- if the history has changed, then it makes sense to load it into the cache 
//--- the ending time set for the current server time
   datetime end=TimeCurrent();
//--- the beginning time is set to 3 days ago
   datetime start=end-3*PeriodSeconds(PERIOD_D1);
//--- request in the cache of the program, the trading history for the last 3 days
   HistorySelect(start,end);
//--- obtain the number of orders in the cache of the history
   int history_orders=HistoryOrdersTotal();
//--- now scan through the orders
   for(int i=0;i<history_orders;i++)
     {
      //--- obtain the ticket of the historical order
      ulong ticket=HistoryOrderGetTicket(i);
      //--- work with this order - receive its problems
      long order_magic=HistoryOrderGetInteger(ticket,ORDER_MAGIC);
      // obtain the rest of the properties for the order by the ticket
      // ...
     }

A idéia básica apresentada por este exemplo é que primeiro você deve verificar o fato das mudanças que ocorrem no histórico do comércio. Uma das opções é, dentro da função OnTrade(), definir para a variável global TradeHistoryChanged, o valor da verdade, pois o evento Comércio sempre retorna com qualquer tipo de evento comercial.

Se o histórico de comercialização não mudou, então não há necessidade de fazer o carregamento do histórico de comercialização novamente para o cache e desperdiçar os recursos da CPU. Isto é lógico e não necessita de qualquer explicação. Se o histórico de comercialização mudou, então carregamos apenas a parte necessária do mesmo e passamos por cada negócio/ordem apenas uma vez. Evite repetir ciclos desnecessários.

Observação: todas as solicitações para o cache de todo o histórico de comercialização feitas pela função HistorySelect() e todos os ciclos do processamento de comércio e ordens do histórico devem ser fundamentados. Do contrário, os recursos do seu computador serão gastos de forma ineficiente.

Exemplos de trabalho correto e incorreto com o histórico de comercialização estão ligados a este artigo, como arquivos WrongWorkWithHistory.mq5 e RightWorkWithHistory.mq5.

Obtenção de informações de ordens do histórico

Trabalhar com o histórico de ordens é quase nada diferente do que trabalhar com ordens ativas, mas com uma exceção. Se o número de ordens ativas no cache do programa MQL5 não pode ser mais do que uma, então o resultado HistoryOrdersTotal() e o número de ordens do histórico no cache dependem da quantidade de histórico de comercialização que foi carregada pela função HistorySelect(início, fim), HistorySelectByPosition() ou HistoryOrderSelection().

Observação: Se o histórico de comercialização não foi carregado no cache do programa MQL5 por uma das funções HistorySelect(), HistorySelectByPosition() ou HistoryOrderSelect(), então, trabalhar com histórico de ordens e negócios é impossível. Não deixe de solicitar o histórico de negócios necessário e ordens antes de receber os dados do histórico de comercialização.

Por exemplo, nós fornecemos um script, que busca a última ordem do último dia e exibe informações para ele.

// --- determining the time intervals of the required trading history
   datetime end=TimeCurrent();                // current server time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning for 24 hours ago
//--- request in the cache of the program the trading history for a day
   HistorySelect(start,end);
//--- receive the number of orders in the history
   int history_orders=HistoryOrdersTotal();
//--- obtain the ticket of the order, which has the last index in the list, from the history
   ulong order_ticket=HistoryOrderGetTicket(history_orders-1);
   if(order_ticket>0) // obtain in the cache the historical order, work with it
     {
      //--- order status
      ENUM_ORDER_STATE state=(ENUM_ORDER_STATE)HistoryOrderGetInteger(order_ticket,ORDER_STATE);
      long order_magic      =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC);
      long pos_ID           =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID);
      PrintFormat("Order #%d: ORDER_MAGIC=#%d, ORDER_STATE=%d, ORDER_POSITION_ID=%d",
                  order_ticket,order_magic,EnumToString(state),pos_ID);
     }
   else              // unsuccessful attempt to obtain the order
     {
      PrintFormat("In total, in the history of %d orders, we couldn't select the order"+
                  " with the index %d. Error %d",history_orders,history_orders-1,GetLastError());
     }
 

Em casos mais gerais, é necessário classificar as ordens no circuito a partir do cache e analisá-las. O algoritmo geral será o seguinte:

  1. Determine o intervalo de tempo do histórico suficiente, se o histórico é carregado pela função HistorySelect() - não é recomendado carregar todo o histórico de comercialização no cache;
  2. Carregue no cache do programa, a função do histórico de comercialização HistorySelect(), HistorySelectByPosition() ou HistoryOrderSelect (ticket);
  3. Obtenha o número total de ordens no cache, usando o HistoryOrdersTotal ();
  4. Organize o circuito através de uma pesquisa de todas as ordens pelo seus índices na lista;
  5. Obtenha um bilhete das ordens no cache usando a função HistoryOrderGetTicket ();
  6. Obtenha os dados da ordem a partir do cache, usando as funções HistoryOrderGetDouble (), HistoryOrderGetInteger () e HistoryOrderGetString (). Se necessário, analise os dados obtidos e tome as devidas ações.

Um exemplo de tal algoritmo:

#property script_show_inputs

input long my_magic=999;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// --- setting the time intervals of the required trading history
   datetime end=TimeCurrent();                // the current server time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning for 24 hours ago
//--- request into the cache of the program the needed interval of the trading history
   HistorySelect(start,end);
//--- obtain the number of orders in history
   int history_orders=HistoryOrdersTotal();
//--- now scroll through all of the orders
   for(int i=0;i<history_orders;i++)
     {
      //--- obtain the ticket of the order by its number in the list
      ulong order_ticket=HistoryOrderGetTicket(i);
      if(order_ticket>0) //  obtain in the cache, the historical order, and work with it
        {
         //--- time of execution
         datetime time_done=HistoryOrderGetInteger(order_ticket,ORDER_TIME_DONE);
         long order_magic  =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC);
         long pos_ID       =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID);
         if(order_magic==my_magic)
           {
           //  process the position with the set ORDER_MAGIC
           }
         PrintFormat("Order #%d: ORDER_MAGIC=#%d, time_done %s, ORDER_POSITION_ID=%d",
                     order_ticket,order_magic,TimeToString(time_done),pos_ID);
        }
      else               // unsuccessful attempt to obtain the order from the history
        {
         PrintFormat("we were not able to select the order with the index %d. Error %d",
                     i,GetLastError());
        }
     }
  }
Observação: sempre consulte cuidadosamente todos os casos da chamada da função HistorySelect()! Carregamento sem consideração e excessivo de todo o histórico de comercialização disponível no cache do programa MQL5 degrada seu desempenho.

Obtenção de informações de negócios do histórico

O processamento de negócios possui as mesmas características que o processamento das ordens históricas. O número de negócios no histórico de comercialização e o resultado da execução de HistoryDealsTotal(), depende de quanto do histórico de comercialização foi carregado no cache pela função HistorySelect(start, end) ou HistorySelectByPosition().

Para preencher o cache com apenas um negócio pelo seu bilhete, use a função HistoryDealSelect(ticket).

// --- determining the time intervals of the required trading history
   datetime end=TimeCurrent();                // current sever time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning for 24 hours ago
//--- request in the cache of the program the needed interval of the trading history
   HistorySelect(start,end);
//--- obtain the number of deals in history
   int deals=HistoryDealsTotal();
//--- obtain the ticket for the deal, which has the last index in the list
   ulong deal_ticket=HistoryDealGetTicket(deals-1);
   if(deal_ticket>0) // we obtained in the cache of the deal, and work with it
     {
      //--- the ticket order, based on which the deal was made
      ulong order     =HistoryDealGetInteger(deal_ticket,DEAL_ORDER);
      long order_magic=HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
      long pos_ID     =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
      PrintFormat("Deal #%d for the order #%d with the ORDER_MAGIC=%d  that participated in the position",
                  deals-1,order,order_magic,pos_ID);
     }
   else              // unsuccessful attempt of obtaining a deal
     {
      PrintFormat("In total, in the history %d of deals, we couldn't select a deal"+
                  " with the index %d. Error %d",deals,deals-1,GetLastError());
     }

Em casos mais gerais, é necessária a busca no circuito de negócio a partir do cache e fazer análise deles. O algoritmo geral será o seguinte:

  1. Determine os limites do histórico suficientes, se o histórico é carregado pela função HistorySelect(start, end) - não é recomendado carregar todo o histórico de comercialização no cache;
  2. Carregue no cache do programa o histórico de comercialização das funções HistorySelect() ou HistorySelectByPosition();
  3. Obtenha o número total de negócios no cache, usando a função HistoryDealsTotal().
  4. Organize o circuito através de pesquisa de todas os negócios, pelo seus números na lista;
  5. Determine o bilhete do próximo negócio no cache, usando o HistoryDealGetTicket ();
  6. Obtenha as informações do negócio a partir do cache, usando as funções HistoryDealGetDouble(), HistoryDealGetInteger() e HistoryDealGetString(). Se necessário, analise os dados obtidos e tome as devidas ações.

Um exemplo de tal algoritmo para calcular os ganhos e perdas:

input long my_magic=111;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// --- determine the time intervals of the required trading history
   datetime end=TimeCurrent();                 // current server time
   datetime start=end-PeriodSeconds(PERIOD_D1);// set the beginning time to 24 hours ago

//--- request in the cache of the program the needed interval of the trading history
   HistorySelect(start,end);
//--- obtain the number of deals in the history
   int deals=HistoryDealsTotal();

   int returns=0;
   double profit=0;
   double loss=0;
//--- scan through all of the deals in the history
   for(int i=0;i<deals;i++)
     {
      //--- obtain the ticket of the deals by its index in the list
      ulong deal_ticket=HistoryDealGetTicket(i);
      if(deal_ticket>0) // obtain into the cache the deal, and work with it
        {
         string symbol             =HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
         datetime time             =HistoryDealGetInteger(deal_ticket,DEAL_TIME);
         ulong order               =HistoryDealGetInteger(deal_ticket,DEAL_ORDER);
         long order_magic          =HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
         long pos_ID               =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
         ENUM_DEAL_ENTRY entry_type=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);

         //--- process the deals with the indicated DEAL_MAGIC
         if(order_magic==my_magic)
           {
            //... necessary actions
           }

         //--- calculate the losses and profits with a fixed results
         if(entry_type==DEAL_ENTRY_OUT)
          {
            //--- increase the number of deals 
            returns++;
            //--- result of fixation
            double result=HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
            //--- input the positive results into the summarized profit
            if(result>0) profit+=result;
            //--- input the negative results into the summarized losses
            if(result<0) loss+=result;
           }
        }
      else // unsuccessful attempt to obtain a deal
        {
         PrintFormat("We couldn't select a deal, with the index %d. Error %d",
                     i,GetLastError());
        }
     }
   //--- output the results of the calculations
   PrintFormat("The total number of %d deals with a financial result. Profit=%.2f , Loss= %.2f",
               returns,profit,loss);
  }
Observação: sempre consulte cuidadosamente todos os casos da chamada da função HistorySelect()! Carregamento sem consideração e excessivo de todo o histórico de comercialização disponível no cache do programa MQL5 degrada seu desempenho.

Obtenção no cache do histórico pelo identificador da posição (POSITION_IDENTIFIER)

A função HistorySelectByPosition (position_ID) assim como a função HistorySelect (start, end) preenche o cache com negócios e ordens do histórico, mas somente sob uma condição - elas devem ter o identificador especificado da posição (POSITION_IDENTIFIER). O identificador da posição - é um número único que é atribuído automaticamente a cada posição reaberta e não muda ao longo de sua vida. Entretanto, deve-se ter em mente que a alteração da posição (deslocamento do tipo de posição a partir de POSITION_TYPE_BUY POSITION_TYPE_SELL) não altera o identificador de posição.

Cada posição aberta é resultado de um ou mais negócios desse instrumento. Portanto, para analisar as mudanças de posição durante a sua vida, cada negócio e ordem, com base no qual o acordo foi feito, é atribuído um identificador para a posição em que este negócio participou. Dessa forma, conhecendo o identificador das atuais posições abertas, podemos reconstruir todo o histórico - encontrar todas as ordens e negócios que mudaram isso.

A função HistorySelectByPosition(position_ID) serve para poupar o programador de ter que escrever o seu próprio código para repetir através de todo o histórico de negociação em busca de tais informações. Um algoritmo típico para trabalhar com esta função:

  1. Obtenha o identificador correto de posição;
  2. Obtenha, usando a função HistorySelectByPosition() no cache do histórico de comercialização, todas as ordens e negócios, o identificador que se iguala ao identificador da posição atual;
  3. Processe o histórico de comercialização de acordo com o algoritmo.

Conclusão

Todo o subsistema da plataforma de comércio MetaTrader 5 é bem pensado e de fácil utilização. Mais que nunca, a abundância de funções de comércio, permite-nos resolver cada problema específico da forma mais eficiente.

Mas mesmo apesar do fato de classes comerciais especializadas da biblioteca nos permitirem não se preocupar com muitas nuanças e escrever programas em um nível elevado, sem entrar em implementação, a compreensão dos princípios, vai nos permitir criar comércios EAs mais confiáveis e eficientes.

Todos os exemplos dados podem ser encontrados nos arquivos ligados a este artigo.

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

Guia para testes e otimização de Expert Advisors no MQL5 Guia para testes e otimização de Expert Advisors no MQL5

Este artigo explica o processo passo-a-passo de identificação e solução de erros de código, assim como os passos para testar e otimizar os parâmetros de entrada do Expert Advisor. Você aprenderá como utilizar o Strategy Tester do terminal do cliente MetaTrader 5 para descobrir o melhor símbolo e definição dos parâmetros de entrada para seu Expert Advisor.

Sistemas de negociação adaptativos e seu uso no terminal do cliente do MetaTrader 5 Sistemas de negociação adaptativos e seu uso no terminal do cliente do MetaTrader 5

Este artigo sugere uma variável de um sistema adaptativo que consiste em várias estratégias, cada uma realizando suas operações de negociação "virtuais". Negociações reais são realizadas de acordo com os sinais da estratégia mais rentável no momento. Obrigado por utilizar a abordagem orientada a objeto, classes para trabalhar com dados e classes de negociação da biblioteca padrão, a arquitetura do sistema pareceu ser simples e escalável; agora você pode facilmente criar e analisar os sistemas adaptativos que incluem centenas de estratégias de negociação.

Eventos de negociação no MetaTrader 5 Eventos de negociação no MetaTrader 5

Um monitoramento do estado atual de uma conta de negócio implica no controle das posições abertas e ordens. Antes de um sinal de negócio se tornar um negócio, ele deve ser enviado a partir de um terminal de cliente como uma solicitação para o servidor de negócio, onde será posicionado na fila de ordem aguardando ser processado. Aceitação de uma solicitação por um servidor de negócio, a excluindo quando expirar ou realizando um negócio em sua base - todas essas ações são seguidas por eventos de negócio; e o servidor de negócio informa o terminal sobre eles.

Princípios básicos dos testes no MetaTrader 5 Princípios básicos dos testes no MetaTrader 5

Quais são as diferenças entre os três modos de teste no MetaTrader 5 e o que deve ser especialmente buscado? Como o teste de um EA, negociando simultaneamente em múltiplos instrumentos, acontece? Quando e como os valores dos indicadores são calculados durante o teste e como os eventos são manuseados? Como sincronizar as barras a partir de diferentes instrumentos durante o teste no modo "apenas preços abertos"? Este artigo tem como objetivo fornecer respostas a estas e muitas outras questões.