Negociação Bidirecional e de Cobertura de Posições no MetaTrader 5 Através da API HedgeTerminal, Parte 2

Vasiliy Sokolov | 23 junho, 2015

Índice


Introdução

Este artigo é uma continuação da primeira parte "Negociação Bidirecional e de Cobertura de Posições no MetaTrader 5 Através do Painel de HedgeTerminal, Parte 1". Na segunda parte, vamos discutir a integração dos Expert Advisors e de outros programas em MQL5 com a biblioteca HedgeTerminalAPI. Leia este artigo para aprender a trabalhar com a biblioteca. Ele irá ajudá-lo a criar Expert Advisors de negociação bidirecional enquanto você ainda estiver trabalhando em um ambiente de negociação confortável e simples.

Além da descrição da biblioteca, o artigo aborda os fundamentos da negociação assíncrona e da programação multitarefa. Estas descrições são dadas na terceira e quarta seções deste artigo. Portanto, este material será útil para os traders que não estão interessados ​​na negociação bidirecional, mas que gostariam de descobrir algo novo sobre a negociação assíncrona e a programação multitarefas.

O material apresentado abaixo é destinado para os traders experientes com algoritmos, que conhecem a linguagem de programação MQL5. Se você não conhece a MQL5, leia a primeira parte do artigo, que contém diagramas simples e desenhos explicando o princípio geral da biblioteca e do painel HedgeTerminal.


Capítulo 1. Interação dos Expert Advisors com o HedgeTerminal e seu painel

1.1. Instalação do HedgeTermianlAPI. O Começo da Biblioteca

O processo de instalação do HedgeTerminalAPI difere da instalação de painel visual HT, já que a biblioteca não pode por si só funcionar no MetaTrader 5. Em vez disso, você vai precisará desenvolver um Expert Advisor especial para chamar a função HedgeTerminalInstall() a partir da biblioteca. Esta função irá definir um arquivo de cabeçalho especial Prototypes.mqh descrevendo as funções disponíveis no HedgeTerminalAPI.

A biblioteca é instalada no computador em três passos:

Passo 1. Faça o download da biblioteca HedgeTerminalAPI em seu computador. Localização da biblioteca em relação ao seu terminal: \MQL5\Experts\Market\HedgeTerminalApi.ex5.

Passo 2. Criar um novo Expert Advisor no Assistente MQL5 usando um modelo padrão. O Assistente MQL5 gera o seguinte código fonte:

//+------------------------------------------------------------------+
//|                                   InstallHedgeTerminalExpert.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"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }

Passo 3. Você vai precisar apenas de uma função do Expert Advisor resultante - OnInit() e a diretiva de exportação descrevendo a função de instalação especial HedgeTerminalInstall() exportada pela biblioteca HedgeTerminalApi. Execute esta função corretamente na função OnInit(). O código fonte marcado em amarelo executa essas operações:

//+------------------------------------------------------------------+
//|                                   InstallHedgeTerminalExpert.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"

#import "HedgeTerminalAPI.ex5"
   void HedgeTerminalInstall(void);
#import

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   HedgeTerminalInstall();
   ExpertRemove();   
//---
   return(INIT_SUCCEEDED);
  }

Passo 4. Suas novas ações dependem se você comprou ou não a biblioteca. Se você comprou, você poderá executar o Expert Advisor em tempo real diretamente do gráfico. Isto irá iniciar o instalador padrão de toda a linha de produtos do HedgeTerminal. Você pode facilmente concluir esta etapa, seguindo as instruções descritas nas seções 2.1 e 2.2 do artigo "Negociação Bidirecional e de Cobertura de Posições no MetaTrader 5 Através do Painel de HedgeTerminal, Parte 1". O assistente de instalação instala todos os arquivos necessários, incluindo o arquivo de cabeçalho e o Expert Advisor de teste em seu computador.

Se você ainda não comprou a biblioteca e gostaria apenas de testá-la, então, a operação do EA em tempo real não estará disponível para você, mas você poderá testar o API executando o EA no Strategy Tester. O instalador não funcionará neste caso. No modo de teste, o HedgeTermianalAPI trabalha em um modo de usuário único, por isso, ele não precisa dos arquivos instalados no modo normal. Isso significa que você não precisará configurar qualquer outra coisa.

Assim que o teste do EA for feito, a pasta \HedgeTerminal é criada na pasta common do terminal. O caminho normal para a pasta common dos terminais MetaTrader é c:\Users\<Username>\AppData\Roaming\MetaQuotes\Terminal\Common\Files\HedgeTerminal\, onde o <nome do usuário> é o nome de sua conta do computador atual. A pasta \HedgeTerminal já contém os arquivos \MQL5\Include\Prototypes.mqh e \MQL5\Experts\Chaos2.mq5. Copie esses arquivos nos mesmos diretórios de seu terminal: o arquivo Prototypes.mqh para \MetaTrader5\MQL5\Include, e o arquivo Chaos2.mq5 para \MetaTrader5\MQL5\Experts.

O arquivo Prototypes.mqh é um arquivo de cabeçalho contendo a descrição das funções exportadas a partir da biblioteca HedgeTerminalAPI. Sua finalidade e descrições estão contidos nos comentários a eles.

O arquivo Chaos2.mq5 contém o exemplo do EA descrito na seção "A Função SendTradeRequest e a Estrutura HedgeTradeRequest através através do EA de Exemplo Chaos II". Desta forma, você poderá entender visualmente o funcionamento do HedgeTerminalAPI e como desenvolver um Expert Advisor utilizando a tecnologia de virtualização do HedgeTerminal.

Os arquivos copiados estão disponíveis para o seus EAs. Assim, você só precisa incluir o arquivo de cabeçalho no código fonte do Expert Advisor para começar a usar a biblioteca. Aqui está um exemplo:

#include <Prototypes.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   int transTotal = TransactionsTotal();
   printf((string)transTotal);
  }

Por exemplo, o código acima obtém o número total de posições ativas e exibe o número na guia "Experts" do terminal MetaTrader 5.

É importante compreender que o HedgeTerminal é realmente inicializado na primeira chamada de uma de suas funções. Esta inicialização é chamada de lazy (preguiçosa). Portanto, a primeira chamada de uma de suas funções pode levar um longo tempo. Se você quer uma resposta rápida durante a sua primeira execução, você deve inicializar o HT com antecedência, por exemplo, você pode chamar a função TransactionTotal() função no bloco da OnInit().

Com a inicialização preguiçosa você pode omitir a inicialização explícita do Expert Advisor. Isso simplifica muito o trabalho com o HedgeTerminal, tornando desnecessário a sua pré-configuração.


1.2. Integração dos Expert Advisors com o Painel HedgeTerminal

Se você tem o painel visuai HedgeTerminal e a versão completa da biblioteca, que pode ser executada em tempo real, você pode integrar os seus Expert Advisors com o painel de modo que todas as operações de negociação realizadas por eles também apareçam no painel. Em geral, a integração não é visível. Se você usar as funções HedgeTermianalAPI, as ações realizadas pelos robôs são automaticamente exibidas no painel. No entanto, você poderá expandir a visibilidade, indicando o nome do EA em cada transação confirmada. Você pode fazer isso descomentando a linha abaixo no arquivo Settings.xml:

<Column ID="Magic" Name="Magic" Width="100"/>

Esta marcação está nas seções <Active-Position> e <History-Position>.

Agora, os comentários são removidos, e as marcações estão incluídas no processamento. Após o reinício do painel, a nova coluna "Magic" aparecerá nas tabelas de posições ativas e do histórico. A coluna contém o número mágico do Expert Advisor, para quem a posição pertence.

Se você quer mostrar o nome do EA, em vez de seu número mágico, adicione o nome ao arquivo de alias ExpertAliases.xml. Por exemplo, o número mágico de um EA é 123847 e você deseja exibir o seu nome, como "Expro 1.1", Adicione a seguinte marca para o arquivo:

<Expert Magic="123847" Name="ExPro 1.1"></Expert>

Se for feito corretamente, o nome do EA será exibido em vez de seu número mágico na coluna apropriada:

Fig. 1.  Exibindo o nome do EA, em vez do número Mágico

Fig. 1. Exibindo o nome do EA, em vez do número Mágico

Note que o painel e os Expert Advisors se comunicam em tempo real. Isto significa que se você fechar a posição de um EA diretamente no painel, o EA saberá sobre isso com a próxima chamada da função TransactionsTotal(). E vice-versa: após o Expert Advisor fechar sua posição, ela desaparecerá imediatamente a partir da guia de posições ativas.


1.3. Princípios Gerais da Operação do HedgeTerminalAPI

Além das posições bidirecionais, o HedgeTerminal trabalha também com os outros tipos de negociação, tais como ordens pendentes, negociações e operações da corretora. O HedgeTerminal trata todos estes tipos como um único grupo de transações. Uma negociação, uma ordem pendente, uma posição bidirecional - todos eles são transações. No entanto, uma transação não pode existir sozinha. Em termos de programação orientada a objeto, uma transação pode ser introduzida como uma classe base abstrata, a partir da qual todas as entidades de negociações possíveis, tais como negociações e posições bidirecionais, são herdadas. A este respeito, todas as funções de HedgeTerminalAPI podem ser divididas em vários grupos:

  1. Funções de busca e seleção da transação. A assinatura comum das funções e a forma como elas funcionam coincidem quase que completamente com as funções OrderSend() e OrderSelect() do MetaTrader 4;
  2. Funções para obter as propriedades de uma transação selecionada. Toda a transação tem um conjunto específico de propriedades e funções específicas para a seleção da propriedade. A assinatura comum das funções e da forma como elas trabalham se assemelham com as funções do sistema MetaTrader 5 da maneira como eles acessam as posições, negociações e ordens (como OrderGetDouble() ou HistoryDealGetInteger());
  3. HedgeTerminalAPI usa apenas uma função de negociação: SendTradeRequest(). Esta função permite fechar uma posição bidirecional ou parte dela. A mesma função é utilizada para modificar o stop loss, take profit ou o comentário de saída. Trabalhar com essa função é semelhante à OrderSend() do MetaTrader 5;
  4. A função para obter os erros comuns GetHedgeError(), funções para a análise detalhada das ações de negociação HedgeTerminal: TotalActionsTask() e GetActionResult(). Ela também é usada para a detecção de erros. Não há análogos no MetaTrader 4 ou MetaTrader 5.

Trabalhar com quase todas as funções é semelhante ao uso das funções do sistema MetaTrader 4 e MetaTrader 5. Como regra geral, a entrada da função é um identificador (valor de enumeração), e a função retorna um valor que corresponde a ele.

As enumerações específicas estão disponíveis para cada função. A assinatura de chamada comum é a seguinte:

<value> = Function(<identifier>);

Vamos considerar um exemplo para obter um identificador de posição única. Esta é a linha que se parece no MetaTrader 5:

ulong id = PositionGetInteger(POSITION_IDENTIFIER);

No HedgeTerminal, ele recebe um identificador exclusivo de uma posição bidirecional como se segue:

ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)

Os princípios gerais das funções são as mesmas. Somente os tipos de enumerações que são diferentes.


1.4. Seleção de Transações

A seleção de uma transação atravessará a lista de transações, que é semelhante a busca de ordens no MetaTrader 4. No entanto, no MetaTrader 4 somente as ordens são procuradas, enquanto que no HedgeTerminal tudo pode ser encontrado como uma transação - como uma ordem pendente ou uma posição de cobertura. Portanto, cada transação deve primeiro ser selecionada usando a função TransactionSelect() e, em seguida, o seu tipo deve ser identificado por meio da TransactionType().

Duas listas de transações estão disponíveis até a data: transações ativas e do histórico. A lista a ser aplicada é definida com base no modificador ENUM_MODE_TRADES. Ela é semelhante ao modificador MODE_TRADES no MetaTrader 4.

O Algoritmo de busca por transação e de seleção é o seguinte:

1: for(int i=TransactionsTotal(MODE_TRADES)-1; i>=0; i--)
2:     {
3:      if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))continue;
4:      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;
5:      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;
6:      if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue;
7:      if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN)continue;
8:      ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)
9:     }

O código percorre a lista de transações ativas no ciclo for (Linha 1). Antes de prosseguir com a transação, selecione-o usando a TransactionSelect() (Linha 3). Somente as posições bidirecionais são selecionados a partir dessas transações (linha 4). Se o número mágico da posição e seu símbolo não corresponderem ao número mágico do EA atual e o símbolo em que ele está sendo executado, HT se move para a próxima posição (linhas 5 e 6). Em seguida, ele define o identificador de posição única (linha 8).

Uma atenção especial deve ser dada para a linha 7. As posições selecionadas devem ser verificadas em termos da possibilidade de modificação. Se a posição já está em processo de modificação, ele não pode ser alterado no segmento atual, embora você pode obter uma de suas propriedades. Se a posição estiver bloqueada, melhor esperar até que ele seja liberada para acessar suas propriedades ou tentar modificá-la novamente. A propriedade HEDGE_POSITION_STATE é usada para descobrir se a modificação da posição é possível.

O modificador POSITION_STATE_FROZEN indica que a posição está "congelada" e não pode ser alterada. O modificador POSITION_STATE_ACTIVE mostra que é uma posição ativa e pode ser alterada. Estes modificadores são listados na enumeração ENUM_HEDGE_POSITION_STATE, que está documentada na seção apropriada.

Se for necessária uma busca através das transações históricas, o modificador MODE_TRADES nas funções TransactionTotal() e TransactionSelect() deve ser substituído por MODE_HISTORY.

Uma operação no HedgeTerminal pode ser agrupado em outro. Isto é muito diferente do conceito do MetaTrader 5, onde não existe agrupamento. Por exemplo, a posição histórica bidirecional no HedgeTerminal consiste de duas ordens, cada uma das quais incluem um conjunto arbitrário de negociações. O agrupamento pode ser representado da seguinte forma:

Fig. 2. Transações agrupadas

Fig. 2. Transações agrupadas

O agrupamento de transações é claramente visto no painel visual HedgeTerminal.

A imagem abaixo mostra os detalhes de uma posição de MagicEx 1.3:

Fig. 3. Transações agrupadas no painel HedgeTerminal

Fig. 3. Transações agrupadas no painel HedgeTerminal

Você pode acessar as propriedades de uma ordem em particular ou um negócio na posição bidirecional.

Para fazer isso:

  1. Selecione uma transação histórica e certifique-se de que ela é uma posição bidirecional;
  2. Selecione uma das ordens desta posição usando HedgeOrderSelect();
  3. Obtenha uma das propriedades da ordem selecionada: o número de negócios que ela contém;
  4. Escolha um dos negócios pertencentes à ordem buscando através das negociações;
  5. Obtenha a propriedade do negócio solicitada.

Note que após a transação ter sido selecionada, suas propriedades específicas tornam-se disponíveis para ele. Por exemplo, se a transação for uma ordem, então, após a seleção através da HedgeOrderSelect(), você poderá encontrar o número de negócios para ele (HedgeOrderGetInter(HEDGE_ORDER_DEALS_TOTAL)) ou o preço de entrada médio ponderado (HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED)).

Vamos descobrir o preço do negócio #1197610, que está marcado em vermelho na imagem. Este negócio é parte da posição bidirecional do EA MagicEx 1.3.

Através do código abaixo, o EA pode acessar a sua posição e esta negociação:

#include <Prototypes.mqh>

ulong Magic=5760655; // MagicEx 1.3.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnTick()
  {
   for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
    {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;        // Select transaction #i;
      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;                 // If transaction is not position - continue;
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;  // If position is not main - continue;
      ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID);   // Get id for closed order;
      if(id!=5917888)continue;                                             // If id of position != 5917888 - continue;
      printf("1: -> Select position #"+(string)id);                        // Print position id;
      if(!HedgeOrderSelect(ORDER_SELECTED_CLOSED))continue;                // Select closed order or continue;    
      ulong order_id = HedgeOrderGetInteger(HEDGE_ORDER_ID);               // Get id closed order;
      printf("2: ----> Select order #" + (string)order_id);                // Print id closed order;
      int deals_total = (int)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);// Get deals total in selected order;
      for(int deal_index = deals_total-1; deal_index >= 0; deal_index--)   // Search deal #1197610...
        {
         if(!HedgeDealSelect(deal_index))continue;                         // Select deal by index or continue;
         ulong deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID);               // Get id for current deal;
         if(deal_id != 1197610)continue;                                   // Select deal #1197610;
         double price = HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED);     // Get price executed;
         printf("3: --------> Select deal #"+(string)deal_id+              // Print price excecuted;
              ". Executed price = "+DoubleToString(price,0));
        }
     }
  }

Após a execução do código, a seguinte entrada será criada na guia Experts do terminal Metatrader 5:

2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      3: --------> Select deal #1197610. Executed price = 4735
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      2: ----> Select order #6389111
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      1: -> Select position #5917888

O EA seleciona primeiro a posição #5917888, e em seguida, seleciona a ordem #6389111 dentro da posição. Uma vez que a ordem é selecionada, o EA começa a procurar o número do negócio 1197610. Quando o negócio for encontrado, o EA recebe o seu preço de execução e adiciona o preço no diário.


1.5. Como Obter os Códigos de Erro Usando a GetHedgeError()

Erros e situações imprevistas podem ocorrer ao trabalhar com o ambiente do HedgeTerminal. As funções de obtenção e análise do erro são utilizadas nestes casos.

O caso mais simples quando você receber um erro é quando você se esquecer de selecionar uma transação usando a função TransactionSelect(). A função TransactionType(), neste caso, retornará o modificador TRANS_NOT_DEFINED.

Para entender onde está o problema, é preciso obter o modificador do último erro. O modificador vai nos dizer agora qual transação que tinha sido selecionado. O código a seguir faz isso:

for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
  {
   //if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;        // forgot to select;
   ENUM_TRANS_TYPE type = TransactionType();
   if(type == TRANS_NOT_DEFINED)
   {
      ENUM_HEDGE_ERR error = GetHedgeError();
      printf("Error, transaction type not defined. Reason: " + EnumToString(error));
   }
  }

Esta é a mensagem resultante:

Error, transaction type not defined. Reason: HEDGE_ERR_TRANS_NOTSELECTED

O ID do erro sugere que nos esquecemos de selecionar uma transação antes de tentar obter o seu tipo.

Todos os possíveis erros estão listados na estrutura ENUM_HEDGE_ERR.


1.6. Análise Detalhada da Negociação e Identificação dos Erros Usando TotalActionsTask() e GetActionResult()

Além dos erros que ocorrem no processo de trabalho com o ambiente HedgeTerminal, os erros de negociação podem ocorrer como um resultado da chamada SendTradeRequest(). Estes tipos de erros são mais difíceis de lidar. Uma tarefa realizada pela SendTradeRequest() pode conter várias atividades de negociação (subtarefas). Por exemplo, para alterar o comentário de saída em uma posição ativa protegida por um nível de stop loss, você deve fazer duas ações de negociação:

  1. Cancelar a ordem de stop pendente que representa o nível do stop;
  2. Colocar uma nova ordem de stop pendente com um novo comentário no lugar da ordem anterior.

Se a nova ordem de stop disparar, então seu comentário será exibido como um comentário de fechando a posição, o que é uma maneira correta.

No entanto, a tarefa pode ser executada em parte. Suponha que, a ordem pendente é cancelada com êxito, mas ao colocar a nova ordem ocorre uma falha por qualquer motivo. Neste caso, a posição irá ser deixada sem o nível de stop loss. A fim de ser capaz de lidar com esse erro, o EA precisará chamar um registro de tarefa especial e fazer uma busca nele para encontrar a sub-tarefa que falhou.

Isto é feito utilizando duas funções: A TotalActionsTask() retorna o número total de ações de negociação (subtarefas) dentro desta tarefa; e a GetActionResult() aceita o índice de sub-tarefas e retorna seu tipo e seu resultado de execução. Uma vez que todas as operações de negociação são realizadas usando a ferramenta padrão MetaTrader 5, o resultado do seu desempenho corresponde ao código de retorno do servidor de negociação.

Em geral, o algoritmo de busca para a causa da falha é a seguinte:

  1. Obter o número total de sub-tarefas na tarefa usando a TotalActionsTask();
  2. Buscar através de todas as subtarefas no loop for. Determinar o tipo de cada sub-tarefa e seu resultado.

Suponha que a ordem de stop com um novo comentário não pôde ser colocado porque o preço de execução das ordens estava muito perto do preço atual.

O código de exemplo abaixo mostra como o EA poderia encontrar a razão para esta falha:

#include <Prototypes.mqh> 

ulong Magic=5760655; // MagicEx 1.3.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnTick()
  {
//detect active position
   for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
     {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;
      ENUM_TRANS_TYPE type=TransactionType();
      if(type==TRANS_NOT_DEFINED)
        {
         ENUM_HEDGE_ERR error=GetHedgeError();
         printf("Error, transaction not defined. Reason: "+EnumToString(error));
        }
      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;
      if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue;
      HedgeTradeRequest request;
      request.action=REQUEST_MODIFY_COMMENT;
      request.exit_comment="My new comment";
      if(!SendTradeRequest(request)) // Is error?
        {
         for(uint action=0; action < TotalActionsTask(); action++)
           {
            ENUM_TARGET_TYPE typeAction;
            int retcode=0;
            GetActionResult(action, typeAction, retcode);
            printf("Action#" + (string)action + ": " + EnumToString(type) +(string)retcode);
           }
        }
     }
  }

A seguinte mensagem irá aparecer após a execução do código:

Action #0 TARGET_DELETE_PENDING_ORDER 10009 (TRADE_RETCODE_PLACED)
Action #1 TARGET_SET_PENDING_ORDER 10015 (TRADE_RETCODE_INVALID_PRICE)

Ao comparar os números com os modificadores padrão de códigos de retorno do servidor de negociação, encontramos que a ordem pendente foi removida com sucesso, mas a colocação de uma nova ordem falhou. O servidor de negociação retornou um erro 10015 (preço incorreto), o que pode significar que o preço atual é muito próximo do nível de stop.

Sabendo disso, o EA pode ter controle sobre os níveis de stop. Para isso, o EA só terá que fechar esta posição usando a mesma função SendTradeRequest().


1.7. Acompanhando o Estado de Execução da Tarefa de Negociação

Cada tarefa de negociação pode consistir em qualquer número de sub-tarefas que devem ser executadas sequencialmente.

No modo assíncrono, uma tarefa pode ser realizada em vários passos do código. Também pode haver casos em que a tarefa pode "congelar". Por isso é necessário o controle do EA sobre a execução da tarefa. Ao chamar a função HedgePositionGetInteger() com o modificador HEDGE_POSITION_TASK_STATUS, ele retorna o tipo de enumeração ENUM_TASK_STATUS contendo o estado da tarefa atual da posição.

Por exemplo, se algo der errado depois de enviar uma ordem para fechar uma posição, então você precisará obter o status da tarefa, devido a posição não estar fechando.

O exemplo a seguir mostra o código que um Expert Advisor assíncrono pode executar para analisar o status da tarefa para a posição:

ENUM_TASK_STATUS status=HedgePositionGetInteger(HEDGE_POSITION_TASK_STATUS);
switch(status)
  {
   case TASK_STATUS_COMPLETE:
      printf("Task complete!");
      break;
   case TASK_STATUS_EXECUTING:
      printf("Task executing. Waiting...");
      Sleep(200);
      break;
   case TASK_STATUS_FAILED:
      printf("Filed executing task. Print logs...");
      for(int i=0; i<TotalActionsTask(); i++)
        {
         ENUM_TARGET_TYPE type;
         uint retcode;
         GetActionResult(i,type,retcode);
         printf("#"+i+" "+EnumToString(type)+" "+retcode);
        }
      break;
   case TASK_STATUS_WAITING:
      printf("task will soon start.");
      break;
  }

Note que a execução de algumas tarefas complexas requer múltiplas iterações.

No modo assíncrono, os próximos eventos que sinalizam as mudanças no ambiente de negociação iniciam uma nova iteração. Assim, todas as iterações são realizadas sem demora, uma após a outra, na sequência de respostas recebidas a partir do servidor de negociação. A execução da tarefa difere do modo síncrono.

O método síncrono utiliza o emulador de operação síncrona, que os usuários podem executar tarefas mesmo compostas em uma única passagem. O emulador usa defasagens de tempo. Por exemplo, após a execução de uma subtarefa iniciar, o emulador não retorna o segmento de execução para o EA. Em vez disso, ele espera por algum tempo esperando o ambiente de negociação mudar. Depois disso, ele faz a releitura do ambiente de negociação novamente. Se ele entende que a sub-tarefa foi concluída com sucesso, ele começa as próximos subtarefas.

Este processo reduz um pouco o desempenho geral, pois leva algum tempo para esperar. Mas ele deixa a execução de tarefas mais complexas em uma operação seqüencial bastante simples realizada em uma única chamada da função. Portanto, você quase nunca precisa analisar o log de execução da tarefa no método síncrono.


1.8. Como Modificar e Fechar Posições Bidirecionais

Posições bidirecionais são modificadas e fechadas usando a função SendTradeRequest(). Apenas três opções pode ser aplicada a uma posição ativa:

  1. Uma posição pode ser totalmente ou parcialmente fechada;
  2. O Stop loss da posição e o take profit pode ser modificado;
  3. O comentário de saída da posição pode ser alterado.

A posição histórica não pode ser mudada. Semelhante a função OrderSend() no MetaTrader 5, a SendTradeRequest() usa uma consulta pré-compilada sob a forma da estrutura de negociação HedgeTraderRequest. Leia a documentação para obter mais detalhes sobre a função SendTradeRequest() e a estrutura HedgeTraderRequest. O exemplo que mostra a modificação e o fechamento da posição está disponível na seção do EA Chaos II.


1.9. Como Definir as Propriedades do HedgeTerminal de um Expert Advisor

HedgeTerminal possui um conjunto de propriedades, tais como freqüência de atualização, o número de segundos para aguardar uma resposta do servidor e outros.

Todas estas propriedades são definidas em Settings.xml. Quando um EA está sendo executado em tempo real, a biblioteca lê as propriedades do arquivo e define os parâmetros internos adequados. Quando o EA é testado em um gráfico, o arquivo Settings.xml não é utilizado. No entanto, em algumas situações, você pode precisar modificar individualmente as propriedades do EA independentemente se ele está sendo executado em um gráfico ou no strategy tester.

Isto é feito através do conjunto especial de funções HedgePropertySet ... A versão atual apresenta apenas um protótipo deste conjunto:

enum ENUM_HEDGE_PROP_INTEGER
{
   HEDGE_PROP_TIMEOUT,
};

bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, int value)

Por exemplo, para definir o tempo limite para a biblioteca esperar por uma resposta do servidor, escreva o seguinte:

bool res = HedgePropertySetInteger(HEDGE_PROP_TIMEOUT, 30);

Se a resposta do servidor não for recebida dentro de 30 segundos depois de enviar uma solicitação assíncrona, a posição de bloqueio será liberada.


1.10. Modos de Operações Síncronas e Assíncronas

HedgeTerminal e sua API realizam as atividades de negociação de forma completamente assíncrona.

No entanto, este modo requer uma lógica mais complexa dos EAs. Para ocultar essa complexidade, o HedgeTerminalAPI inclui um emulador especial de operação síncrona, permitindo que os EAs desenvolvidos em um método síncrono convencional se comunique com os algoritmos assíncronos do HedgeTerminalAPI. Esta interação é revelada no momento da alteração das posições bidirecionais e fechamento através da SendTradeRequest(). Esta função permite executar a tarefa de negociação tanto no modo síncrono quanto no assíncrono. Por padrão, todas as ações de negociação são executadas de forma síncrona através do emulador de operação síncrona. No entanto, se uma solicitação de negociação (estrutura HedgeTradeRequest) contém uma flag explicitamente específica asynch_mode = true, a tarefa de negociação será realizada no modo assíncrono.

No modo assíncrono, as tarefas são executadas de forma independente do segmento principal. A Implementação da interação entre um EA assíncrono e dos algoritmos assíncronos do HedgeTerminal ainda não está completa.

O emulador síncrono é muito simples. Ele começa as subtarefas sequencialmente, e aguarda por algum tempo até que o ambiente de negociação do MetaTrader 5 se altere. O emulador analisa estas alterações e determina o estado da tarefa atual. Se a execução da tarefa for bem-sucedida, o emulador se move para a próxima.

O Emulador síncrono causa pequenos atrasos na execução de ordens de negociação. Isto é devido ao fato de que o ambiente de negociação no MetaTrader 5 leva algum tempo para refletir as atividades de negociação executadas. A necessidade de acesso ao ambiente está principalmente ligada ao fato de que HedgeTermianlAPI não pode acessar os eventos vindo do manipulador OnTradeTransaction() no modo de emulação síncrona.

Os problemas de interação entre segmentos assíncronos, bem como entre os segmentos assíncrono e síncronos através da emulação são muito complicados e não têm soluções óbvias.


1.11. Gerenciamento das Propriedades da Posição Bidirecional através de um Script de Exemplo

No script abaixo, a função TransactionSelect() procura por todas as operações disponíveis na lista de transações ativas.

Cada transação é selecionada a partir da lista. Se a transação é uma posição, algumas de suas propriedades são acessados ​​e, em seguida, impressas. Em adição às propriedades das posições, as propriedades de ordens e negociações no interior da posição também são impressos. Uma ordem e uma negociação são selecionados primeiro utilizando a HedgeOrderSelect() e HedgeDealSelect(), respectivamente.

Todas as propriedades da posição, as suas ordens e negociações são combinadas e impressas como uma única linha utilizando a função do sistema printf.

//+------------------------------------------------------------------+
//|                                           sample_using_htapi.mq5 |
//|         Copyright 2014, Vasiliy Sokolov, Russia, St.-Petersburg. |
//|                              https://login.mql5.com/ru/users/c-4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Vasiliy Sokolov."
#property link      "https://login.mql5.com/ru/users/c-4"
#property version   "1.00"

// Include prototypes function of HedgeTerminalAPI library.
#include <Prototypes.mqh> 

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnStart()
  {
   // Search all transaction in list transaction...
   for(int i=TransactionsTotal(); i>=0; i--)
     {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))                           // Selecting from active transactions
        {
         ENUM_HEDGE_ERR error=GetHedgeError();                                      // Get reason if selecting has failed
         printf("Error selecting transaction # "+(string)i+". Reason: "+            // Print reason
                EnumToString(error));
         ResetHedgeError();                                                         // Reset error
         continue;                                                                  // Go to next transaction
        }
      // Only for hedge positions
      if(TransactionType()==TRANS_HEDGE_POSITION) 
        {
         // --- Position captions --- //
         ENUM_TRANS_DIRECTION direction=(ENUM_TRANS_DIRECTION)                      // Get direction caption
                              HedgePositionGetInteger(HEDGE_POSITION_DIRECTION);
         double price_entry = HedgeOrderGetDouble(HEDGE_ORDER_PRICE_EXECUTED);      // Get volume of positions
         string symbol = HedgePositionGetString(HEDGE_POSITION_SYMBOL);             // Get symbol of position
         // --- Order captions --- //
         if(!HedgeOrderSelect(ORDER_SELECTED_INIT))continue;                        // Selecting init order in position
         double slippage = HedgeOrderGetDouble(HEDGE_ORDER_SLIPPAGE);               // Get some slippage was
         uint deals_total = (uint)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);    // Get deals total
         // --- Deals captions --- //
         double commissions=0.0;
         ulong deal_id=0;
         //Search all deals in list deals...
         for(uint d_index=0; d_index<deals_total; d_index++)                        
           {
            if(!HedgeDealSelect(d_index))continue;                                  // Selecting deal by its index
            deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID);                           // Get deal id
            commissions += HedgeDealGetDouble(HEDGE_DEAL_COMMISSION);               // Count commissions
           }
         int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
         printf("Position #" + (string)i + ": DIR " + EnumToString(direction) +     // Print result line
         "; PRICE ENTRY " + DoubleToString(price_entry, digits) + 
         "; INIT SLIPPAGE " + DoubleToString(slippage, 2) + "; LAST DEAL ID " +
         (string)deal_id + "; COMMISSIONS SUM " + DoubleToString(commissions, 2));
        }
     }
  }

1.12. A Função SendTradeRequest e a Estrutura HedgeTradeRequest através do Exemplo do EA Chaos II

Como exemplo, vamos desenvolver um robô de negociação baseado nas táticas de negociação propostas pelo Bill Williams em seu livro Trading Chaos. Second Edition.

Nós não vamos seguir todas as suas recomendações, mas simplificar o sistema omitindo o indicador Alligator e algumas outras condições. A escolha desta estratégia decorre de várias considerações. A principal delas é que essa estratégia inclui táticas de manutenção de posição compostas. Às vezes você precisa fechar uma parte de uma posição e mover o stop loss para o ponto de equilíbrio.

Quando mudou-se para o ponto de equilíbrio, o nível de stop deve ser arrastado seguinte o preço. A segunda consideração é que essa tática é conhecida o suficiente e os indicadores desenvolvidos por ela estão incluídos no pacote padrão do MetaTrader 5. Vamos modificar um pouco e simplificar as regras, para evitar que a lógica complexa do Expert Advisor de impeça os seus objetivos principais: para mostrar um exemplo da interação do EA com a biblioteca HedgeTerminalAPI. A lógica do EA utiliza grande parte das funções de negociação do HedgeTerminalAPI. Este é um bom teste para a biblioteca.

Vamos começar na barra de reversão. Uma barra de reversão de alta é uma barra com o preço de fechamento em sua terceira maior de alta, cuja a mínima é a menor das últimas N barras. Uma barra de reversão de baixa é um barra com o preço de fechamento em sua terceira menor, cuja a máxima é a mais elevada das últimas N barras. N é um parâmetro escolhido aleatoriamente, ele pode ser definido durante o início do Expert Advisor. Isso difere da estratégia clássica "Chaos 2".

Depois que a barra de reversão for definida, duas ordens pendentes são colocadas. Para um barra de alta, as ordens são colocadas acima da sua máxima, a barra de baixa - apenas abaixo de sua mínima. Se essas duas ordens não acionam durante a barra OldPending, o sinal é considerado obsoleto, e as ordens são canceladas. Os valores de OldPending e N são definidos pelo usuário antes de lançar a EA no gráfico.

As ordens disparam e se transformam em duas posições bidirecionais. O EA distingue entre eles por números nos comentários, "#1" e "#2", respectivamente. Esta não é uma solução muito elegante, mas é bom para fins de demonstração. Uma vez que as ordens são disparadas, um stop loss é colocado na máxima (para uma barra de baixa) ou na mínima (para uma barra de alta) de barra de reversão.

A primeira posição tem metas apertadas. O seu take profit é estabelecido de modo que, em caso de disparo, o lucro da posição será igual à perda absoluta de um stop loss acionado. Por exemplo, se uma posição de compra é aberta a um preço de 1.0000, e seu stop loss no nível de 0.9000, o nível de take profit seria 1,0000 + (1,0000-0,9000) = 1,1000. A EA sai da posição no stop loss ou no take profit.

A segunda posição é de longo prazo. Seu stop loss é movido seguinte o preço. O stop se move após a formação de um novo fractal de Bill Williams. Para uma posição de compra, o stop se move de acordo com fractal de baixa, e o fractal superior é utilizado para posições de venda. O EA fecha a posição somente no stop loss.

O gráfico a seguir ilustra esta estratégia:

Fig. 4. A representação das posições bidirecionais do EA Chaos 2 no gráfico de preço

Fig. 4. A representação das posições bidirecionais do EA Chaos 2 no gráfico de preço

Barras de reversão são marcadas com uma moldura vermelha. O período N neste gráfico é igual a 2. O momento mais oportuno é escolhido para esta estratégia. As posições de venda são mostrados como uma linha pontilhada azul, as posições de compra são representadas pelo verde. Como pode ser visto, as posições de compra e venda, podem existir ao mesmo tempo, mesmo em uma estratégia relativamente simples. Preste atenção ao período de 5-8 de janeiro de 2014.

Este é um ponto de reversão para a tendência de baixa do AUDCAD. No dia 4 de janeiro, um sinal foi recebido na barra de reversão de alta e no dia 05 de janeiro duas posições de compra foram abertas. Ao mesmo tempo, ainda havia três posições de venda cujos stops foram arrastados seguindo a tendência (linha vermelha tracejada). Em seguida, no dia 7 de janeiro, o stop foi acionado para as posições de venda, sobrando apenas as posições compradas no mercado.

Alterações seriam difíceis de controlar em uma posição líquida, uma vez que o volume líquido não levaria em conta o número de posições reais mantidos pelo EA. HedgeTerminal permite que os EAs monitorem suas posições individuais, independentemente da posição líquida atual, tornando possível obter estes gráficos e desenvolver estratégias semelhantes.

Abaixo está o código de implementação desta estratégia.

Eu intencionalmente não usei a programação orientada a objeto, adaptando o código para iniciantes:

//+------------------------------------------------------------------+
//|                                                       Chaos2.mq5 |
//|     Copyright 2014, Vasiliy Sokolov specially for HedgeTerminal. |
//|                                          St.-Petersburg, Russia. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Vasiliy Sokolov."
#property link      "https://login.mql5.com/ru/users/c-4"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Prototypes.mqh>           // Include prototypes function of HedgeTerminalAPI library

//+------------------------------------------------------------------+
//| Input parameters.                                                |
//+------------------------------------------------------------------+
input uint N=2;                     // Period of extermum/minimum
input uint OldPending=3;            // Old pending

//+------------------------------------------------------------------+
//| Private variables of expert advisor.                             |
//+------------------------------------------------------------------+
ulong Magic = 2314;                 // Magic number of expert
datetime lastTime = 0;              // Remembered last time for function DetectNewBar
int hFractals = INVALID_HANDLE;     // Handle of indicator 'Fractals'. See: 'https://www.mql5.com/en/docs/indicators/ifractals'
//+------------------------------------------------------------------+
//| Type of bar by Bill Wiallams strategy.                           |
//+------------------------------------------------------------------+
enum ENUM_BAR_TYPE
  {
   BAR_TYPE_ORDINARY,               // Ordinary bar. 
   BAR_TYPE_BEARISH,                // This bar close in the upper third and it's minimum is lowest at N period
   BAR_TYPE_BULLISH,                // This bar close in the lower third and it's maximum is highest at N period
  };
//+------------------------------------------------------------------+
//| Type of Extremum.                                                |
//+------------------------------------------------------------------+
enum ENUM_TYPE_EXTREMUM
  {
   TYPE_EXTREMUM_HIGHEST,           // Extremum from highest prices
   TYPE_EXTREMUM_LOWEST             // Extremum from lowest prices
  };
//+------------------------------------------------------------------+
//| Type of position.                                                |
//+------------------------------------------------------------------+
enum ENUM_ENTRY_TYPE
  {
   ENTRY_BUY1,                      // Buy position with short target
   ENTRY_BUY2,                      // Buy position with long target
   ENTRY_SELL1,                     // Sell position with short target
   ENTRY_SELL2,                     // Sell position with long target
   ENTRY_BAD_COMMENT                // My position, but wrong comment
  };
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create indicator 'Fractals' ---//
   hFractals=iFractals(Symbol(),NULL);
   if(hFractals==INVALID_HANDLE)
      printf("Warning! Indicator 'Fractals' not does not create. Reason: "+
             (string)GetLastError());
//--- Corection magic by timeframe ---//
   int minPeriod=PeriodSeconds()/60;
   string strMagic=(string)Magic+(string)minPeriod;
   Magic=StringToInteger(strMagic);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Delete indicator 'Fractals' ---//
   if(hFractals!=INVALID_HANDLE)
      IndicatorRelease(hFractals);
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Run logic only open new bar. ---//
   int totals=SupportPositions();
   if(NewBarDetect()==true)
     {
      MqlRates rates[];
      CopyRates(Symbol(),NULL,1,1,rates);
      MqlRates prevBar=rates[0];
      //--- Set new pendings order ---//
      double closeRate=GetCloseRate(prevBar);
      if(closeRate<=30 && BarIsExtremum(1,N,TYPE_EXTREMUM_HIGHEST))
        {
         DeleteOldPendingOrders(0);
         SetNewPendingOrder(1,BAR_TYPE_BEARISH);
        }
      else if(closeRate>=70 && BarIsExtremum(1,N,TYPE_EXTREMUM_LOWEST))
        {
         DeleteOldPendingOrders(0);
         SetNewPendingOrder(1,BAR_TYPE_BULLISH);
        }
      DeleteOldPendingOrders(OldPending);
     }
//---
  }
//+------------------------------------------------------------------+
//| Analyze open positions and modify it if needed.                  |
//+------------------------------------------------------------------+
int SupportPositions()
  {
//---
   int count=0;
   //--- Analize active positions... ---//
   for(int i=0; i<TransactionsTotal(); i++) // Get total positions.
     {
      //--- Select main active positions ---//
      if(!TransactionSelect(i, SELECT_BY_POS, MODE_TRADES))continue;             // Select active transactions
      if(TransactionType() != TRANS_HEDGE_POSITION)continue;                     // Select hedge positions only
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)                 // Select main positions by magic
      if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN) // If position is frozen - continue
         continue;                                                               // Let's try to get access to positions later
      count++;
      //--- What position do we choose?... ---//
      ENUM_ENTRY_TYPE type=IdentifySelectPosition();
      bool modify=false;
      double sl = 0.0;
      double tp = 0.0;
      switch(type)
        {
         case ENTRY_BUY1:
         case ENTRY_SELL1:
           {
            //--- Check sl, tp levels and modify it if need. ---//
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            sl=GetStopLossLevel();
            if(!DoubleEquals(sl,currentStop))
               modify=true;
            tp=GetTakeProfitLevel();
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            //--- Close by take-profit if price more tp level
            bool isBuyTp=tp<bid && !DoubleEquals(tp,0.0) && type==ENTRY_BUY1;
            bool isSellTp=tp>ask && type==ENTRY_SELL1;
            if(isBuyTp || isSellTp)
              {
               HedgeTradeRequest request;
               request.action=REQUEST_CLOSE_POSITION;
               request.exit_comment="Close by TP from expert";
               request.close_type=CLOSE_AS_TAKE_PROFIT;
               if(!SendTradeRequest(request))
                 {
                  ENUM_HEDGE_ERR error=GetHedgeError();
                  string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
                  printf("Close position by tp failed. Reason: "+EnumToString(error)+" "+logs);
                  if(error==HEDGE_ERR_TASK_FAILED)
                     PrintTaskLog();
                  ResetHedgeError();
                 }
               else break;
              }
            double currentTakeProfit=HedgePositionGetDouble(HEDGE_POSITION_TP);
            if(!DoubleEquals(tp,currentTakeProfit))
               modify=true;
            break;
           }
         case ENTRY_BUY2:
           {
            //--- Check sl level and set modify flag. ---//
            sl=GetStopLossLevel();
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            if(sl>currentStop)
               modify=true;
            break;
           }
         case ENTRY_SELL2:
           {
            //--- Check sl level and set modify flag. ---//
            sl=GetStopLossLevel();
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            bool usingSL=HedgePositionGetInteger(HEDGE_POSITION_USING_SL);
            if(sl<currentStop || !usingSL)
               modify=true;
            break;
           }
        }
      //--- if  need modify sl, tp levels - modify it. ---//
      if(modify)
        {
         HedgeTradeRequest request;
         request.action=REQUEST_MODIFY_SLTP;
         request.sl = sl;
         request.tp = tp;
         if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
            request.exit_comment="Exit by T/P level";
         else
            request.exit_comment="Exit by trailing S/L";
         if(!SendTradeRequest(request))
           {
            ENUM_HEDGE_ERR error=GetHedgeError();
            string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
            printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs);
            if(error==HEDGE_ERR_TASK_FAILED)
               PrintTaskLog();
            ResetHedgeError();
           }
         else break;
        }
     }
   return count;
//---
  }
//+------------------------------------------------------------------+
//| Return stop-loss level for selected position.                    |
//| RESULT                                                           |
//|   Stop-loss level                                                |
//+------------------------------------------------------------------+
double GetStopLossLevel()
  {
//---
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   double fractals[];
   double sl=0.0;
   MqlRates ReversalBar;

   if(!LoadReversalBar(ReversalBar))
     {
      printf("Reversal bar load failed.");
      return sl;
     }
   //--- What position do we choose?... ---//
   switch(IdentifySelectPosition())
     {
      case ENTRY_SELL2:
        {
         if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
           {
            sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits());
            CopyBuffer(hFractals,UPPER_LINE,ReversalBar.time,TimeCurrent(),fractals);
            for(int i=ArraySize(fractals)-4; i>=0; i--)
              {
               if(DoubleEquals(fractals[i],DBL_MAX))continue;
               if(DoubleEquals(fractals[i],sl))continue;
               if(fractals[i]<sl)
                 {
                  double price= SymbolInfoDouble(Symbol(),SYMBOL_ASK);
                  int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL);
                  double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze;
                  if(fractals[i]>price+freeze)
                     sl=NormalizeDouble(fractals[i]+point,Digits());
                 }
              }
            break;
           }
        }
      case ENTRY_SELL1:
         sl=ReversalBar.high+point;
         break;
      case ENTRY_BUY2:
         if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
           {
            sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits());
            CopyBuffer(hFractals,LOWER_LINE,ReversalBar.time,TimeCurrent(),fractals);
            for(int i=ArraySize(fractals)-4; i>=0; i--)
              {
               if(DoubleEquals(fractals[i],DBL_MAX))continue;
               if(DoubleEquals(fractals[i],sl))continue;
               if(fractals[i]>sl)
                 {
                  double price= SymbolInfoDouble(Symbol(),SYMBOL_BID);
                  int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL);
                  double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze;
                  if(fractals[i]<price-freeze)
                     sl=NormalizeDouble(fractals[i]-point,Digits());
                 }
              }
            break;
           }
      case ENTRY_BUY1:
         sl=ReversalBar.low-point;
     }
   sl=NormalizeDouble(sl,Digits());
   return sl;
//---
  }
//+------------------------------------------------------------------+
//| Return Take-Profit level for selected position.                  |
//| RESULT                                                           |
//|   Take-profit level                                              |
//+------------------------------------------------------------------+
double GetTakeProfitLevel()
  {
//---
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   ENUM_ENTRY_TYPE type=IdentifySelectPosition();
   double tp=0.0;
   if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
     {
      if(!HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
         return tp;
      double sl=HedgePositionGetDouble(HEDGE_POSITION_SL);
      double openPrice=HedgePositionGetDouble(HEDGE_POSITION_PRICE_OPEN);
      double deltaStopLoss=MathAbs(NormalizeDouble(openPrice-sl,Digits()));
      if(type==ENTRY_BUY1)
         tp=openPrice+deltaStopLoss;
      if(type==ENTRY_SELL1)
         tp=openPrice-deltaStopLoss;
      return tp;
     }
   else
      return 0.0;
//---
  }
//+------------------------------------------------------------------+
//| Identify what position type is select.                           |
//| RESULT                                                           |
//|   Return type position. See ENUM_ENTRY_TYPE                      |
//+------------------------------------------------------------------+
ENUM_ENTRY_TYPE IdentifySelectPosition()
  {
//---   
   string comment=HedgePositionGetString(HEDGE_POSITION_ENTRY_COMMENT);
   int pos=StringLen(comment)-2;
   string subStr=StringSubstr(comment,pos);
   ENUM_TRANS_DIRECTION posDir=(ENUM_TRANS_DIRECTION)HedgePositionGetInteger(HEDGE_POSITION_DIRECTION);
   if(subStr=="#0")
     {
      if(posDir==TRANS_LONG)
         return ENTRY_BUY1;
      if(posDir==TRANS_SHORT)
         return ENTRY_SELL1;
     }
   else if(subStr=="#1")
     {
      if(posDir==TRANS_LONG)
         return ENTRY_BUY2;
      if(posDir==TRANS_SHORT)
         return ENTRY_SELL2;
     }
   return ENTRY_BAD_COMMENT;
//---
  }
//+------------------------------------------------------------------+
//| Set pending orders under or over bar by index_bar.               |
//| INPUT PARAMETERS                                                 |
//|   index_bar - index of bar.                                      |
//|   barType - type of bar. See enum ENUM_BAR_TYPE.                 |
//| RESULT                                                           |
//|   True if new order successfully set, othewise false.            | 
//+------------------------------------------------------------------+
bool SetNewPendingOrder(int index_bar,ENUM_BAR_TYPE barType)
  {
//---
   MqlRates rates[1];
   CopyRates(Symbol(),NULL,index_bar,1,rates);
   MqlTradeRequest request={0};
   request.volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   double vol=request.volume;
   request.symbol = Symbol();
   request.action = TRADE_ACTION_PENDING;
   request.type_filling=ORDER_FILLING_FOK;
   request.type_time=ORDER_TIME_GTC;
   request.magic=Magic;
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   string comment="";
   if(barType==BAR_TYPE_BEARISH)
     {
      request.price=rates[0].low-point;
      comment="Entry sell by bearish bar";
      request.type=ORDER_TYPE_SELL_STOP;
     }
   else if(barType==BAR_TYPE_BULLISH)
     {
      request.price=rates[0].high+point;
      comment="Entry buy by bullish bar";
      request.type=ORDER_TYPE_BUY_STOP;
     }
   MqlTradeResult result={0};
//--- Send pending order twice...
   for(int i=0; i<2; i++)
     {
      request.comment=comment+" #"+(string)i;       // Detect order by comment;
      if(!OrderSend(request,result))
        {
         printf("Trade error #"+(string)result.retcode+" "+
                result.comment);
         return false;
        }
     }
   return true;
//---
  }
//+------------------------------------------------------------------+
//| Delete old pending orders. If pending order set older that       |
//| n_bars ago pending orders will be removed.                       |
//| INPUT PARAMETERS                                                 |
//|   period - count bar.                                            |
//+------------------------------------------------------------------+
void DeleteOldPendingOrders(int n_bars)
  {
//---
   for(int i=0; i<OrdersTotal(); i++)
     {
      ulong ticket = OrderGetTicket(i);            // Get ticket of order by index.
      if(!OrderSelect(ticket))                     // Continue if not selected.
         continue;
      if(Magic!=OrderGetInteger(ORDER_MAGIC))      // Continue if magic is not main.
         continue;
      if(OrderGetString(ORDER_SYMBOL)!=Symbol())   // Continue if symbol is not main.
         continue;
      //--- Count time elipsed ---//
      datetime timeSetup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);
      int secElapsed=(int)(TimeCurrent()-timeSetup);
      //--- delete old pending order ---//
      if(secElapsed>=PeriodSeconds() *n_bars)
        {
         MqlTradeRequest request={0};
         MqlTradeResult result={0};
         request.action= TRADE_ACTION_REMOVE;
         request.order = ticket;
         if(!OrderSend(request,result))
            printf("Delete pending order failed. Reason #"+(string)result.retcode+" "+result.comment);
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| Detect new bar.                                                  |
//+------------------------------------------------------------------+
bool NewBarDetect(void)
  {
//---
   datetime timeArray[1];
   CopyTime(Symbol(),NULL,0,1,timeArray);
   if(lastTime!=timeArray[0])
     {
      lastTime=timeArray[0];
      return true;
     }
   return false;
//---
  }
//+------------------------------------------------------------------+
//| Get close rate. Type bar defined in trade chaos strategy         |
//| and equal enum 'ENUM_TYPE_BAR'.                                  |
//| INPUT PARAMETERS                                                 |
//|   index - index of bars series. for example:                     |
//|   '0' - is current bar. 1 - previous bar.                        |
//| RESULT                                                           |
//|   Type of ENUM_TYPE_BAR.                                         | 
//+------------------------------------------------------------------+
double GetCloseRate(const MqlRates &bar)
  {
//---
   double highLowDelta = bar.high-bar.low;      // Calculate diaposon bar.
   double lowCloseDelta = bar.close - bar.low;  // Calculate Close - Low delta.
   double percentClose=0.0;
   if(!DoubleEquals(lowCloseDelta, 0.0))                    // Division by zero protected.   
      percentClose = lowCloseDelta/highLowDelta*100.0;      // Calculate percent 'lowCloseDelta' of 'highLowDelta'.
   return percentClose;
//---
  }
//+------------------------------------------------------------------+
//| If bar by index is extremum - return true, otherwise             |
//| return false.                                                    |
//| INPUT PARAMETERS                                                 |
//|   index - index of bar.                                          |
//|   period - Number of bars prior to the extremum.                 |
//|   type - Type of extremum. See ENUM_TYPE_EXTREMUM TYPE enum.     |
//| RESULT                                                           |
//|   True - if bar is extremum, otherwise false.                    | 
//+------------------------------------------------------------------+
bool BarIsExtremum(const int index,const int period,ENUM_TYPE_EXTREMUM type)
  {
//--- Copy rates --- //
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   CopyRates(Symbol(),NULL,index,N+1,rates);
//--- Search extremum --- //
   for(int i=1; i<ArraySize(rates); i++)
     {
      //--- Reset comment if you want include volume analize. ---//
      //if(rates[0].tick_volume<rates[i].tick_volume)
      //   return false;
      if(type==TYPE_EXTREMUM_HIGHEST && 
         rates[0].high<rates[i].high)
         return false;
      if(type==TYPE_EXTREMUM_LOWEST && 
         rates[0].low>rates[i].low)
         return false;
     }
   return true;
//---
  }
//+------------------------------------------------------------------+
//| Print current error and reset it.                                |
//+------------------------------------------------------------------+  
void PrintTaskLog()
  {
//---
   uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL);
   for(uint i = 0; i<totals; i++)
     {
      uint retcode=0;
      ENUM_TARGET_TYPE type;
      GetActionResult(i,type,retcode);
      printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode);
     }
//---
  }
//+------------------------------------------------------------------+
//| Load reversal bar. The current position must be selected.        |
//| OUTPUT PARAMETERS                                                |
//|   bar - MqlRates bar.
//+------------------------------------------------------------------+  
bool LoadReversalBar(MqlRates &bar)
  {
//---
   datetime time=(datetime)(HedgePositionGetInteger(HEDGE_POSITION_ENTRY_TIME_SETUP_MSC)/1000+1);
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   CopyRates(Symbol(),NULL,time,2,rates);
   int size=ArraySize(rates);
   if(size==0)return false;
   bar=rates[size-1];
   return true;
//---   
  }
//+------------------------------------------------------------------+
//| Compares two double numbers.                                     |
//| RESULT                                                           |
//|   True if two double numbers equal, otherwise false.             |
//+------------------------------------------------------------------+
bool DoubleEquals(const double a,const double b)
  {
//---
   return(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b)));
//---
  }

Abaixo está uma breve descrição de como esse código funciona. A EA é chamado a cada tick. Ele analisa a barra anterior, utilizando a função BarIsExtremum(): se é de baixa ou alta, ele coloca duas ordens pendentes (função SetNewPendingOrder()). Uma vez ativada, as ordens pendentes são convertidas em posições. O EA define o stop loss e take profit para as posições em seguida.

Infelizmente, estes níveis não podem ser colocados junto com as ordens pendentes, porque não há nenhuma posição real ainda. Os níveis são estabelecidos através da função SupportPositions(). Para funcionar corretamente, nós precisamos saber a posição para qual o take profit deve ser colocado, e a posição que deve ser arrastada seguindo os fractais. Esta definição de posições é feita pela função IdentifySelectPosition(). Ele analisa o comentário da posição de início, e se ela contém a string "#1", um alvo cruto é definido por ele; se ele contiver "#2", é aplicado um stop móvel.

Para modificar uma posição bidirecional em aberto, ou para fechá-la, uma solicitação de negociação especial é criada, na qual ela é enviada para a função SendTradeRequest() para execução:

...
if(modify)
  {
   HedgeTradeRequest request;
   request.action=REQUEST_MODIFY_SLTP;
   request.sl = sl;
   request.tp = tp;
   if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
      request.exit_comment="Exit by T/P level";
   else
      request.exit_comment="Exit by trailing S/L";
   if(!SendTradeRequest(request))
     {
      ENUM_HEDGE_ERR error=GetHedgeError();
      string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
      printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs);
      if(error==HEDGE_ERR_TASK_FAILED)
         PrintTaskLog();
      ResetHedgeError();
     }
   else break;
  }
...

Preste atenção ao erro de manipulação.

Se o envio falhar e a função retornar false, nós precisamos obter o último código de erro usando a função GetHedgeError(). Em alguns casos, a execução de uma ordem de negociação não vai começar mesmo. Se a posição não tiver sido pré-selecionada, então, a consulta é feita de forma incorreta, e sua execução é impossível.

Se uma ordem não for executada, é inútil analisar o log de sua implementação, recebendo um código de erro já é suficiente.

No entanto, se a consulta está correta, mas a ordem não foi executada por alguma razão, o erro HEDGE_ERR_TASK_FAILED será devolvido. Neste caso, é necessário analisar o log de execução da ordem, pesquisando através do log. Isto é feito através da função especial PrintTaskLog():

//+------------------------------------------------------------------+
//| Print current error and reset it.                                |
//+------------------------------------------------------------------+  
void PrintTaskLog()
  {
//---
   uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL);
   for(uint i = 0; i<totals; i++)
     {
      uint retcode=0;
      ENUM_TARGET_TYPE type;
      GetActionResult(i,type,retcode);
      printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode);
     }
//---
  }

Estas mensagens permitem identificar o motivo da falha e corrigi-lo.

Vamos agora ilustrar a exibição do EA Chaos2 e as suas posições no HedgeTerminal em tempo real. O EA está em execução no gráfico M1:

Fig. 5. A representação das posições bidirecionais do EA Chaos no painel HedgeTerminal

Fig. 5. A representação das posições bidirecionais do EA Chaos no painel HedgeTerminal

Como pode ser visto, até as posições bidirecionais de um EA podem perfeitamente coexistir.


1.13. "Símbolos Duplicados" e a Virtualização pelo Broker

Quando o MetaTrader 5 foi lançado, alguns corretores começaram a fornecer os chamados símbolos duplicados. Suas cotações são iguais aos instrumentos originais, mas que têm um sufixo como uma regra, como "_m" ou "_1". Eles foram introduzidos para permitir que os traders possuíssem posições bidirecionais virtualmente no mesmo símbolo.

No entanto, esses símbolos são quase que inúteis para os traders algorítmicos que usam robôs. E aqui está o porquê. Suponha que que seria necessário nós escrevermos o EA "Chaos II" sem a biblioteca HedgeTerminalAPI. Em vez disso, nós teríamos símbolos duplicados. Como nós faríamos isso? Suponha que todas as operações de venda foram abertas em um único instrumento, como o EURUSD, e todas as operações de compra no outro, por exemplo o EURUSD_m1.

Mas o que aconteceria se, no momento da abertura da posição um dos símbolos já foram negociadas por um outro robô ou pelo trader? Mesmo se tais símbolos forem sempre livres, o problema não seria resolvido para este robô, que poderia ter simultaneamente múltiplas posições na mesma direção.

A imagem acima mostra três posições de venda, e podendo haver ainda mais delas. As posições têm diferentes níveis de stop de proteção, é por isso que eles não podem ser combinados em uma posição líquida única. A solução é abrir uma nova posição para um novo símbolo duplicado. Mas pode não ser suficiente tais símbolos, porque um robô precisa de seis instrumentos em duplicado (três em cada direção da negociação). Se dois robôs executam em diferentes intervalos de tempo, 12 símbolos são obrigatórios.

Nenhuma das corretoras fornecem tantos símbolos duplicados. Mas mesmo se houvesse uma quantidade ilimitada de tais símbolos, e eles estivessem sempre livres, seria necessário uma decomposição complexa do algoritmo. O robô teria que passar por todos os símbolos disponíveis à procura de duplicatas e suas próprias posições. Isso criaria mais problemas do que soluções. 

Há ainda mais dificuldades com os símbolos duplicados. Aqui está uma breve lista de problemas adicionais decorrentes de sua utilização:

Um símbolo duplicado é essencialmente uma virtualização no lado da corretora. HedgeTerminal usa a virtualização no lado do cliente.

Em ambos os casos, nós usamos a virtualização como tal. Ela muda a representação real das obrigações do trader. Com a virtualização, uma posição pode se transformar em duas posições. Não há nenhum problema quando ele ocorre no lado do cliente, porque os clientes podem representar o que quiserem. Mas se a virtualização é feita pela corretora, os organismos reguladores e de licenciamento pode ter dúvidas sobre como a informação fornecida refere-se à informação real. A segunda dificuldade é que isto requer ter duas APIs em uma: um conjunto de funções e modificadores para uso no modo de rede, e outro para o modo bidirecional.

Muitos traders algorítmicos encontraram a sua própria maneira de vincular as negociações em uma única posição. Muitos destes métodos funcionam bem, e existem artigos que descrevem estes métodos. No entanto, a virtualização de posições é um procedimento mais complicado do que parece. No HedgeTerminal, os algoritmos associados com virtualização de posições levaram cerca de 20.000 linhas de código fonte. Além disso, o HedgeTerminal implementa apenas as funções básicas. Criando uma quantidade semelhante de código em seu EA só para acompanhar as posições bidirecionais consumiria muitos recursos.


Capítulo 2. Manual do API HedgeTerminal

2.1. Funções de Seleção da Transação

Função TransactionsTotal()

A função retorna o número total de transações na lista de transações. Esta é a função básica para busca através das transações disponíveis (veja o exemplo na seção 1.4 e 1.11 deste artigo).

int TransactionsTotal(ENUM_MODE_TRADES pool = MODE_TRADES);

Parâmetros

Valor de Retorno

A função retorna o número total de transações na lista de transações.


Função TransactionType()

A função retorna o tipo de uma transação selecionada.

ENUM_TRANS_TYPE TransactionType(void);

Valor de Retorno

Tipo de retorno O valor pode ser um dos valores ENUM_TRANS_TYPE.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função TransactionSelect()

Esta função seleciona uma transação para manipulações adicionais. A função seleciona uma transação pelo seu índice ou pelo identificador único na lista de transações.

bool TransactionSelect(int index,
     ENUM_MODE_SELECT select = SELECT_BY_POS,
     ENUM_MODE_TRADES pool=MODE_TRADES
     );

Parâmetros

Valor de Retorno

Retorna true se a operação foi selecionada com sucesso ou false caso contrário. Para obter detalhes do erro, chame GetHedgeError().

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".

Nota

Se uma transação é selecionada com base no seu índice, a complexidade da operação corresponde a O(1). Se uma transação é selecionada com base no seu identificador único, a complexidade da operação tende assintoticamente para O(log2(n)).


Função HedgeOrderSelect()

A função seleciona uma das ordens incluídas na posição bidirecional. A posição bidirecional, que inclui a ordem requerida, deve ser pré-selecionada usando a TransactionSelect().

bool HedgeOrderSelect(ENUM_HEDGE_ORDER_SELECTED_TYPE type);

Parâmetros

Valor de Retorno

Retorna true se uma ordem foi selecionada com sucesso ou false caso contrário. Para obter detalhes do erro, chame GetHedgeError().

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgeDealSelect()

A função seleciona uma das negociações que executou a ordem. A ordem da parte da qual o negócio foi selecionado deve ser pré-selecionado usando a função HedgeOrderSelect().

bool HedgeDealSelect(int index);

Parâmetros

Valor de Retorno

Retorna true se uma negociação tiver sido selecionada com sucesso ou false caso contrário. Para obter detalhes do erro, chame GetHedgeError().

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


2.2. Funções para Obter as Propriedades da Transação Selecionada

Função HedgePositionGetInteger()

A função retorna a propriedade de uma posição bidirecional selecionada. A propriedade pode ser do tipo int, long, datetime ou bool dependendo do tipo da propriedade requerida. A posição bidirecional devem ser pré-selecionada utilizando a função TransactionSelect().

ulong HedgePositionGetInteger(ENUM_HEDGE_POSITION_PROP_INTEGER property);

Parâmetros

Valor de Retorno

Valor do tipo ulong. Para continuar a utilizar o valor, seu tipo deve ser convertido explicitamente para o tipo de propriedade requerida.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgePositionGetDouble()

A função retorna a propriedade de uma posição bidirecional selecionada. O tipo da propriedade de retorno é double. O tipo da propriedade é especificada através da enumeração ENUM_HEDGE_POSITION_PROP_DOUBLE. A posição bidirecional deve ser pré-selecionada utilizando a TransactionSelect().

ulong HedgePositionGetDouble(ENUM_HEDGE_POSITION_PROP_DOUBLE property);

Parâmetros

Valor de Retorno

O valor do tipo double.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgePositionGetString()

A função retorna a propriedade de uma posição bidirecional selecionada. A propriedade é do tipo string. O tipo da propriedade é especificado através da enumeração ENUM_HEDGE_POSITION_PROP_STRING. A posição bidirecional deve ser pré-selecionada utilizando a TransactionSelect().

ulong HedgePositionGetString(ENUM_HEDGE_POSITION_PROP_STRING property);

Parâmetros

Valor de Retorno

O valor do tipo string.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgeOrderGetInteger()

A função retorna a propriedade da ordem selecionada, que é parte da posição bidirecional. A propriedade pode ser do tipo int, long, datetime ou bool. O tipo da propriedade é especificado através da enumeração ENUM_HEDGE_ORDER_PROP_INTEGER. A ordem deve ser pré-selecionada usando a função HedgeOrderSelect().

ulong HedgeOrderGetInteger(ENUM_HEDGE_ORDER_PROP_INTEGER property);

Parâmetros

Valor de Retorno

Valor do tipo ulong. Para continuar a utilizar o valor, o seu tipo deve ser convertido explicitamente para o tipo de propriedade requerida.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgeOrderGetDouble()

A função retorna a propriedade da ordem selecionada, que é parte da posição bidirecional. A propriedade solicitada é do tipo double. O tipo de propriedade é especificado através da enumeração ENUM_HEDGE_ORDER_PROP_DOUBLE. A ordem deve ser pré-selecionada usando a função HedgeOrderSelect().

double HedgeOrderGetDouble(ENUM_HEDGE_ORDER_PROP_DOUBLE property);

Parâmetros

Valor de Retorno

O valor do tipo double.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgeDealGetInteger()

A função retorna a propriedade do negócio selecionado, que faz parte da ordem executada. A propriedade pode ser do tipo int, long, datetime ou bool. O tipo da propriedade é especificado através da enumeração ENUM_HEDGE_DEAL_PROP_INTEGER. A negociação deve ser pré-selecionada usando a função HedgeDealSelect().

ulong HedgeOrderGetInteger(ENUM_HEDGE_DEAL_PROP_INTEGER property);

Parâmetros

Valor de Retorno

Valor do tipo ulong. Para continuar a utilizar o valor, seu tipo deve ser explicitamente convertido para o tipo da propriedade solicitada.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função HedgeDealGetDouble()

A função retorna a propriedade do negócio selecionado, que faz parte da ordem executada. A propriedade pode ser do tipo double. O tipo da propriedade é especificado através da enumeração ENUM_HEDGE_DEAL_PROP_DOUBLE. A negociação deve ser pré-selecionada usando a função HedgeDealSelect().

ulong HedgeOrderGetDouble(ENUM_HEDGE_DEAL_PROP_DOUBLE property);

Parâmetros

Valor de Retorno

O valor do tipo double.

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


2.3. Funções para Definir e Obter as Propriedades do HedgeTerminal a partir dos Expert Advisors

Função HedgePropertySetInteger()

A função define uma das propriedades do HedgeTerminal. A propriedade pode ser do tipo int, long, datetime ou bool. O tipo da propriedade é especificado através da enumeração ENUM_HEDGE_PROP_INTEGER.

bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, long value);

Parâmetros

Valor de Retorno

O valor do tipo bool. Se a propriedade tiver sido definida com sucesso, a função retorna true, caso contrário ela retorna false.

Exemplo de Uso

No exemplo, a função é utilizada para definir o tempo da posição de bloqueio ao enviar uma solicitação assíncrona. Se a resposta do servidor não for recebida dentro de 30 segundos depois de enviar uma solicitação assíncrona, a posição bloqueada será desbloqueada.

void SetTimeOut()
  {
   bool res=HedgePropertySetInteger(HEDGE_PROP_TIMEOUT,30);
   if(res)
      printf("The property is set successfully");
   else
      printf("Property is not set");
  }

Função HedgePropertyGetInteger()

A função obtém uma das propriedades do HedgeTerminal. A propriedade pode ser do tipo int, long, datetime ou bool. O tipo da propriedade é especificado através da enumeração ENUM_HEDGE_PROP_INTEGER.

long HedgePropertyGetInteger(ENUM_HEDGE_PROP_INTEGER property);

Parâmetros

Valor de Retorno

Um valor do tipo long.

Exemplo de Uso

A função recebe a posição de tempo de bloqueio ao enviar uma solicitação assíncrona e mostra no terminal.

void GetTimeOut()
  {
   int seconds=HedgePropertyGetInteger(HEDGE_PROP_TIMEOUT);
   printf("Timeout is "+(string) seconds);
  }

2.4. Funções para Obter e Manipular os Códigos de Erro

Função GetHedgeError()

A função retorna o identificador do erro, que foi obtido da última ação. O identificador de erro corresponde a enumeração ENUM_HEDGE_ERR.

ENUM_HEDGE_ERR GetHedgeError(void);

Valor de Retorno

ID da posição. O valor pode ser qualquer um do tipo da enumeração ENUM_HEDGE_ERR.

Nota

Após a chamada, a função GetHedgeError() não repõe a ID de erro. Para repor a ID de erro usar-se a função ResetHedgeError().

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função ResetHedgeError()

A função redefine o identificador do último erro recebido. Após sua chamada, o identificador ENUM_HEDGE_ERR retornado por GetHedgeError() será igual a HEDGE_ERR_NOT_ERROR.

void ResetHedgeError(void);

Exemplo de Uso

Veja o exemplo do uso da função na seção 1.11 deste artigo: "Gerenciamento das Propriedades da Posição Bidirecional através de um script de exemplo".


Função TotalActionsTask()

Uma vez que a posição é selecionada utilizando a função HedgePositionSelect(), ela pode ser modificada utilizando a função SendTradeRequest(). Por exemplo, ela pode ser fechada, ou o seu comentário de saída pode ser alterado. Esta modificação é efetuada por uma tarefa de negociação especial. Cada tarefa pode consistir de diversas atividades de negociação (subtarefas). Uma tarefa pode falhar. Neste caso, você pode precisar analisar o resultado de todas as sub-tarefas incluídas na tarefa para ver que tipo de subtarefas que falharam.

A função TotalActionTask() retorna o número de sub-tarefas contidas na última tarefa de negociação que está sendo executada para a posição selecionada. Sabendo o número total de sub-tarefas, você pode procurar através de todas as subtarefas pelo seu índice, e analisar seus resultados de execução usando a função GetActionResult() e, assim, descobrir as circunstâncias da falha.

uint TotalActionsTask(void);

Valor de Retorno

Retorna o número total de sub-tarefas dentro da tarefa.

Exemplo de Uso

Veja o exemplo de uso na seção 1.6 deste artigo: "Análise Detalhada da Negociação e Identificação dos Erros Usando TotalActionsTask() e GetActionResult()".


Função GetActionResult()

A função usa o índice da sub-tarefa dentro da tarefa (veja TotalActionTask ()). Retorna o tipo da sub-tarefa e sua execução resulta através dos parâmetros de referência. O tipo da subtarefa é definida pela enumeração ENUM_TARGET_TYPE. O resultado da execução da sub-tarefa corresponde aos códigos de retorno do servidor de negociação do MetaTrader 5.

void GetActionResult(uint index, ENUM_TARGET_TYPE& target_type, uint& retcode);

Parâmetros

Exemplo de Uso

Veja o exemplo de uso na seção 1.6 deste artigo: "Análise Detalhada da Negociação e Identificação dos Erros Usando TotalActionsTask() e GetActionResult()".


2.5. Negociação

Função SendTradeRequest()

A função envia um pedido para alterar a posição bidirecional selecionada em HedgeTerminalAPI. O resultado da execução da função é uma das três ações:

  1. Fechar uma posição ou uma parte do seu volume;
  2. Modificação do nível de stop loss e take profit;
  3. Modificação do comentário de saída.

O tipo de ação e seus parâmetros estão especificados na estrutura HedgeTradeRequest, que é passado por referência como um parâmetro. Antes da chamada da função, a posição bidirecional deve ser pré-selecionada usando a função TransactionSelect().

bool SendTradeRequest(HedgeTradeRequest& request);

Parâmetros

[in] request – A estrutura do pedido para modificar a posição de bidirecional. Por favor, consulte a descrição da estrutura e da explicação dos seus campos na descrição da estrutura HedgeTradeRequest.

Valor de Retorno

Retorna true, se o pedido de alteração da posição foi executado com sucesso. Caso contrário retorna false. Em caso de falha de execução do pedido, use as funções TotalActionsTask() e GetActionResult() para encontrar a falha e suas razões.

Nota

No modo assíncrono de solicitação de envio, o flag de retorno contém true se uma tarefa foi colocada e iniciada com êxito. No entanto, devemos lembrar que, mesmo no caso de o início de uma tarefa ser bem sucedido, a sua execução não pode ser garantida. Por conseguinte, esta flag não pode ser usada para controlar a conclusão da tarefa no modo assíncrono. No modo síncrono, uma tarefa é iniciada e executada em um único segmento, assim, no modo síncrono, você pode controlar o resultado da execução do pedido de negociação usando esta flag.


Estrutura de solicitação de negociação HedgeTradeRequest()

Posições bidirecionais no HedgeTerminal são fechadas e modificadas através de uma chamada da função SendTradeRequest(), em que a solicitação de negociação é utilizada como um argumento. A solicitação representada pela estrutura especial predefinida HedgeTradeRequest, que contém todos os campos necessários para o fehcamento ou modificação da posição selecionada:

struct HedgeTradeRequest
  {
   ENUM_REQUEST_TYPE action;             // type of action
   double            volume;             // volume of position
   ENUM_CLOSE_TYPE   close_type;         // Marker of closing order
   double            sl;                 // stop-loss level
   double            tp;                 // take-profit level
   string            exit_comment;       // outgoing comment
   uint              retcode;            // last retcode in executed operation
   bool              asynch_mode;        // true if the closure is performed asynchronously, otherwise false
   ulong             deviation;          // deviation in step price
                     HedgeTradeRequest() // default params
     {
      action=REQUEST_CLOSE_POSITION;
      asynch_mode=false;
      volume=0.0;
      sl = 0.0;
      tp = 0.0;
      retcode=0;
      deviation=3;
     }
  };

Descrição dos campos

CampoDescrição
 ação O tipo da ação necessária com a posição. O valor pode ser qualquer um dos valores de enumeração ENUM_REQUEST_TYPE
 volume O volume para fechamento. Pode ser menor do que o volume da posição atualmente ativa. Se o volume for zero, a posição ativa será fechada completamente.
 sl O nível de stop loss para ser colocado para a posição ativa.
 tp O nível de take profit para ser colocado para a posição ativa.
 exit_comment  O comentário de saída para a posição ativa.
 retcode Código de resultado da última operação executada.
 asynch_mode True se o modo assíncrono para o envio de solicitações é usado, false caso contrário.
 deviation O desvio máximo do preço utilizado.


2.6. Enumerações para Trabalhar com as Funções de Seleção da Transação

ENUM_TRANS_TYPE

Todas as transações disponíveis para a análise, incluindo as ordens pendentes e posições bidirecionais, estão na lista de transações ativas e históricas.

A enumeração ENUM_TRANS_TYPE contém o tipo de cada transação selecionada. Essa enumeração é retornada pela função TransactionType(). Estes são os campos de enumeração e suas descrições:

CampoDescrição
 TRANS_NOT_DEFINED A transação não está selecionada pela função TransactionSelect() ou seu tipo é indefinido.
 TRANS_HEDGE_POSITION A transação é uma posição bidirecional.
 TRANS_BROKERAGE_DEAL  A transação é um negócio da corretora (operação da conta). Por exemplo, adicionar dinheiro para a conta ou correção.
 TRANS_PENDING_ORDER A transação é uma ordem pendente.
 TRANS_SWAP_POS A transação é um swap cobrado por uma posição líquida.


ENUM_MODE_SELECT

A enumeração define o tipo de índice definido no parâmetro da função TransactionSelect().

CampoDescrição
 SELECT_BY_POS O parâmetro índice é usado para passar o índice da transação na lista.
 SELECT_BY_TICKET O número do ticket é passado no parâmetro index.


ENUM_MODE_TRADES

A enumeração define a fonte de dados, a partir do qual uma operação é selecionada usando a TransactionSelect().

CampoDescrição
 MODE_TRADES A transação é selecionada das transações ativas.
 MODE_HISTORY A transação é selecionado das transações históricas.

2.7. Enumerações para Trabalhar com as Funções que Obtêm as Propriedades da Transação

Enumeração ENUM_TRANS_DIRECTION

Toda a transação, não importa se é um negócio ou uma posição bidirecional, tem uma direção do mercado.

Esta direção do mercado é definido pela enumeração ENUM_TRANS_DIRECTION. Abaixo estão os seus campos e suas descrições:

CampoDescrição
 TRANS_NDEF A direção de uma transação é indefinido. Por exemplo, as transações do corretor sobre a conta não tem direção do mercado e vêm com este modificador.
 TRANS_LONG Indica que a transação (ordem ou posição a bidirecional) é uma transação de Compra.
 TRANS_SHORT  Indica que a transação (ordem ou posição bidirecional) é uma transação de Venda.


Enumeração ENUM_HEDGE_POSITION_STATUS

A enumeração contém o estado de uma posição bidirecional.

CampoDescrição
 HEDGE_POSITION_ACTIVE  Uma posição ativa. Posições ativas aparecem na guia ativa do painel do HedgeTerminal.
 HEDGE_POSITION_HISTORY  A posição histórica. Posições históricas aparecem no separador Histórico do painel do HedgeTerminal.


Enumeração ENUM_HEDGE_POSITION_STATE

A enumeração contém o estado de uma posição bidirecional.

CampoDescrição
 POSITION_STATE_ACTIVE A posição selecionada está ativa e pode ser modificada usando o HedgeTradeRequest.
 POSITION_STATE_FROZEN  A posição selecionada está bloqueada e não pode ser modificada. Se este modificador é recebido, deve-se esperar até que a posição é desbloqueado.


Enumeração ENUM_HEDGE_POSITION_PROP_INTEGER

A enumeração define o tipo da propriedade retornada por HedgePositionGetInteger().

CampoDescrição
 HEDGE_POSITION_ENTRY_TIME_SETUP_MSC O tempo em milisegundos desde 01.01.1970, quando a ordem de iniciar a posição bidirecional foi colocado.
 HEDGE_POSITION_ENTRY_TIME_EXECUTED_MSC  O tempo em milissegundos desde 01.01.1970, quando a ordem de iniciar a posição bidirecional foi executada (tempo de abertura da posição).
 HEDGE_POSITION_EXIT_TIME_SETUP_MSC O tempo em milisegundos desde 01.01.1970, quando a ordem de fechamento da posição bidirecional foi colocada.
 HEDGE_POSITION_EXIT_TIME_EXECUTED_MSC O tempo em milisegundos desde 01.01.1970, quando a ordem de fechamento da posição bidirecional foi executada (hora de fechamento da posição).
 HEDGE_POSITION_TYPE O tipo da posição bidirecional. Igual ao tipo do iniciador da ordem. Contém um dos valores do sistema de enumeração ENUM_ORDER_TYPE.
 HEDGE_POSITION_DIRECTION Direção da posição. Definido pela enumeração ENUM_TRANS_DIRECTION.
 HEDGE_POSITION_MAGIC O número mágico do Expert Advisor em que a posição selecionada pertence. Um valor igual a zero indica que a posição foi aberta manualmente.
 HEDGE_POSITION_CLOSE_TYPE O marcador da ordem de fechamento da posição. Definido por ENUM_CLOSE_TYPE.
 HEDGE_POSITION_ID ID da posição. Igual ao identificador da ordem de iniciação.
 HEDGE_POSITION_ENTRY_ORDER_ID O identificador da ordem de iniciação.
 HEDGE_POSITION_EXIT_ORDER_ID O identificador de uma ordem de fechamento para uma posição histórica.
 HEDGE_POSITION_STATUS Status da posição. Definido pela ENUM_HEDGE_POSITION_STATUS.
 HEDGE_POSITION_STATE Estado da posição. Definido pela ENUM_HEDGE_POSITION_STATE
 HEDGE_POSITION_USING_SL A flag da utilização do stop loss. Se for utilizado um stop loss, então a função HedgePositionGetInteger() retorna true, caso contrário false.
 HEDGE_POSITION_USING_TP A flag de utilização do nível de take profit. Se o take profit é usado, HedgePositionGetInteger() retorna true, caso contrário ele retorna false.
 HEDGE_POSITION_TASK_STATUS O status da tarefa que está sendo executada para a posição selecionada. A posição pode estar sob modificação. Este modificador é usado para rastrear mudanças nesta posição. O status da posição é definido por ENUM_TASK_STATUS.
 HEDGE_POSITION_ACTIONS_TOTAL Retorna o número total de sub-tarefas começando a mudar esta posição.

 

Enumeração ENUM_HEDGE_POSITION_PROP_DOUBLE

A enumeração define o tipo de propriedade retornado pela função HedgePositionGetDouble().

CampoDescrição
 HEDGE_POSITION_VOLUME O volume da posição bidirecional.
 HEDGE_POSITION_PRICE_OPEN O preço médio ponderado de abertura de uma posição.
 HEDGE_POSITION_PRICE_CLOSED O preço médio ponderado do fechamento de uma posição.
 HEDGE_POSITION_PRICE_CURRENT O preço atual de uma posição ativa. Para uma posição histórica, este modificador retorna o preço de fechamento da posição.
 HEDGE_POSITION_SL O nível de stop loss. Zero se o stop loss não é usado.
 HEDGE_POSITION_TP O nível de take profit Zero se o take profit não for usado.
 HEDGE_POSITION_COMMISSION O montante da comissão paga para a posição.
 HEDGE_POSITION_SLIPPAGE Desvio em pontos.
 HEDGE_POSITION_PROFIT_CURRENCY  Lucro ou perda da posição. O valor é especificado na moeda do depósito.
 HEDGE_POSITION_PROFIT_POINTS Lucro ou perda da posição. O valor é especificado nos pontos do símbolo da posição financeira.

Nota

O desvio HEDGE_POSITION_SLIPPAGE é calculado como a diferença de pontos entre a melhor entrada da posição do negócio e o preço médio ponderado de entrada.

 

Enumeração ENUM_HEDGE_POSITION_PROP_STRING

A enumeração define o tipo de propriedade retornado pela função HedgePositionGetString().

CampoDescrição
 HEDGE_POSITION_SYMBOL O símbolo da posição atual.
 HEDGE_POSITION_ENTRY_COMMENT O comentário de entrada de uma posição.
 HEDGE_POSITION_EXIT_COMMENT O comentário de saída de uma posição.

 

Enumeração ENUM_HEDGE_ORDER_STATUS

A enumeração contém o tipo de ordem.

CampoDescrição
 HEDGE_ORDER_PENDING  A ordem está pendente e está disponível na aba Negociação do MetaTrader 5.
 HEDGE_ORDER_HISTORY A ordem é histórica e está disponível na histórico de ordens do MetaTrader 5.

Enumeração ENUM_HEDGE_ORDER_SELECTED_TYPE

A enumeração define o tipo de ordem escolhida pela função HedgeOrderSelect().

CampoValor
 ORDER_SELECTED_INIT A ordem inicia uma posição bidirecional.
 ORDER_SELECTED_CLOSED  A ordem fecha uma posição bidirecional.
 ORDER_SELECTED_SL A ordem age como um nível de stop loss.


Enumeração ENUM_HEDGE_ORDER_PROP_INTEGER

A enumeração define o tipo de propriedade retornada pela função HedgePositionGetInteger().

CampoDescrição
 HEDGE_ORDER_ID Um identificador de ordem único
 HEDGE_ORDER_STATUS Status da ordem. O valor pode ser um dos valores de enumeração ENUM_HEDGE_ORDER_STATUS.
 HEDGE_ORDER_DEALS_TOTAL O número total de negócios que preencheram a ordem. O valor é igual a zero para as ordens pendentes.
 HEDGE_ORDER_TIME_SETUP_MSC Tempo de colocação da ordem pendente em milissegundos desde 01.01.1970.
 HEDGE_ORDER_TIME_EXECUTED_MSC O tempo de execução de uma ordem executada em milissegundos desde 01.01.1970.
 HEDGE_ORDER_TIME_CANCELED_MSC O tempo de cancelamento de uma ordem executada em milissegundos desde 01.01.1970.

Nota

O tempo de execução de ordens HEDGE_ORDER_TIME_EXECUTED_MSC é igual ao tempo de seu mais recente negócio. 


Enumeração ENUM_HEDGE_ORDER_PROP_DOUBLE

A enumeração define o tipo de propriedade retornado pela função HedgeOrderGetDouble().

CampoDescrição
 HEDGE_ORDER_VOLUME_SETUP O volume da ordem especificada na ordem.
 HEDGE_ORDER_VOLUME_EXECUTED O volume executado da ordem. Se uma ordem estiver pendente, o volume executado é zero.
 HEDGE_ORDER_VOLUME_REJECTED O volume da ordem que não pode ser executado. Igual à diferença entre o volume inicial e o volume executado.
 HEDGE_ORDER_PRICE_SETUP Colocação do preço da ordem.
 HEDGE_ORDER_PRICE_EXECUTED O preço médio ponderado de execução de uma ordem.
 HEDGE_ORDER_COMMISSION O valor da comissão paga a corretora para execução da ordem. Especificada na moeda depósito.
 HEDGE_ORDER_SLIPPAGE Desvio da ordem.

Nota

O desvio HEDGE_ORDER_SLIPPAGE é calculado como a diferença de pontos entre o melhor negócio executado e do preço de entrada médio ponderado da ordem.


Enumeração ENUM_HEDGE_DEAL_PROP_INTEGER

A enumeração define o tipo da propriedade retornada por HedgeDealGetInteger().

CampoDescrição
 HEDGE_DEAL_ID Um identificador de negócio único.
 HEDGE_DEAL_TIME_EXECUTED_MSC Tempo de execução do negócio em milissegundos desde 01.01.1970


Enumeration ENUM_HEDGE_DEAL_PROP_DOUBLE

A enumeração define o tipo da propriedade retornada por HedgeDealGetDouble().

CampoDescrição
 HEDGE_DEAL_VOLUME_EXECUTED Volume de um negócio.
 HEDGE_DEAL_PRICE_EXECUTED Preço de execução da negociação.
 HEDGE_DEAL_COMMISSION O valor da comissão paga a corretora para a execução do negócio. Especificada na moeda depósito.

 

2.8. Enumerações para Definir e Obter as Propriedads do HedgeTerminal

Enumeração ENUM_HEDGE_PROP_INTEGER

A enumeração define o tipo da propriedade que você deseja obter ou definir em HedgeTerminal.

CampoDescrição
 HEDGE_PROP_TIMEOUT Tempo, em segundos, durante o qual o HedgeTerminal irá esperar por uma resposta do servidor antes de desbloquear uma posição que está sendo modificada.


2.9. Enumerações para Trabalhar com as Funções de Manipulação dos Códigos de Erro

Enumeração ENUM_TASK_STATUS

Cada posição bidirecional pode estar sob modificação. Uma posição é modificada através de uma tarefa de negociação.

Cada tarefa de negociação em execução tem seu status de execução definido no ENUM_TASK_STATUS. Abaixo estão os seus campos e suas descrições:

CampoDescrição
 TASK_STATUS_WAITING Nenhuma tarefa atual, ou a tarefa está esperando.
 TASK_STATUS_EXECUTING A tarefa de negociação está atualmente a ser executada.
 TASK_STATUS_COMPLETE A tarefa de negociação para a posição foi concluída com êxito.
 TASK_STATUS_FAILED A tarefa de negociação para a posição falhou.


Enumeração ENUM_HEDGE_ERR

A enumeração contém a identificação do erro que pode ser retornado por GetHedgeError().

CampoDescrição
 HEDGE_ERR_NOT_ERROR Nenhum erro.
 HEDGE_ERR_TASK_FAILED A tarefa para a posição selecionada falhou.
 HEDGE_ERR_TRANS_NOTFIND Transação não encontrada.
 HEDGE_ERR_WRONG_INDEX Índice incorreto.
 HEDGE_ERR_WRONG_VOLUME Volume incorreto.
 HEDGE_ERR_TRANS_NOTSELECTED  Transação não foi pré-selecionada usando a TransactionSelect().
 HEDGE_ERR_WRONG_PARAMETER Um dos parâmetros passados ​​está incorreto.
 HEDGE_ERR_POS_FROZEN A posição bidirecional está atualmente sob a modificação e não está disponível para novas mudanças. Espere até que a posição seja liberada.
 HEDGE_ERR_POS_NO_CHANGES A solicitação de negociação não teve alterações.

 

Enumeration ENUM_TARGET_TYPE

A enumeração define o tipo da tarefa selecionada pela função GetActionResult().

CampoDescrição
 TARGET_NDEF A sub-tarefa é indefinida.
 TARGET_CREATE_TASK A sub-tarefa está sendo criada agora. Este tipo é utilizado nas lógicas internas de HedgeTerminalAPI.
 TARGET_DELETE_PENDING_ORDER A exclusão de uma ordem pendente.
 TARGET_SET_PENDING_ORDER Colocação de uma ordem pendente.
 TARGET_MODIFY_PENDING_ORDER  Modificação do preço da ordem pendente.
 TARGET_TRADE_BY_MARKET Fazendo as operações de negociação.


2.10. Enumerações para Trabalhar com as Funções de Manipulação dos Códigos de Erro

Enumeração ENUM_REQUEST_TYPE

A enumeração descreve a ação de HedgeTerminal aplicado à posição bidirecional.

CampoDescrição
 REQUEST_CLOSE_POSITION Fecha a posição. Se o campo de volume da estrutura HedgeTradeRequest contém um volume abaixo da atual, apenas uma parte da posição será fechada. Neste caso, a parte da posição de fechamento corresponde ao valor do campo volume.
 REQUEST_MODIFY_SLTP Define ou modifica os atuais níveis de stop loss e take profit.
 REQUEST_MODIFY_COMMENT Modifica o comentário de saída de uma posição ativa.


Enumeração ENUM_CLOSE_TYPE

A enumeração define um marcador especial para o fim do fechamento da posição bidirecional. O marcador indica a razão para a posição de fechamento. Ele pode ser uma das seguintes razões:

CampoDescrição
 CLOSE_AS_MARKET Indica que a posição é fechada pelo mercado. Os níveis de stop loss e o take profit não foram colocados ou atingido.
 CLOSE_AS_STOP_LOSS Indica que a posição é fechada devido ter atingido o nível de stop loss.
 CLOSE_AS_TAKE_PROFIT  Indica que a posição é fechada devido ter atingido o nível de take profit.

 

Capítulo 3. Os Fundamentos da Negociação Assíncrona

O assunto de operações assíncronas é complexo e exige um artigo detalhado separado. No entanto, devido ao fato de que o HedgeTerminal usa ativamente as operações assíncronas, é apropriada descrever brevemente os princípios de organização do Expert Advisors que utilizam este tipo de apresentação do pedido. Além disso, não há quase nenhum material sobre o assunto.

3.1. Organização e Esquema do Envio de uma Ordem de Negociação Síncrona

O MetaTrader 5 fornece duas funções para o envio de pedidos de negociação para o servidor:

A função OrderSend() aceita um pedido como uma estrutura MqlTradeRequest preenchida e executa a verificação básica da exatidão da estrutura. Se a verificação básica é bem sucedida, ele envia a solicitação para um servidor, aguarda seu resultado, e, em seguida, retorna o resultado para o segmento personalizado através da estrutura MqlTradeResult e a flag de retorno. Se a verificação básica falhar, a função retorna um valor negativo.

A razão pela qual o pedido não pode ser verificado também está incluído no MqlTradeResult.

O esquema que se segue apresenta a execução do segmento de um programa personalizado em MQL5 com a função OrderSend():

Fig. 6. O esquema de organização e envio de uma solicitação de negociação síncrona

Fig. 6. O esquema de organização e envio de uma solicitação de negociação síncrona

Como pode ser visto a partir do esquema, o segmento do programa MQL5 não pode ser separado do segmento do sistema comum, enviando um pedido ao servidor e executando as operações de negociação na bolsa.

É por isso que, após a conclusão da OrderSend(), nós odemos analisar o resultado real da solicitação de negociação. O segmento personalizado é marcado por setas vermelhas. É executada quase que instantaneamente. Na maioria das vezes é levado a executar operações de negociação na bolsa. Uma vez que os dois segmentos são ligados, uma quantidade considerável de tempo passa entre o início e o fim da função OrderSend(). Devido ao fato de que as operações de negociação são executadas num único segmento, a lógica dos programas MQL5 podem ser sequencial.


3.2. Organização e Esquema de Envio de uma Ordem de Negociação Assíncrona

A função OrderSendAsync() é diferente. Como a OrderSend(), ela aceita a solicitação de negociação MqlTradeRequest e retorna um sinalizador que indica o seu resultado.

No entanto, ao contrário do primeiro exemplo, ele não aguarda o pedido de negociação ser executado pelo servidor, mas retorna os valores obtidos apenas a partir do módulo de verificação básico dos valores de solicitação de negociação (verificação básica dentro do terminal). O esquema a seguir mostra o processo de execução do segmento de execução quando se utiliza a função OrderSendAsync():

Fig. 7. O esquema de organização e envio de uma solicitação de negociação assíncrona.

Fig. 7. O esquema de organização e envio de uma solicitação de negociação assíncrona

Uma vez que o pedido de negociação foi verificado com sucesso, ele é enviado para o servidor de negociação paralelo ao segmento principal. Passando de um pedido de negociação através da rede, bem como a sua execução sobre a bolsa leva algum tempo, como no primeiro caso. Mas o segmento personalizado terá quase que um resultado imediato da função OrderSendAsync().

O esquema acima mostra que a OrderSendAsync() na verdade, forma uma nova linha paralela que é executado por um servidor de negociação, e os seus resultados entram na execução da função OnTradeTransaction() ou OnTrade(). Estas funções iniciam um novo segmento personalizado. O resultado de enviar uma solicitação de negociação deve ser processado neste novo segmento. Isso complica muito a lógica do Expert Advisor, porque com a ordem de envio assíncrona, é impossível organizar o envio do pedido e sua verificação em um único segmento. Por exemplo, você não pode colocar sequencialmente o código para o envio e verificação de um pedido na OnTick().

Vamos escrever uma EA de teste simples para ilustrar acima:

//+------------------------------------------------------------------+
//|                                                    AsynchExp.mq5 |
//|                           Copyright 2014, Vasiliy Sokolov (C-4). |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
input bool UsingAsynchMode=true;
bool sendFlag=false;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(sendFlag)return;
   printf("Formation of order and send to the server...");
   MqlTradeRequest request={0};
   request.magic=12345;
   request.symbol = Symbol();
   request.volume = 0.1;
   request.type=ORDER_TYPE_BUY;
   request.comment= "asynch test";
   request.action = TRADE_ACTION_DEAL;
   request.type_filling=ORDER_FILLING_FOK;
   MqlTradeResult result;
   uint tiks= GetTickCount();
   bool res = false;
   if(UsingAsynchMode)
      res=OrderSendAsync(request,result);
   else
      res=OrderSend(request,result);
   uint delta=GetTickCount()-tiks;
   if(OrderSendAsync(request,result))
     {
      printf("The order has been successfully"+
             "sent to the server.");
     }
  else
     {
     printf("The order is not shipped."+
             " Reason: "+(string)result.retcode);
     }
   printf("Time to send a trade request: "+(string)delta);
   sendFlag=true;
//---
  }

Vamos ter certeza de que a EA funciona por iniciá-lo com UsingAsynchMode = false.

O EA abre uma posição de compra com 0.1 lote. A solicitação de negociação é realizada de forma síncrona com a função OrderSend(). Aqui está o seu registo de exemplo:

2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1)   Time to send a trade request: 94
2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1)   The order has been successfullysent to the server.
2014.11.06 17:49:28.345 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

A solicitação de negociação foi concluída dentro de 94 milissegundos. Desta vez, nos diz que o pedido passou pela verificação básica, foi enviado para o servidor, e, em seguida, foi preenchido.

Agora vamos modificar o código do EA, alterando o volume de transações para o valor máximo possível DBL_MAX:

request.volume = DBL_MAX;

Obviamente, este valor está fora do alcance real. Vamos tentar executar este pedido no modo síncrono:

2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   The order is not shipped. Reason: 10014
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

O envio da ordem falhou. A razão para a falha é o erro 10014 (Volume solicitado inválido). A solicitação falhou durante a verificação básica e nem sequer foi enviada para o servidor, como resultado do tempo de execução do pedido de 0 milissegundos.

Mais uma vez, vamos mudar o pedido. Desta vez, nós especificado um volume grande o suficiente, mas não um valor extremo - 15 lotes. Para a conta de US $ 1.000 em que o EA está sendo testado, é demais. Tal posição não pode ser aberta nesta conta.

Vamos ver o que a OrderSend() retorna:

2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1)   Time to send a trade request: 78
2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1)   The order is not shipped. Reason: 10019
2014.11.06 17:59:22.550 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

O erro é diferente desta vez: 10019 (fundos insuficientes para a execução do pedido, o que é verdade). Note que o tempo de execução do pedido é agora de 79 milissegundos. Ele indica que o pedido foi enviado para o servidor, e que o servidor retornou um erro.

Vamos agora enviar o mesmo pedido com o volume de 15 lotes usando a função OrderSendAsync(). Como a OrderSend(), nenhuma posição é aberta. Mas vamos analisar o log:

2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1)   The order has been successfully sent to the server.
2014.11.06 18:03:58.104 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

O registro diz que não há erro! Uma vez que o erro 10019 é detectado pelo servidor de negociação, ele não está disponível para o segmento atual no modo de envio da ordem assíncrona. O valor de retorno indica apenas que o pedido passou pela verificação básica. A fim de obter o atual erro 10019, precisamos analisar os resultados em um novo segmento personalizado, na OnTradeTransaction() a função do sistema, que deve ser adicionado ao nosso EA:

void  OnTradeTransaction(const MqlTradeTransaction    &trans,
                         const MqlTradeRequest        &request,
                         const MqlTradeResult         &result)
  {
   uint delta = GetTickCount() - tiks;
   printf("Server answer: " + (string)result.retcode + "; Time: " + (string)delta);
  }

Vamos executar o EA novamente e ver os logs:

2014.11.06 18:17:00.943 AsynchExp (AUDCAD,H1)   Server answer: 10019; Time: 94
2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1)   The order has been successfully sent to the server.
2014.11.06 18:17:00.851 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Erro 10019 foi recebida, mas não imediatamente após o envio. Ele foi recebido na nova execução do segmento personalizado na OnTradeTransaction().


3.3. Velocidade de Execução de Ordem Assíncrona

Os traders acreditam erroneamente que a velocidade de execução de um pedido assíncrono está perto de zero.

Ela decorre da observação da OrderSendAsync(), que é completado por via de regra, em menos de um milissegundo. Na realidade, como tem sido demonstrado acima, o tempo real da execução da operação de negociação deve ser medido quando uma resposta do servir é recebido no interior das funções OnTradeTransaction() ou OnTrade(). Esta medida mostra a velocidade real, que é igual à velocidade de uma execução síncrona para uma única ordem. Vantagens reais em tempo de execução são perceptíveis ao enviar um grupo de transações. Há pelo menos três situações em que você precisa enviar várias solicitações:

Lembre-se de levar em conta o limite para o envio de pedidos ao colocar várias ordens.

No MetaTrader 5 build 1010 e superior, o limite é de 64 transações, 4 dos quais estão reservados para os usuários, e as outras estão disponíveis para os Expert Advisors. O limite é destinado a proteger os traders inexperientes de erros graves em seus programas, bem como reduzir a carga de spam em um servidor de negociação.

Isto significa que, ao mesmo tempo, por exemplo, no loop for, você pode enviar até 60 ordens de negociação chamando a SendOrderAsync() com uma solicitação de negociação apropriada. Depois que todas as 60 transações são enviadas, o buffer de transação vai estar cheio. Precisamos aguardar a confirmação do servidor que uma das operações foi processado pelo servidor.

Depois de ser manuseado, o lugar de uma transação no buffer de transações é liberado, e uma nova solicitação de negociação pode levá-lo. Uma vez que o buffer está cheio, o espaço para novas transações é liberado lentamente, porque o servidor de negociação precisa de tempo para processar cada transação, e o evento notificador TradeTransaction() do início do processamento é passado através da rede, o que provoca atrasos adicionais.

Assim, o tempo necessário para enviar os pedidos vai crescer não-linearmente em comparação com o crescimento do número de pedidos. A tabela abaixo apresenta as taxas estimadas de ordem de envio no modo assíncrono. Os testes foram realizados várias vezes, e a taxa mostrada é o valor médio:

Número de pedidosTempo, milissegundos
5050
100180
2002100
5009000
100023000

No caso em que o número de pedidos for inferior a 60, o script não espera a resposta do servidor, por isso é que o tempo é tão pequeno. Ela é aproximadamente igual ao tempo que leva para enviar uma solicitação única. De fato, para obter um tempo de execução real aproximado, adicione o tempo médio de execução do pedido para o tempo de colocação do pedido especificado na tabela.

 

Capítulo 4. Os Fundamentos da Programação Multitarefa no IDE MetaTrader 5

Programadores em MQL5 sabem que as tarefas não podem ser controladas diretamente dos programas em MQL. Essa restrição é para o bem de programadores iniciantes, porque o uso de threads complica muito os algoritmos de programa. No entanto, em algumas situações, dois ou mais EAs devem se comunicar uns com os outros, por exemplo, eles devem criar e ler dados globais.

O HedgeTerminal é um desses EAs. Para informar cada EA usando a biblioteca HedgeTerminalAPI sobre as ações de outros Expert Advisors, o HT organiza a troca de dados através da leitura multi camada e escreve no arquivo ActivePositions.xml. Esta solução não é trivial e é raramente usada por programadores MQL. Portanto, vamos criar um EA multitarefa com o algoritmo semelhante ao HedgeTerminal. Isso vai ajudar a compreender melhor a programação multitarefa e, assim, entender melhor como funciona o HedgeTerminal.


4.1. Programação Multitarefa através do Exemplo do Coletor de Cotações UnitedExchangeQuotes

Vamos aprender as noções básicas de programação multitarefas através de um exemplo específico: vamos escrever um coletor de cotações de diferentes provedores (corretoras).

A idéia é a seguinte: suponha que temos 6-7 corretoras que fornecem cotações para o mesmo instrumento. Naturalmente, as cotações de diferentes corretoras podem variar ligeiramente. A análise destas diferenças abre caminho para estratégias de arbitragem. Além disso, a comparação da dinâmica das cotações vai ajudar a identificar o melhor e o pior provedor. Por exemplo, se uma corretora está oferecendo preços melhores, preferimos selecionar esta corretora para negociar. Não estamos caçando o valor prático dos resultados, em vez disso, apenas descrever o mecanismo pelo qual estes resultados podem ser conseguidos.

Aqui está uma imagem do EA que vamos ter de escrever no final deste capítulo:

Fig. 8. A aparência do coletor de cotação UnitedExhangesQuotes.

Fig. 8. A aparência do coletor de cotação UnitedExhangesQuotes

O Expert Advisor exibe os resultados em uma tabela simples que consiste em quatro colunas e um número ilimitado de linhas.

Cada linha representa uma corretora fornecendo cotações do símbolo (neste caso, EURUSD). Ask e Bid são a melhor oferta de compra e venda da corretora. A imagem mostra que os preços diferem ligeiramente. A diferença entre a oferta de venda da corretora atual e a outra aparece na coluna D-ASK (Delta Ask). Da mesma forma, a diferença entre os valores da oferta de compra é exibida na D-BID (Delta Bid). Por exemplo, no momento em que a imagem foi tirada, a melhor oferta foi fornecida pela "Alpari Limited", e os mais caros foi do "Bank VTB 24".

Programas em MQL não podem acessar o ambiente de outros terminais MetaTrader. Em outras palavras, se um programa está sendo executado em um terminal, ele não pode receber dados de outro. No entanto, todos os programas em MQL podem se comunicar através dos arquivos no diretório compartilhado dos terminais MetaTrader. Se qualquer programa grava informações, por exemplo, a cotação atual, para um arquivo, o programa MQL de outro terminal pode lê-lo. O MQL não tem nenhum outro meio sem DLLs externos. Portanto, vamos usar este método.

A maior dificuldade é organizar esse acesso. Por um lado, o EA tem de ler as cotações de outros fornecedores, e por outro - escrever a cotação de seu fornecedor para o mesmo arquivo. Outro problema é o fato de que, no momento da leitura das cotações, um outro EA pode estar escrevendo uma nova cotação para este arquivo. O resultado desse trabalho paralelo é imprevisível. Na melhor das hipóteses, este será seguido por um acidente e a interrupção do programa, e, na pior, isso vai levar ao surgimento ocasional de erros sutis e estranhos, associados com a exibição das cotações.

Para eliminar esses erros, ou pelo menos minimizar a probabilidade da sua ocorrência, vamos desenvolver um plano claro.

Em primeiro lugar, toda a informação serão armazenadas no formato XML. Este formato tem substituído o arquivo ini desajeitado. XML permite a implementação flexível de seus nós em estruturas complexas de dados, tais como classes. Em seguida, vamos determinar o algoritmo geral de leitura e escrita. Há duas operações básicas: leitura de dados e escrita de dados. Quando qualquer programa MQL está lendo ou gravando, nenhum outro programa pode acessar o arquivo. Assim, eliminar uma situação em que um programa lê os dados, e o segundo altera-los. Devido a isto, o acesso aos dados não será sempre possível.

Vamos criar uma classe especial CQuoteList que vai conter os algoritmos de acesso XML, bem como os dados de todas as cotações a partir deste arquivo.

Uma das funções desta classe é a TryGetHandle(), ele tenta acessar o arquivo e retorna seu identificador no caso de sucesso. Aqui está a implementação da função:

int CQuoteList::TryGetHandle(void)
{
   int attempts = 10;
   int handle = INVALID_HANDLE;
   // We try to open 'attemps' times
   for(att = 0; att < attempts; att++)
   {
      handle = FileOpen("Quotes.xml", FILE_WRITE|FILE_READ|FILE_BIN|FILE_COMMON);
      if(handle == INVALID_HANDLE)
      {
         Sleep(15);
         continue;
      }
      break;
   }
   return handle;
}

Ele faz várias tentativas para abrir o arquivo em modo de leitura/escrita combinada. O número padrão de tentativas é dez.

Se uma tentativa não for bem sucedida, a função congela por 15 milissegundos e tenta abrir o arquivo, fazendo assim até 10 tentativas.

Uma vez que o arquivo é aberto, seu identificador é passado para a função LoadQuotes(). Uma lista completa desta função, e a classe CQuoteList estão disponíveis como um anexo para o artigo. Então aqui nós descrevemos apenas a sequência das ações na função:

  1. TryGetHandle() abre o arquivo para ler e escrever;
  2. O documento XML é carregado para a memória do EA usando a biblioteca Analisador XML;
  3. Com base no documento XML carregado, um novo conjunto de cotações é formado para armazenar as informações necessárias;
  4. O array criado contém uma cotação pertencente ao EA atual. Seus valores são atualizados;
  5. O conjunto de cotações é convertido novamente em um documento XML. O conteúdo do arquivo XML aberto são substituídos com este documento XML;
  6. O arquivo XML de cotações é fechado.

A função LoadQuotes() faz um ótimo trabalho, mas na maioria dos casos, leva menos de 1 milissegundo.

A leitura de dados e a atualização de dados com a maior poupança são combinados em um bloco. Isso é feito de propósito, de modo a não perder o controle de acesso para o arquivo entre as operações de leitura e escrita.

Uma vez que as cotações são carregadas e estão dentro de uma classe, elas podem ser acessados ​​apenas como quaisquer outros dados no MetaTrader 5. Isto é feito através de uma interface especial do MetaTrader 5 - estilo de implementação da interface do programa na classe CQuotesList.

A função chama a renderização de dados e realiza dentro do bloco da OnTick(). Aqui está o seu conteúdo:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
      return;
   if(!QuotesList.LoadQuotes())    
      return;   
   PrintQuote quote = {0};
   Panel.DrawAccess(QuotesList.CountAccess());
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   string brokerName = AccountInfoString(ACCOUNT_COMPANY);
   for(int i = 0; i < QuotesList.BrokersTotal(); i++)
   {
      if(!QuotesList.BrokerSelect(i))
         continue;
      if(!QuotesList.SymbolSelect(Symbol()))
         continue;
      quote.ask = QuotesList.QuoteInfoDouble(QUOTE_ASK);
      quote.bid = QuotesList.QuoteInfoDouble(QUOTE_BID);
      quote.delta_ask = ask - quote.ask;
      quote.delta_bid = quote.bid - bid;
      quote.broker_name = QuotesList.BrokerName();
      quote.index = i;
      Panel.DrawBroker(quote);
   }
  }

Vale ressaltar que o código de exemplo funciona tanto no MetaTrader 4 quanto no MetaTrader 5 sem quaisquer modificações adicionais!

Existem apenas pequenas diferenças cosméticas na forma como os painéis são exibidos em diferentes versões do terminal. Sem dúvida, este é um fato notável que facilita a portabilidade de código entre plataformas.

A operação do EA é melhor observado na dinâmica. O vídeo abaixo mostra o funcionamento do EA em contas diferentes:

 

Ler e escrever em um arquivo têm vantagens significativas, mas há também algumas desvantagens.

As principais vantagens são:

  1. Flexibilidade. Você pode armazenar e carregar todos os dados, inclusive classes inteiras;
  2. Velocidade relativamente alta. Todo o ciclo de leitura e reescrita quase sempre leva não mais de 1 milissegundo, que é um bom tempo, em comparação com operações de negociação relativamente lentas que levam de 80-150 milissegundos ou às vezes até mais;
  3. Baseado nas ferramentas padrão da língua MQL5 sem chamar DLL.

O principal inconveniente de tal solução é uma séria carga no sistema de armazenamento. Quando há uma cotação e duas corretores, o número de operações de reescrita é relativamente pequeno, mas com um fluxo pesado de cotações e um grande número de corretoras/símbolos, o número de operações de reescrita torna-se muito grande. Em menos de uma hora, o EA demo produziu mais de 90 mil Quotes.xml operações de reescrita no arquivo. Estas estatísticas são mostradas na parte superior do painel do EA: "I/O Rewrite" mostra o número total de regravações no arquivo, "fps" indica a relação entre as duas últimas operações de reescrita, e "AVRG" exibe a velocidade média de regravações por segundo.

Se você armazenar arquivos no SSD ou HDD, estas operações terão um impacto negativo sobre a vida útil do disco. Por isso, é melhor usar um disco de RAM virtual para o referido intercâmbio dos dados.

Ao contrário do exemplo anterior, HedgeTerminal quase não usa ActivePositions.xml, Escrevendo apenas as mudanças de posição significativas que são inacessíveis por meio do contexto global. Então ela produz muito menos operações de leitura/escrita do que o exemplo acima, e, portanto, não requer quaisquer condições especiais, tais como discos de RAM.


4.2. Uso da Interação Multitarefa entre os Expert Advisors

Interação em tempo real entre os programas MQL independentes é um assunto complicado, mas interessante. O artigo contém apenas uma breve descrição do mesmo, mas é digno de um artigo separado. Na maioria dos casos não é necessária a interação multitarefa entre Expert Advisors. No entanto, aqui está uma lista de tarefas e a variedade de programas, para os quais é necessária a organização de tal interação:


Descrição dos Anexos

Aqui está uma breve descrição dos arquivos anexados ao artigo, bem como do processo de elaboração.

Prototypes.mqh é um arquivo com a descrição das funções da biblioteca HedgeTerminalAPI. Este arquivo contém a descrição e os protótipos das funções da biblioteca HedgeTerminalAPI. Ele permite que o seu EA saiba o quais funções e modificadores estão disponíveis na biblioteca, como chamar as funções, e que valores eles retornam.

Salve este arquivo para C:\Program Files\MetaTrader 5\MQL5\Include, onde "C:\Program Files\MetaTrader 5\" é o nome do diretório onde o seu terminal MetaTrader 5 está instalado. Uma vez que o arquivo foi copiado para o diretório correto, você pode referir a ele em seu programa MQL. Isto deve ser feito sempre que você precisar usar a biblioteca HedgeTerminalAPI. Para se referir ao arquivo Prototypes.mqh, adicione o arquivo especial de inclusão do diretório em seu código:

#include <Prototypes.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //...
   // Here is the content of your program.
   //...
  }

No exemplo acima, esta diretiva é marcada em amarelo e é chamada de "#include <Ptototypes.mqh>". Agora, o script acima pode referir-se as funções de biblioteca e utilizar a sua funcionalidade.

Por favor, note que, no processo de desenvolvimento da biblioteca HedgeTerminalAPI, o arquivo dos protótipos podem sofrer pequenas alterações. Muitas vezes, com a atualização da versão da biblioteca, você precisará atualizar o arquivo de protótipo, que irá descrever as alterações. Por favor, tome este fator desconfortável com compreensão. Em qualquer caso, a mais recente versão do arquivo protótipo pode ser sempre instalados manualmente a partir da biblioteca (o procedimento de instalação é descrito na seção 1.1) ou descarregado a partir do anexo a este artigo (atualizações regulares para os anexos são esperados).

Chaos2.mqh é um código fonte do EA Chaos2. Seu funcionamento é descrito no ponto 1.12: "A função SendTradeRequest e a estrutura HedgeTradeRequest através do exemplo do EA Chaos II. Para compilar o código com êxito, salve o arquivo da função protótipos para o diretório correspondente \Include e salvae a biblioteca HedgeTerminalAPI para: C:\Program Files\MetaTrader 5\MQL5\Market\hedgeterminalapi.ex5. Onde "C:\Program Files\MetaTrader 5\" é o nome do diretório (pasta de dados do terminal), onde seu terminal MetaTrader 5 está instalado.

O código fonte UnitedExchangeQuotes é o arquivo zip especial (unitedexchangequotes.zip) Que contém o projeto descrito em detalhes no capítulo 4: "Os fundamentos da programação multitarefas no IDE MetaTrader 5". Este zip contém os seguintes arquivos:

Todos os arquivos do arquivo conter caminhos relativos. Por exemplo, um arquivo UnitedExchangeQuotes.mq5 está localizado na pasta \MQL5\Experts. Isso significa que ele deve ser colocado no mesmo subdiretório da pasta de dados do terminal MetaTrader 5, tais como C:\Program Files\MetaTrader 5\MQL5\Experts\UnitedExchangeQuotes.mq5.


Conclusão

Nós consideramos os detalhes de se trabalhar com a interface do programa HedgeTerminal.

Ele Demonstrou que os princípios da presente biblioteca são muito semelhantes para a API MetaTrader 4. Como no API MetaTrader 4 , antes de começar a trabalhar com uma transação (um análogo do conceito de "ordem" em MetaTrader 4), primeiro você deve selecioná-lo usando a TransactionSelect(). A operação no Hedge Terminal é uma posição bi-direcional como uma regra. Uma vez que uma posição foi selecionada, você pode obter suas propriedades ou aplicar uma ação de negociação para que, por exemplo, definir um nível de stop loss ou fechá-la. Esta seqüência de ações é quase idêntica ao algoritmo para trabalhar com ordens no MetaTrader 4.

Além de informações básicas sobre o número de posições bidirecionais e suas propriedades, HedgeTerminal fornece acesso aos valores que não estão disponíveis diretamente no MetaTrader 5 e exigem cálculos analíticos complexos. Por exemplo, você pode ver a quantidade de desvio de cada posição bidirecional, solicitando apenas uma de suas propriedades. Você pode verifica o número de transações dentro de uma posição selecionada Todos esses cálculos e a correspondência necessária das negociações são realizadas "por trás das cenas" durante o ínicio do HedgeTerminal. Ele é conveniente porque o Expert Advisor de negociação não precisa calcular nada. Toda a informação necessária já foi calculada e está disponível através de uma API simples e intuitiva.

A utilização dos algoritmos comuns pelo painel e API HedgeTerminal permite a apresentação dos dados unificados. Portanto, você pode controlar os EAs partir do painel HedgeTerminal, enquanto que as alterações feitas pelo EA será exibido diretamente no painel.