English Español Deutsch 日本語
preview
MQL5 Trading Toolkit (Parte 1): Desenvolvendo uma Biblioteca EX5 para Gerenciamento de Posições

MQL5 Trading Toolkit (Parte 1): Desenvolvendo uma Biblioteca EX5 para Gerenciamento de Posições

MetaTrader 5Exemplos | 23 outubro 2024, 10:13
89 0
Kelvin Muturi Muigua
Kelvin Muturi Muigua

Introdução

Como desenvolvedor de software, muitas vezes acho conveniente e eficiente criar minhas próprias bibliotecas de código ou kits de ferramentas. Isso me economiza tempo porque não preciso reescrever repetidamente o código para tarefas comuns exigidas em meus vários projetos de desenvolvimento em MQL5. Nesta série de artigos, vamos criar uma biblioteca de negociação MQL5 responsável por executar tarefas repetitivas em projetos comuns de desenvolvimento MQL5.

Neste primeiro artigo, discutiremos o que são bibliotecas de desenvolvedor, por que elas são importantes e os diferentes tipos de bibliotecas de código que você pode criar com MQL5. Em seguida, prosseguiremos para criar uma biblioteca de funções MQL5 para lidar com várias operações de posição como um exemplo prático para ajudar a solidificar sua compreensão de como você pode usar uma biblioteca de código para um projeto do mundo real.


O que são bibliotecas de código em MQL5?

As bibliotecas de código MQL5 são funções de código pré-escritas (ex5) ou bibliotecas vinculadas dinamicamente (DLLs) que podemos usar para acelerar de forma eficiente o processo de desenvolvimento de Expert Advisors, indicadores personalizados, scripts ou serviços para a plataforma MetaTrader 5. 

Imagine uma biblioteca de código como uma caixa de ferramentas de um mecânico. Assim como a caixa de ferramentas do mecânico contém uma variedade de ferramentas (chaves, chaves de fenda, etc.) para tarefas específicas, uma biblioteca de código contém funções pré-escritas que desempenham papéis semelhantes. Cada função aborda uma tarefa específica dentro do seu programa, como abrir, fechar ou modificar posições, enviar notificações push, gerenciar bancos de dados, etc., assim como uma chave aperta parafusos ou uma chave de fenda gira parafusos.


Tipos de bibliotecas de código em MQL5

Como desenvolvedor MQL5, você tem várias opções para construir sua biblioteca de código ou kit de ferramentas:

  • Bibliotecas de Função (ex5): Essas bibliotecas implementam um estilo de codificação procedural, oferecendo uma coleção de funções pré-escritas para tarefas específicas. Elas também fornecem a vantagem adicional de segurança através da encapsulação de código. Pense nelas como ferramentas individuais, cada uma projetada para um trabalho específico.
  • DLLs C++ de Terceiros: Você pode integrar bibliotecas C++ pré-escritas como DLLs (Dynamic Linked Libraries). Isso expande as capacidades do MQL5, permitindo que você aproveite funcionalidades externas.

O MetaTrader 5 também oferece formas adicionais de estender seu kit de ferramentas:

  • Bibliotecas .NET: O MetaEditor oferece integração perfeita com bibliotecas .NET através da importação de funções "inteligentes", eliminando a necessidade de wrappers personalizados.
  • Módulo de Linguagem Python: O módulo de linguagem Python recentemente suportado permite que você aproveite as funcionalidades do Python dentro de seus projetos MQL5.

Se você estiver confortável com C++, pode criar DLLs personalizadas que se integram facilmente em seus projetos MQL5. Você pode utilizar ferramentas como o Microsoft Visual Studio para desenvolver arquivos de código-fonte C++ (CPP e H), compilá-los em DLLs e, em seguida, importá-los para o MetaEditor para uso com seu código MQL5.

Outros recursos de código semelhantes às bibliotecas MQL5

  • Classe/Include (*.mqh): Os arquivos de inclusão MQL5 utilizam programação orientada a objetos, oferecendo classes pré-construídas que encapsulam dados e funcionalidades. Imagine-os como ferramentas mais complexas que combinam funções/métodos e estruturas de dados. Em essência, uma classe ou estrutura não pode ser exportada para criar uma biblioteca MQL5 (ex5), mas você pode usar ponteiros e referências para classes ou estruturas em funções de biblioteca MQL5.


Por que você precisa criar ou usar uma biblioteca MQL5 ex5?

Criar suas próprias bibliotecas de código para projetos MQL5 pode tornar seu processo de desenvolvimento muito mais eficiente. Essas bibliotecas, salvas como arquivos .ex5, funcionam como uma caixa de ferramentas pessoal cheia de funções que você otimizou para tarefas específicas.

Fácil reutilização e design modular

Economize tempo sem precisar reescrever funções comuns toda vez que iniciar um novo projeto. Com bibliotecas .ex5, você escreve o código uma vez, o otimiza para o melhor desempenho, exporta as funções e, em seguida, as importa facilmente para qualquer projeto. Essa abordagem modular mantém seu código limpo e organizado, separando funcionalidades principais da lógica específica do projeto. Cada parte da biblioteca se torna um bloco de construção para criar sistemas de negociação poderosos.

Funções seguras com encapsulamento

Criar bibliotecas MQL5 ajuda a compartilhar suas funções, mantendo o código-fonte oculto. O encapsulamento garante que os detalhes do seu código sejam seguros e não visíveis para os usuários, enquanto ainda fornece uma interface clara para funcionalidade. Você só precisa compartilhar o arquivo de biblioteca .ex5 juntamente com documentação clara das definições das funções exportadas com outros desenvolvedores para que eles possam importar as funções da biblioteca em seus projetos. O arquivo de biblioteca .ex5 efetivamente esconde o código-fonte e como as funções exportadas são codificadas, mantendo um ambiente de trabalho seguro e encapsulado no código principal do seu projeto.

Atualizações fáceis e benefícios a longo prazo

Quando novos recursos de linguagem são lançados ou antigos se tornam obsoletos, atualizar seu código é fácil com bibliotecas .ex5. Basta atualizar o código da biblioteca, redistribuir e recompilar todos os projetos que a utilizam para obter automaticamente as mudanças. Isso economiza muito tempo e esforço, especialmente para projetos grandes. A biblioteca atua como um sistema central para sua base de código, e uma atualização ou mudança afetará todos os projetos relacionados, tornando seu trabalho mais eficiente a longo prazo.


Como criar uma biblioteca ex5 em MQL5?

Todas as bibliotecas .ex5 começam como arquivos de código-fonte .mq5 com a diretiva de biblioteca #property adicionada no início do arquivo e uma ou mais funções designadas como exportáveis usando a palavra-chave especial export. O arquivo de código-fonte .mq5 é transformado em um arquivo de biblioteca .ex5 após ser compilado, encapsulando ou ocultando com segurança o código-fonte e tornando-o pronto para import e uso em outros projetos MQL5.

Criar uma nova biblioteca MQL5 é fácil com o IDE MetaEditor. Siga os passos abaixo para criar um novo arquivo de código-fonte de biblioteca (.mq5) que conterá as funções de gerenciamento de posições e, posteriormente, será compilado em uma biblioteca .ex5.

Passo 1: Abra o IDE MetaEditor e inicie o 'MQL Wizard' usando o botão do item de menu 'Novo'.

Arquivo de nova biblioteca Mql5Wizard

Passo 2: Selecione a opção 'Biblioteca' e clique em 'Próximo'.

Arquivo de nova biblioteca Mql5 Wizard

Passo 3: Na seção 'Propriedades Gerais do Arquivo da Biblioteca', preencha a pasta e o nome da sua nova biblioteca "Libraries\Toolkit\PositionsManager" e prossiga clicando em 'Concluir' para gerar nossa nova biblioteca.

Propriedades gerais do novo arquivo de biblioteca Mql5 Wizard

Uma Biblioteca MQL5 é geralmente armazenada em um arquivo com a extensão ".mq5". Esse arquivo contém o código-fonte de diferentes funções escritas para várias tarefas específicas. As bibliotecas de código são armazenadas na pasta MQL5\Libraries por padrão dentro do diretório de instalação do MetaTrader 5. Uma maneira rápida de acessar a pasta Libraries é utilizando o painel Navegador no MetaEditor.

Pasta de bibliotecas padrão do MQL5 no painel Navegador do MetaEditor

Agora temos um novo arquivo de biblioteca MQL5 em branco PositionsManager.mq5 que contém as diretivas de propriedade de cabeçalho e uma função comentada. Lembre-se de salvar o novo arquivo antes de continuar. Este é o aspecto do nosso arquivo de biblioteca recém-gerado:

//+------------------------------------------------------------------+
//|                                             PositionsManager.mq5 |
//|                          Copyright 2024, Wanateki Solutions Ltd. |
//|                                         https://www.wanateki.com |
//+------------------------------------------------------------------+
#property library
#property copyright "Copyright 2024, Wanateki Solutions Ltd."
#property link      "https://www.wanateki.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| My function                                                      |
//+------------------------------------------------------------------+
// int MyCalculator(int value,int value2) export
//   {
//    return(value+value2);
//   }
//+------------------------------------------------------------------+

Componentes de um Arquivo de Código-Fonte de Biblioteca MQL5

Um arquivo de código-fonte de biblioteca MQL5 (.mq5) consiste em dois componentes principais:

1. A diretiva #property library: Esta diretiva de propriedade deve ser adicionada no topo do arquivo de código-fonte .mq5 da biblioteca. A propriedade da biblioteca informa ao compilador que o arquivo em questão é uma biblioteca, e essa indicação é armazenada no cabeçalho da biblioteca compilada no arquivo .ex5 resultante da compilação da biblioteca .mq5.

#property library

2. Funções Exportadas: No núcleo das bibliotecas MQL5 estão as funções exportadas. Esses são os principais componentes de uma biblioteca, pois são responsáveis por executar todas as tarefas importantes que a biblioteca deve realizar. Uma função MQL5 exportada é semelhante a uma função MQL5 comum, exceto que é declarada com o modificador de exportação, permitindo que ela seja importada e utilizada em outros programas MQL5 após a compilação. O modificador export instrui o compilador a adicionar a função especificada à tabela de funções ex5 exportadas por este arquivo de biblioteca. Apenas funções declaradas com o modificador export são acessíveis e detectáveis por outros programas MQL5, onde podem ser chamadas após a importação com a diretiva especial #import.

//+------------------------------------------------------------------+
//| Example of an exported function with the export postmodifier         |
//+------------------------------------------------------------------+
int ExportedFunction(int a, int b) export
  {
   return(a + b);
  }
//+------------------------------------------------------------------+

As bibliotecas MQL5 não são esperadas ou requeridas para executar diretamente qualquer manipulação de eventos padrão, como consultores especializados, indicadores personalizados ou scripts. Isso significa que elas não possuem nenhuma das funções padrão, como OnInit(), OnDeinit() ou OnTick().


Biblioteca de Funções MQL5 para Gerenciamento de Posições

O gerenciamento de posições é uma tarefa fundamental para todo Consultor Especializado em desenvolvimento. Essas operações essenciais formam o núcleo de qualquer sistema de negociação algorítmica. Para evitar a codificação repetitiva, os desenvolvedores MQL5 devem usar bibliotecas para gerenciar posições de forma eficiente. Isso garante que os desenvolvedores não precisem reescrever o mesmo código de gerenciamento de posições para cada Consultor Especializado.

Nesta seção, vamos adicionar algum código ao nosso arquivo recém-criado PositionsManager.mq5 para criar uma biblioteca de gerenciamento de posições usando MQL5. Esta biblioteca lidará com todas as operações relacionadas a posições. Ao importá-la para o seu código do Consultor Especializado, você poderá executar e gerenciar posições de forma eficaz, mantendo sua base de código limpa e organizada.


Variáveis globais


As operações de solicitação de negociação em MQL5 são representadas por uma estrutura especial predefinida conhecida como MqlTradeRequest. Essa estrutura inclui todos os campos necessários para executar operações de negociação e garante que todos os dados de solicitação de ordem necessários sejam incluídos em uma única estrutura de dados. Quando uma solicitação de negociação é processada, o resultado é salvo em outra estrutura predefinida chamada MqlTradeResult. O MqlTradeResult é responsável por fornecer informações detalhadas sobre o resultado da solicitação de negociação, incluindo se a solicitação foi bem-sucedida e qualquer dado relevante sobre a execução da negociação.

Como utilizaremos essas duas estruturas de dados especiais na maioria de nossas funções, vamos começar declarando-as como variáveis globais para que estejam disponíveis em toda a biblioteca.

//---Global variables
//-------------------------------
//-- Trade operations request and result data structures
MqlTradeRequest tradeRequest;
MqlTradeResult  tradeResult;
//-------------------------------


Função de Gerenciamento de Erros de Posição


A primeira função em nossa biblioteca será uma função de Gerenciamento de Erros. Ao abrir, modificar e fechar posições com MQL5, frequentemente encontramos diferentes tipos de erros que exigem que abortemos a operação ou reenviemos a solicitação de gerenciamento de posição para o servidor de negociação novamente. Criar uma função dedicada para monitorar e recomendar a ação correta é uma necessidade para ter uma biblioteca devidamente codificada e otimizada.

Antes de podermos criar a função de tratamento de erros, precisamos entender os vários códigos de erro de gerenciamento de posições MQL5 que podemos encontrar. A tabela abaixo destaca alguns dos códigos de erro retornados pelo servidor de negociação e em tempo de execução que precisamos detectar e superar ao gerenciar diferentes operações de posição. Você pode encontrar uma lista completa de todos os códigos de erros e avisos na documentação do MQL5.

Código Constante de Código  Descrição
Ação Tomada Tipo de Código 
10004  TRADE_RETCODE_REQUOTE Recotação Reenviar a solicitação de pedido novamente. Código de retorno do servidor de negociação (RETCODE)
10008  TRADE_RETCODE_PLACED Pedido colocado Nenhuma ação necessária. Operação bem-sucedida. Código de retorno do servidor de negociação (RETCODE)
10009  TRADE_RETCODE_DONE Solicitação concluída Nenhuma ação necessária. Operação completada.
Código de retorno do servidor de negociação (RETCODE)
10013  TRADE_RETCODE_INVALID Solicitação inválida Pare de reenviar a solicitação de inicialização do pedido e atualize os detalhes do pedido. Código de retorno do servidor de negociação (RETCODE)
10014  TRADE_RETCODE_INVALID_VOLUME Volume inválido na solicitação Pare de reenviar a solicitação de inicialização do pedido e atualize os detalhes do pedido. Código de retorno do servidor de negociação (RETCODE)
10016  TRADE_RETCODE_INVALID_STOPS Stops inválidos na solicitação Pare de reenviar a solicitação de inicialização do pedido e atualize os detalhes do pedido. Código de retorno do servidor de negociação (RETCODE)
10017  TRADE_RETCODE_TRADE_DISABLED Negociação desativada Encerre todas as operações de negociação e pare de reenviar a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10018  TRADE_RETCODE_MARKET_CLOSED Mercado fechado Pare de reenviar a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10019  TRADE_RETCODE_NO_MONEY Não há dinheiro suficiente para concluir a solicitação Pare de reenviar a solicitação de inicialização do pedido e atualize os detalhes do pedido. Código de retorno do servidor de negociação (RETCODE)
10026  TRADE_RETCODE_SERVER_DISABLES_AT Negociação automática desativada pelo servidor Negociação não é permitida pelo servidor. Pare de reenviar a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10027  TRADE_RETCODE_CLIENT_DISABLES_AT Negociação automática desativada pelo terminal do cliente O terminal do cliente desativou a negociação por EA. Pare de reenviar a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10034  TRADE_RETCODE_LIMIT_VOLUME O volume de ordens e posições para o símbolo atingiu o limite Pare de reenviar a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10011  TRADE_RETCODE_ERROR Erro no processamento da solicitação Continue reenviando a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10012  TRADE_RETCODE_TIMEOUT Solicitação cancelada por timeout Pause a execução por alguns milissegundos e continue reenviando a solicitação de inicialização do pedido. Código de retorno do servidor de negociação (RETCODE)
10015  TRADE_RETCODE_INVALID_PRICE Preço inválido na solicitação Atualize o preço de entrada do pedido e reenviar a solicitação de inicialização. Código de retorno do servidor de negociação (RETCODE)
10020  TRADE_RETCODE_PRICE_CHANGED Os preços mudaram Atualize o preço de entrada do pedido e reenviar a solicitação de inicialização. Código de retorno do servidor de negociação (RETCODE)
10021  TRADE_RETCODE_PRICE_OFF Não há cotações para processar a solicitação Pause a execução por alguns milissegundos e reenviar a solicitação de inicialização. Código de retorno do servidor de negociação (RETCODE)
10024  TRADE_RETCODE_TOO_MANY_REQUESTS Solicitações muito frequentes Pause a execução por alguns segundos e reenviar a solicitação de inicialização. Código de retorno do servidor de negociação (RETCODE)
 10031  TRADE_RETCODE_CONNECTION Sem conexão com o servidor de negociação Pause a execução por alguns milissegundos e reenviar a solicitação de inicialização novamente. Código de retorno do servidor de negociação (RETCODE)
 0  ERR_SUCCESS A operação foi concluída com sucesso Pare de reenviar a solicitação. Pedido enviado com sucesso.   Código de erro em tempo de execução
 4752  ERR_TRADE_DISABLED Negociação por Expert Advisors proibida Pare de reenviar a solicitação de inicialização do pedido. Código de erro em tempo de execução
 4753  ERR_TRADE_POSITION_NOT_FOUND Posição não encontrada Pare de reenviar a solicitação de operação de negociação.
Código de erro em tempo de execução
 4754  ERR_TRADE_ORDER_NOT_FOUND Pedido não encontrado Pare de reenviar a solicitação do pedido.
Código de erro em tempo de execução
 4755  ERR_TRADE_DEAL_NOT_FOUND Negociação não encontrada Pare de reenviar a solicitação do pedido.
Código de erro em tempo de execução


Vamos criar nossa primeira função na biblioteca para processar os erros mencionados acima. A função de processamento de erro, chamada ErrorAdvisor(), será do tipo booleano, o que significa que retornará True ou False dependendo do tipo de erro encontrado. Ela aceitará dois argumentos para auxiliar no processamento dos dados:

  1. callingFunc (string): Este parâmetro armazena o nome ou identificador da função que chama ErrorAdvisor().
  2. symbol (string): Este parâmetro armazena o nome do símbolo do ativo em questão.
  3. tradeServerErrorCode (integer): Este parâmetro armazena o tipo de erro encontrado.

Se o erro for recuperável e não crítico, a função ErrorAdvisor() retornará True. Isso indica para a função chamadora que o pedido ainda não foi executado e que ela deve reenviar a solicitação do pedido. Se ErrorAdvisor() retornar False, significa que a função chamadora deve parar de enviar mais solicitações, pois o pedido já foi executado com sucesso ou um erro crítico, não recuperável, foi encontrado.

Lembre-se de colocar o pós-modificador export antes da abertura da chave da função para indicar que a função pertence a uma biblioteca e é destinada a ser usada em outros programas MQL5.

//------------------------------------------------------------------+
// ErrorAdvisor(): Error analysis and processing function.          |
// Returns true if order opening failed and order can be re-sent    |
// Returns false if the error is critical and can not be executed   |
//------------------------------------------------------------------+
bool ErrorAdvisor(string callingFunc, string symbol, int tradeServerErrorCode)  export
  {
//-- place the function body here
  }

Vamos começar declarando e inicializando uma variável inteira para armazenar o erro de tempo de execução atual. Nomeie a variável inteira runtimeErrorCode e chame a função GetLastError() para armazenar o erro de tempo de execução mais recente. Usaremos essa variável para processar qualquer erro de tempo de execução que possamos encontrar no segundo operador switch aninhado.

//-- save the current runtime error code
   int runtimeErrorCode = GetLastError();

Vamos escanear e processar os erros retornados pelo servidor de negociações (retcode) e erros de tempo de execução usando operadores switch aninhados. Isso é conveniente porque permite identificar rapidamente o tipo de erro, imprimir uma descrição para o usuário no log do ExpertAdvisor e instruir a função chamadora sobre como proceder. Você notará que agrupei os erros em duas categorias:

  1. Erros que indicam conclusão ou não execução do pedido: Esses erros retornarão False, instruindo a função chamadora a parar de enviar solicitações de pedido.
  2. Erros que indicam pedidos incompletos: Esses erros retornarão True, instruindo a função chamadora a reenviar a solicitação do pedido.

O primeiro operador switch lidará com os códigos de retorno do servidor de negociações e o segundo switch aninhado lidará com os códigos de erro de tempo de execução. Essa abordagem minimiza o código da função ao evitar verificações sequenciais para cada código de erro ou aviso.

Agora, vamos codificar a primeira instrução switch para examinar o tradeServerErrorCode encontrado e ver que tipo de erro o servidor de negociação relatou.

switch(tradeServerErrorCode)//-- check for trade server errors
     {
        //--- Cases to scan different retcodes/server return codes
     }

Dentro da instrução switch, adicionaremos casos para diferentes códigos de erro retornados pelo servidor de negociações. Aqui estão alguns deles:

Requote (código 10004): Isso significa que o preço mudou desde que o usuário tentou abrir o pedido. Nesse caso, vamos querer imprimir uma mensagem no log, esperar alguns milissegundos usando a função Sleep() e, em seguida, informar a função chamadora para tentar abrir o pedido novamente.

case 10004:
    Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Requote!");
    Sleep(10);
    return(true);    //--- Exit the function and retry opening the order again

Pedido Colocado com Sucesso (código 10008): Se esse código for retornado, tudo correu bem e o pedido foi colocado. Podemos imprimir uma mensagem no log dizendo que o pedido foi bem-sucedido e, em seguida, informar a função chamadora para parar de tentar abrir o pedido.

case 10008:
    Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Order placed!");
    return(false);    //--- success - order placed ok. exit function

Adicionaremos casos semelhantes para outros erros do servidor de negociações (códigos 10009, 10011, 10012, etc.), seguindo a mesma lógica de imprimir uma mensagem no log do ExpertAdvisor, esperar um pouco, se necessário, e instruir a função chamadora sobre se deve ou não tentar reenviar a solicitação de negociação novamente.

Se a instrução switch para erros do servidor de negociações não encontrar uma correspondência, isso significa que o erro pode ser um erro de tempo de execução e só pode ser encontrado através da criação de outra instrução switch que analise o erro de tempo de execução atual retornado pela função GetLastError(). Para resolver esse desafio, usaremos uma instrução switch aninhada na seção default da instrução switch anterior para examinar o valor de runtimeErrorCode que salvamos anteriormente.

default:
  switch(runtimeErrorCode)//-- check for runtime errors
    //-- Add cases for different runtime errors here
  }
}

Repita o mesmo processo que fizemos com os erros do servidor de negociações e adicione casos para diferentes códigos de erro de tempo de execução:

Sem Erros (código 0): Isso significa que tudo funcionou bem do ponto de vista do nosso programa. Podemos imprimir uma mensagem no log dizendo que não houve erros e, em seguida, informar a função chamadora para parar de tentar.

case 0:
    Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") The operation completed successfully!");
    ResetLastError(); //--- reset error cache
    return(false);    //--- Exit the function and stop trying to open order

Continuaremos adicionando casos semelhantes para outros erros de tempo de execução (códigos 4752, 4753, 4754, etc.), seguindo a mesma lógica de imprimir uma mensagem no log de erro do ExpertAdvisor e informar a função chamadora para parar ou continuar com a solicitação de negociação.

Como consideramos apenas os códigos de erro mais importantes que podem afetar o processo de execução do pedido e não escaneamos ou processamos todos os códigos de erro possivelmente existentes, podemos encontrar um erro que atualmente não é processado ou contabilizado em nosso código atual. Nesse caso, imprimiremos uma mensagem no log indicando que ocorreu um erro desconhecido (outro), especificaremos o código de erro de retorno do servidor e o erro de tempo de execução encontrado, e, em seguida, informaremos à função chamadora para parar de tentar abrir o pedido.

default: //--- All other error codes
    Print(symbol, " - ", callingFunc, " *OTHER* Error occurred \r\nTrade Server RetCode: ", tradeServerErrorCode, ", Runtime Error Code = ", runtimeErrorCode);
    ResetLastError(); //--- reset error cache
    return(false);    //--- Exit the function and stop trying to open order
    break;

Aqui está a função completa de gerenciamento de erros ErrorAdvisor() com todos os segmentos de código concluídos:

bool ErrorAdvisor(string callingFunc, string symbol, int tradeServerErrorCode)  export
  {
//-- save the current runtime error code
   int runtimeErrorCode = GetLastError();

   switch(tradeServerErrorCode)//-- check for trade server errors
     {
      case 10004:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Requote!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again
      case 10008:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Order placed!");
         return(false);    //--- success - order placed ok. exit function

      case 10009:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Request completed!");
         return(false);    //--- success - order placed ok. exit function

      case 10011:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Request processing error!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again

      case 10012:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Request canceled by timeout!");
         Sleep(100);
         return(true);    //--- Exit the function and retry opening the order again

      case 10015:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Invalid price in the request!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again

      case 10020:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Prices changed!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again

      case 10021:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") There are no quotes to process the request!");
         Sleep(100);
         return(true);    //--- Exit the function and retry opening the order again

      case 10024:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Too frequent requests!");
         Sleep(1000);
         return(true);    //--- Exit the function and retry opening the order again

      case 10031:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") No connection with the trade server!");
         Sleep(100);
         return(true);    //--- Exit the function and retry opening the order again

      default:
         switch(runtimeErrorCode)//-- check for runtime errors
            case 0:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") The operation completed successfully!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4752:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Trading by Expert Advisors prohibited!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4753:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Position not found!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4754:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Order not found!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4755:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Deal not found!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            default: //--- All other error codes
               Print(symbol, " - ", callingFunc, " *OTHER* Error occurred \r\nTrade Server RetCode: ", tradeServerErrorCode, ", Runtime Error Code = ", runtimeErrorCode);
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order
               break;
           }
     }
  }


Função de Permissões de Negociação


Esta função verifica se a negociação está atualmente permitida no terminal de negociação. Ela considera a autorização do usuário, do servidor de negociação e do corretor. A função é chamada antes que qualquer operação de posição ou solicitação de pedido seja enviada para processamento ao servidor de negociação.

Nomearemos a função como TradingIsAllowed() e daremos a ela um tipo de retorno booleano</i>. Se a negociação estiver permitida e ativada, ela retornará um valor booleano</i> de True</i> e um valor booleano</i> de False</i> se a negociação automática estiver desativada ou não permitida. Ela não terá nenhum parâmetro ou argumento e conterá o segmento de código abaixo:

//+-----------------------------------------------------------------------+
//| TradingIsAllowed() verifies whether auto-trading is currently allowed |                                                                 |
//+-----------------------------------------------------------------------+
bool TradingIsAllowed() export
  {
   if(
      !IsStopped() &&
      MQLInfoInteger(MQL_TRADE_ALLOWED) && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) &&
      AccountInfoInteger(ACCOUNT_TRADE_ALLOWED) && AccountInfoInteger(ACCOUNT_TRADE_EXPERT)
   )
     {
      return(true);//-- trading is allowed, exit and return true
     }
   return(false);//-- trading is not allowed, exit and return false
  }


Função de Registro e Impressão de Detalhes do Pedido


Esta é uma função simples para registrar e imprimir as propriedades das diferentes operações de negociação ou solicitações de abertura de posição. Ela fornece uma maneira simples para o usuário do ExpertAdvisor se manter atualizado sobre o status das operações do ExpertAdvisor através da aba de log do MetaTrader 5 EA. Você notará que esta função não é exportada e, portanto, só é acessível por outras funções exportadas que a chamam explicitamente ou a executam. Nomearemos esta função como PrintOrderDetails() e especificaremos que ela não retornará nenhum dado, tornando-a uma função do tipo void, e aceitará uma variável do tipo string como seu parâmetro ou argumento de entrada.

//+-----------------------------------------------------------------------+
//| PrintOrderDetails() prints the order details for the EA log           |
//+-----------------------------------------------------------------------+
void PrintOrderDetails(string header)
  {
   string orderDescription;
//-- Print the order details
   orderDescription += "_______________________________________________________________________________________\r\n";
   orderDescription += "--> "  + tradeRequest.symbol + " " + EnumToString(tradeRequest.type) + " " + header +
                       " <--\r\n";
   orderDescription += "Order ticket: " + (string)tradeRequest.order + "\r\n";
   orderDescription += "Volume: " + StringFormat("%G", tradeRequest.volume) + "\r\n";
   orderDescription += "Price: " + StringFormat("%G", tradeRequest.price) + "\r\n";
   orderDescription += "Stop Loss: " + StringFormat("%G", tradeRequest.sl) + "\r\n";
   orderDescription += "Take Profit: " + StringFormat("%G", tradeRequest.tp) + "\r\n";
   orderDescription += "Comment: " + tradeRequest.comment + "\r\n";
   orderDescription += "Magic Number: " + StringFormat("%d", tradeRequest.magic) + "\r\n";
   orderDescription += "Order filling: " + EnumToString(tradeRequest.type_filling)+ "\r\n";
   orderDescription += "Deviation points: " + StringFormat("%G", tradeRequest.deviation) + "\r\n";
   orderDescription += "RETCODE: " + (string)(tradeResult.retcode) + "\r\n";
   orderDescription += "Runtime Code: " + (string)(GetLastError()) + "\r\n";
   orderDescription += "---";
   Print(orderDescription);
  }


Funções de Abertura de Posições


Agruparemos estas funções em duas categorias:

  1. Função OpenBuyPositions(): Esta função exportável será responsável por abrir novas posições de compra, como seu nome sugere.
  2. Função OpenSellPositions(): Esta função exportável terá a única tarefa de abrir novas posições de venda, como seu nome também sugere.

Função OpenBuyPositions()

Esta função é do tipo bool e retorna um valor true se for bem-sucedida em abrir uma nova posição de compra conforme solicitado e false se não for possível abrir uma nova posição de compra. Ela aceita seis parâmetros ou argumentos:

  1. ulong magicNumber: Usado para salvar o número mágico do ExpertAdvisor para facilitar a modificação ou encerramento de posições com filtragem fácil de posições.
  2. string symbol: Armazena o nome do símbolo ou ativo para o qual a solicitação está sendo executada.
  3. double lotSize: Armazena o volume ou quantidade da posição de compra a ser aberta.
  4. int sl: Armazena o valor do stop loss em pontos/pips da posição de compra.
  5. int tp: Armazena o valor do take profit em pontos/pips da posição de compra.
  6. string positionComment: Usado para salvar ou armazenar o comentário da posição de compra.

Vamos começar codificando a definição da função. Observe que colocamos o pós-modificador export antes da abertura da chave da função para instruir o compilador a tornar esta função exportável para uso em outros projetos MQL5 que implementam esta biblioteca.

bool OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment) export
  {
        //--- Function body
  }

Antes de tentarmos abrir um pedido, vamos verificar se o nosso Expert Advisor está mesmo autorizado a negociar. Faremos isso chamando a função TradingIsAllowed() que criamos anteriormente. A função TradingIsAllowed() verificará várias configurações e permissões para garantir que a negociação automatizada esteja ativada.

Se TradingIsAllowed() retornar false, isso significa que a negociação está desativada e nosso ExpertAdvisor não pode abrir pedidos. Neste caso, retornaremos imediatamente false</i> desta função e sairemos dela sem abrir uma nova ordem de compra.

//-- first check if the ea is allowed to trade
if(!TradingIsAllowed())
  {
   return(false); //--- algo trading is disabled, exit function
  }

O próximo passo será preparar a solicitação de pedido limpando qualquer dado residual de tentativas de negociação anteriores. Para fazer isso, usaremos a função ZeroMemory() nas duas estruturas globais de dados de negociação que criamos no início do nosso arquivo: tradeRequest e tradeResult. Estas armazenarão os detalhes do pedido de compra que queremos abrir e os resultados retornados pelo servidor de negociação, respectivamente.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

Agora, vamos definir os parâmetros para abrir uma posição de compra na variável da estrutura de dados tradeRequest</i>:

  • tradeRequest.type: Defina como ORDER_TYPE_BUY</i> para indicar uma ordem de compra.
  • tradeRequest.action: Defina como TRADE_ACTION_DEAL</i> para especificar a abertura de uma nova posição.
  • tradeRequest.magic: Vamos atribuir o magicNumber</i> fornecido como argumento aqui. Isso ajuda a identificar as ordens abertas pelo nosso ExpertAdvisor.
  • tradeRequest.symbol: Atribua o símbolo fornecido como argumento aqui, especificando o par de moedas a ser negociado.
  • tradeRequest.tp e tradeRequest.sl: Vamos definir como 0 por enquanto, pois lidaremos com os níveis de take profit (TP) e stop loss (SL) mais tarde.
  • tradeRequest.comment: Atribua o positionComment</i> fornecido como argumento aqui, que pode ser usado para adicionar um comentário de texto ao pedido.
  • tradeRequest.deviation: Defina para permitir um desvio de até duas vezes o spread atual para o símbolo sendo negociado. Isso dá à plataforma alguma flexibilidade para encontrar um preço de pedido correspondente e limita requotações de ordens.

//-- initialize the parameters to open a buy position
   tradeRequest.type = ORDER_TYPE_BUY;
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.magic = magicNumber;
   tradeRequest.symbol = symbol;
   tradeRequest.tp = 0;
   tradeRequest.sl = 0;
   tradeRequest.comment = positionComment;
   tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2;

A normalização do volume ou tamanho do lote do pedido é um passo muito importante.. Faremos isso ajustando o argumento ou parâmetro da variável lotSize</i> para garantir que ele esteja dentro do intervalo permitido para o símbolo escolhido. Veja como vamos ajustá-lo:

//-- set and moderate the lot size or volume
   lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));  //-- Verify that volume is not less than allowed minimum
   lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));  //-- Verify that volume is not more than allowed maximum
   lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); //-- Round down to nearest volume step
   tradeRequest.volume = lotSize;

Em seguida, usaremos a função ResetLastError() para limpar qualquer código de erro de tempo de execução anterior da plataforma. Isso garante que obteremos um código de erro preciso da função ErrorAdvisor() posteriormente.

//--- Limpar cache de erro para obter um código de erro de tempo de execução preciso na função ErrorAdvisor    ResetLastError();

Agora, entraremos em um loop que pode tentar abrir o pedido até 100 vezes (com um máximo de 101 iterações). Este loop atua como uma medida de segurança caso a ordem falhe em abrir na primeira tentativa devido a flutuações temporárias do mercado ou outros motivos.

for(int loop = 0; loop <= 100; loop++) //-- try opening the order untill it is successful (100 max tries)
   {
    //--- Place the order request code to open a new buy position
   }

A primeira tarefa no loop for será atualizar o preço de abertura do pedido em cada iteração, caso haja uma requotação do preço do pedido. Usaremos SymbolInfoDouble(symbol, SYMBOL_ASK)</i> para obter o preço de venda atual do símbolo e atribuí-lo a tradeRequest.price</i>. Isso garante que nossa solicitação de pedido reflita o preço de mercado mais recente.

//--- update order opening price on each iteration
   tradeRequest.price = SymbolInfoDouble(symbol, SYMBOL_ASK);

Em seguida, atualizaremos os valores de take profit e stop loss em cada iteração para corresponder ao preço de entrada atualizado, ao mesmo tempo que normalizamos seus valores para garantir que atendam aos requisitos de precisão exigidos e, em seguida, atribuí-los à estrutura de dados tradeRequest.tp</i> e tradeRequest.sl</i>.

//-- set the take profit and stop loss on each iteration
   if(tp > 0)
     {
      tradeRequest.tp = NormalizeDouble(tradeRequest.price + (tp * _Point), _Digits);
     }
   if(sl > 0)
     {
      tradeRequest.sl = NormalizeDouble(tradeRequest.price - (sl * _Point), _Digits);
     }

Em seguida, enviaremos o pedido ao servidor de negociação para execução. Para essa tarefa, usaremos a função OrderSend(), passando a tradeRequest</i> preparada e uma variável tradeResult</i> vazia como argumentos ou parâmetros. Essa função tenta abrir o pedido com base nas especificações armazenadas em tradeRequest</i>. Os resultados, incluindo códigos de sucesso ou erro, serão armazenados na variável tradeResult</i> após a conclusão de sua execução.

A declaração if</i> que colocamos para a função OrderSend()</i> nos permitirá verificar e confirmar se a solicitação de pedido foi bem-sucedida ou não. Se OrderSend()</i> retornar true</i>, isso significa que a solicitação de pedido foi enviada com sucesso, e se retornar false</i>, significa que a solicitação falhou.

Em seguida, chamamos a função PrintOrderDetails()</i> que codificamos anteriormente com a mensagem "Enviado com sucesso</i>" para registrar essa informação no log do Expert Advisor.

Também verifique o tradeResult.retcode</i> para confirmar a execução bem-sucedida do pedido. Retorne true</i> da função OpenBuyPosition()</i> para indicar o sucesso e use break</i> para sair do loop completamente. Se OrderSend()</i> retornar false (o que significa que a solicitação de pedido falhou), isso indica que ocorreu um problema. Chamaremos PrintOrderDetails()</i> com a mensagem "Falha ao enviar</i>" para registrar essa informação. Também imprimiremos uma mensagem de erro para destacar os diferentes códigos de erro encontrados e retornaremos false</i> da função OpenBuyPosition()</i> para indicar falha, e usaremos break</i> para sair do loop.

//--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Print the order details
         PrintOrderDetails("Sent OK");

         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " BUY POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
            PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
            Print("_______________________________________________________________________________________");
            return(true); //-- exit the function
            break; //--- success - order placed ok. exit the for loop
           }
        }
      else //-- Order request failed
        {
         //-- Print the order details
         PrintOrderDetails("Sending Failed");

         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, ": ", symbol, " ERROR opening a BUY POSITION at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume);
            Print("_______________________________________________________________________________________");
            return(false); //-- exit the function
            break; //-- exit the for loop
           }
        }

Aqui está a função OpenBuyPosition() com todos os segmentos de código e sua sequência correta:

//-------------------------------------------------------------------+
// OpenBuyPosition(): Function to open a new buy entry order.        |
//+------------------------------------------------------------------+
bool OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment) export
  {
//-- first check if the ea is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to open a buy position
   tradeRequest.type = ORDER_TYPE_BUY;
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.magic = magicNumber;
   tradeRequest.symbol = symbol;
   tradeRequest.tp = 0;
   tradeRequest.sl = 0;
   tradeRequest.comment = positionComment;
   tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2;

//-- set and moderate the lot size or volume
   lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));  //-- Verify that volume is not less than allowed minimum
   lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));  //-- Verify that volume is not more than allowed maximum
   lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); //-- Round down to nearest volume step
   tradeRequest.volume = lotSize;

//--- Reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function
   ResetLastError();

   for(int loop = 0; loop <= 100; loop++) //-- try opening the order untill it is successful (100 max tries)
     {
      //--- update order opening price on each iteration
      tradeRequest.price = SymbolInfoDouble(symbol, SYMBOL_ASK);

      //-- set the take profit and stop loss on each iteration
      if(tp > 0)
        {
         tradeRequest.tp = NormalizeDouble(tradeRequest.price + (tp * _Point), _Digits);
        }
      if(sl > 0)
        {
         tradeRequest.sl = NormalizeDouble(tradeRequest.price - (sl * _Point), _Digits);
        }

      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Print the order details
         PrintOrderDetails("Sent OK");

         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " BUY POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
            PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
            Print("_______________________________________________________________________________________");
            return(true); //-- exit the function
            break; //--- success - order placed ok. exit the for loop
           }
        }
      else //-- Order request failed
        {
         //-- Print the order details
         PrintOrderDetails("Sending Failed");

         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, ": ", symbol, " ERROR opening a BUY POSITION at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume);
            Print("_______________________________________________________________________________________");
            return(false); //-- exit the function
            break; //-- exit the for loop
           }
        }
     }
   return(false);
  }


Função OpenSellPositions()

Esta função é muito semelhante à função OpenBuyPosition() que terminamos de codificar acima e segue os mesmos procedimentos, com algumas diferenças, como o tipo de ordem sendo processada. A função OpenSellPosition() é projetada para abrir uma nova posição de venda. Ela inclui um loop que faz várias tentativas em caso de falha, aumentando significativamente sua taxa de sucesso, desde que sejam fornecidos parâmetros válidos para a solicitação de negociação. Aqui está o código da função OpenSellPosition():

//-------------------------------------------------------------------+
// OpenSellPosition(): Function to open a new sell entry order.      |
//+------------------------------------------------------------------+
bool OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment) export
  {
//-- first check if the ea is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to open a sell position
   tradeRequest.type = ORDER_TYPE_SELL;
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.magic = magicNumber;
   tradeRequest.symbol = symbol;
   tradeRequest.tp = 0;
   tradeRequest.sl = 0;
   tradeRequest.comment = positionComment;
   tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2;

//-- set and moderate the lot size or volume
   lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));  //-- Verify that volume is not less than allowed minimum
   lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));  //-- Verify that volume is not more than allowed maximum
   lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); //-- Round down to nearest volume step
   tradeRequest.volume = lotSize;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try opening the order (101 max) times untill it is successful
     {
      //--- update order opening price on each iteration
      tradeRequest.price = SymbolInfoDouble(symbol, SYMBOL_BID);

      //-- set the take profit and stop loss on each iteration
      if(tp > 0)
        {
         tradeRequest.tp = NormalizeDouble(tradeRequest.price - (tp * _Point), _Digits);
        }
      if(sl > 0)
        {
         tradeRequest.sl = NormalizeDouble(tradeRequest.price + (sl * _Point), _Digits);
        }

      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Print the order details
         PrintOrderDetails("Sent OK");

         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print("CONFIRMED: Successfully openend a ", symbol, " SELL POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
            PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
            Print("_______________________________________________________________________________________");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- Print the order details
         PrintOrderDetails("Sending Failed");

         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped())
           {
            Print(symbol, " ERROR opening a SELL POSITION at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume);
            Print("_______________________________________________________________________________________");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }


Função de Modificação de Stop Loss e Take Profit da Posição


Nossa próxima função na biblioteca será chamada SetSlTpByTicket() e será responsável por modificar os níveis de Stop Loss (SL) e Take Profit (TP) para uma posição aberta existente usando o ticket da posição como mecanismo de filtragem. Ela recebe o número do ticket da posição, o SL desejado em pips (pontos) e o TP desejado em pips (pontos) como argumentos e tenta atualizar o SL e o TP da posição no servidor de negociação. A função retornará um valor booleano (true ou false). Se os níveis de Stop Loss e Take Profit forem modificados com sucesso para a posição, ela retornará true, e se não for possível definir ou modificar com sucesso os níveis de Stop Loss ou Take Profit, ela retornará false.

Aqui está uma descrição dos argumentos ou parâmetros da função SetSlTpByTicket():

  1. ulong positionTicket: Este é um identificador único para a posição que iremos modificar.
  2. int sl: Este é o nível desejado de Stop Loss em pips (pontos) a partir do preço de abertura.
  3. int tp: Este é o nível desejado de Take Profit em pips (pontos) a partir do preço de abertura.

Lembre-se de usar o pós-modificador export na definição da função para torná-la acessível externamente para nossa biblioteca.

bool SetSlTpByTicket(ulong positionTicket, int sl, int tp) export
  {
//-- Function body
  }

Assim como nas outras funções acima, primeiro verificaremos se a negociação para o nosso ExpertAdvisor é permitida usando a função TradingIsAllowed(). Se a negociação estiver desativada, a função sai e retorna false.

//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

Antes de selecionar a posição especificada usando o argumento positionTicket, primeiro redefiniremos a variável de sistema de código de erro de tempo de execução para obter uma resposta de erro acionável precisa para a função ErrorAdvisor() posteriormente. Se a seleção for bem-sucedida, uma mensagem será impressa indicando que a posição foi selecionada, nos dando o sinal verde para acessar as propriedades da posição. Se a seleção falhar, uma mensagem de erro será impressa juntamente com o código de erro recuperado usando GetLastError(). A função então sai retornando false.

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to set SLTP.");
     }
   else
     {
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

Depois de selecionar a posição, precisamos reunir e salvar alguns detalhes sobre ela. Usaremos essas informações para cálculos e referência posterior.

//-- create variables to store the calculated tp and sl prices to send to the trade server
   double tpPrice = 0.0, slPrice = 0.0;
   double newTpPrice = 0.0, newSlPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

Também precisaremos de algumas informações específicas do símbolo:

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

Agora, vamos calcular os novos preços de Stop Loss (SL) e Take Profit (TP) com base no preço de abertura da posição, SL/TP desejado em pips (pontos) e o tipo de posição (Compra ou Venda). Armazenaremos esses cálculos iniciais antes de validá-los:

//--Save the non-validated tp and sl prices
   if(positionType == POSITION_TYPE_BUY) //-- Calculate and store the non-validated sl and tp prices
     {
      newSlPrice = entryPrice - (sl * symbolPoint);
      newTpPrice = entryPrice + (tp * symbolPoint);
     }
   else  //-- SELL POSITION
     {
      newSlPrice = entryPrice + (sl * symbolPoint);
      newTpPrice = entryPrice - (tp * symbolPoint);
     }

Em seguida, imprimiremos um resumo dos detalhes da posição que coletamos anteriormente:

//-- Print position properties before modification
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " SLTP Modification Details" +
   " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Proposed SL: " + (string)newSlPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New Proposed TP: " + (string)newTpPrice + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

Como os valores fornecidos pelo usuário da biblioteca para SL e TP podem não ser diretamente utilizáveis pela função OrderSend(). Precisamos fazer uma validação simples dos seus valores antes de prosseguir:

//-- validate the sl and tp to a proper double that can be used in the OrderSend() function
   if(sl == 0)
     {
      slPrice = 0.0;
     }
   if(tp == 0)
     {
      tpPrice = 0.0;
     }

Agora, precisamos realizar uma validação mais complexa com base nos detalhes do símbolo que salvamos anteriormente. Agruparemos a lógica de validação em dois grupos, um para as posições de Compra e outro para as posições de Venda. A validação do SL e TP será baseada no preço atual do símbolo, nas restrições mínimas de nível de stop do símbolo e no spread do símbolo.

Se um preço de TP ou SL especificado for inválido e estiver fora do intervalo exigido, o preço original de TP ou SL será mantido, e uma mensagem será impressa explicando por que a modificação falhou. Depois de terminar de validar os valores de SL e TP, imprimiremos outro resumo para registrar os valores confirmados e verificados para referência:

//--- Check if the sl and tp are valid in relation to the current price and set the tpPrice
   if(positionType == POSITION_TYPE_BUY)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice + (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice - (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP is valid
         tp > 0 &&
         (
            newTpPrice <= entryPrice + (spread * symbolPoint) ||
            newTpPrice <= positionPriceCurrent ||
            (
               newTpPrice - entryPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent > entryPrice && newTpPrice - positionPriceCurrent < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice >= positionPriceCurrent ||
            entryPrice - newSlPrice < symbolStopLevel * symbolPoint ||
            positionPriceCurrent - newSlPrice < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }
   if(positionType == POSITION_TYPE_SELL)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice - (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice + (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP price is valid
         tp > 0 &&
         (
            newTpPrice >= entryPrice - (spread * symbolPoint) ||
            newTpPrice >= positionPriceCurrent ||
            (
               entryPrice - newTpPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent < entryPrice && positionPriceCurrent - newTpPrice < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice <= positionPriceCurrent ||
            newSlPrice - entryPrice < symbolStopLevel * symbolPoint ||
            newSlPrice - positionPriceCurrent < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }

//-- Print verified position properties before modification
   positionProperties = "---\r\n";
   positionProperties += "--> Validated and Confirmed SL and TP: <--\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + ", Price Current: " + StringFormat("%G", positionPriceCurrent) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New TP: " + (string)tpPrice + "\r\n";
   Print(positionProperties);

Agora que temos os valores de SL e TP validados, é hora de enviar uma solicitação ao servidor de negociação para modificá-los. Usamos a função ZeroMemory() para limpar as estruturas tradeRequest</i> e tradeResult</i>, garantindo que não contenham dados residuais de operações anteriores. Em seguida, inicializamos a estrutura tradeRequest</i> com as seguintes informações:

  • action: Definido como TRADE_ACTION_SLTP</i> para indicar a modificação de Stop Loss e Take Profit.
  • position: Definido como o positionTicket</i> para especificar a posição em que estamos trabalhando.
  • symbol: Definido como o positionSymbol</i> para identificar o símbolo desta posição.
  • sl: Definido como o slPrice</i>, que contém o valor validado do Stop Loss.
  • tp: Definido como o tpPrice</i>, que contém o valor validado do Take Profit.

Em seguida, chamamos a função ResetLastError() para limpar qualquer código de erro anterior armazenado internamente. Isso garante que obteremos códigos de erro precisos durante o processo de envio do pedido.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

Estamos prontos para enviar o pedido ao servidor de negociação. No entanto, a experiência me ensinou que a execução do pedido pode falhar ocasionalmente devido a problemas temporários de rede ou sobrecarga do servidor. Isso significa que precisamos encontrar uma maneira inteligente de lidar com o envio do pedido com tentativas de reenvio. Para resolver isso, usaremos um loop for que itera até 101 vezes (loop <= 100). Esse mecanismo de reenvio ajuda a lidar com possíveis erros temporários durante a execução do pedido.

Dentro do loop for, usamos OrderSend() para enviar a solicitação de pedido contida em tradeRequest</i> e armazenar o resultado em tradeResult</i>. Se OrderSend() retornar true, isso indica que os preços de SL e TP foram alterados com sucesso e a solicitação de pedido foi concluída sem problemas.

Também faremos uma confirmação final verificando o tradeResult.retcode</i> para códigos específicos (10008 ou 10009) que indicam a modificação bem-sucedida de SL/TP para esta posição. Se os códigos corresponderem, imprimimos uma mensagem de confirmação com detalhes como o ticket da posição, o símbolo e os códigos de retorno. Em seguida, usamos return(true) para sair da função com sucesso. A instrução break sai do loop apenas para garantir completamente que saímos do loop for para evitar iterações desnecessárias. Se OrderSend() retornar false ou o retcode</i> não corresponder aos códigos de sucesso, isso indica um erro.

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = tpPrice;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }

Aqui está a função SetSlTpByTicket() com todos os segmentos de código e sua sequência correta. Certifique-se de que sua função tenha todos os componentes do código abaixo:

bool SetSlTpByTicket(ulong positionTicket, int sl, int tp) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to set SLTP.");
     }
   else
     {
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

//-- create variables to store the calculated tp and sl prices to send to the trade server
   double tpPrice = 0.0, slPrice = 0.0;
   double newTpPrice = 0.0, newSlPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

//--Save the non-validated tp and sl prices
   if(positionType == POSITION_TYPE_BUY) //-- Calculate and store the non-validated sl and tp prices
     {
      newSlPrice = entryPrice - (sl * symbolPoint);
      newTpPrice = entryPrice + (tp * symbolPoint);
     }
   else  //-- SELL POSITION
     {
      newSlPrice = entryPrice + (sl * symbolPoint);
      newTpPrice = entryPrice - (tp * symbolPoint);
     }

//-- Print position properties before modification
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " SLTP Modification Details" +
   " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Proposed SL: " + (string)newSlPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New Proposed TP: " + (string)newTpPrice + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

//-- validate the sl and tp to a proper double that can be used in the OrderSend() function
   if(sl == 0)
     {
      slPrice = 0.0;
     }
   if(tp == 0)
     {
      tpPrice = 0.0;
     }

//--- Check if the sl and tp are valid in relation to the current price and set the tpPrice
   if(positionType == POSITION_TYPE_BUY)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice + (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice - (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP is valid
         tp > 0 &&
         (
            newTpPrice <= entryPrice + (spread * symbolPoint) ||
            newTpPrice <= positionPriceCurrent ||
            (
               newTpPrice - entryPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent > entryPrice && newTpPrice - positionPriceCurrent < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice >= positionPriceCurrent ||
            entryPrice - newSlPrice < symbolStopLevel * symbolPoint ||
            positionPriceCurrent - newSlPrice < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }
   if(positionType == POSITION_TYPE_SELL)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice - (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice + (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP price is valid
         tp > 0 &&
         (
            newTpPrice >= entryPrice - (spread * symbolPoint) ||
            newTpPrice >= positionPriceCurrent ||
            (
               entryPrice - newTpPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent < entryPrice && positionPriceCurrent - newTpPrice < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice <= positionPriceCurrent ||
            newSlPrice - entryPrice < symbolStopLevel * symbolPoint ||
            newSlPrice - positionPriceCurrent < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }

//-- Print verified position properties before modification
   positionProperties = "---\r\n";
   positionProperties += "--> Validated and Confirmed SL and TP: <--\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + ", Price Current: " + StringFormat("%G", positionPriceCurrent) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New TP: " + (string)tpPrice + "\r\n";
   Print(positionProperties);

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = tpPrice;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }


Função de Fechamento de Posição


Esta função será chamada ClosePositionByTicket() e usará uma abordagem estruturada para garantir que possamos fechar efetivamente as posições com base em seus números de ticket. Ela recebe o número do ticket da posição como argumento. Ela verificará se a negociação é permitida, selecionará a posição usando o ticket fornecido, recuperará e imprimirá suas propriedades, preparará uma solicitação de negociação e tentará fechar a posição enquanto lida com quaisquer erros que ocorram.

Primeiro, definimos a função e especificamos que ela retornará um valor booleano</i> (true</i> ou false</i>) e aceitará um parâmetro como argumento.

bool ClosePositionByTicket(ulong positionTicket) export
  {
//--- Function body
  }

Em seguida, verificaremos se o Expert Advisor está autorizado a negociar.

//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

Em seguida, redefinimos quaisquer erros anteriores usando a função ResetLastError() e então selecionamos a posição usando o número do ticket fornecido. Se a posição for selecionada, imprimimos uma mensagem confirmando a seleção; se a seleção falhar, imprimimos uma mensagem de erro e saímos da função retornando false</i>.

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to be closed.");
     }
   else
     {
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

Uma vez que a posição for selecionada com sucesso, salvamos suas propriedades e as imprimimos.

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double positionVolume = PositionGetDouble(POSITION_VOLUME);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Print position properties before closing it
   string positionProperties;
   positionProperties += "-- "  + positionSymbol + " " + EnumToString(positionType) + " Details" +
   " -------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", PositionGetDouble(POSITION_VOLUME)) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", PositionGetDouble(POSITION_PRICE_OPEN)) + "\r\n";
   positionProperties += "SL: " + StringFormat("%G", PositionGetDouble(POSITION_SL)) + "\r\n";
   positionProperties += "TP: " + StringFormat("%G", PositionGetDouble(POSITION_TP)) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "_______________________________________________________________________________________";
   Print(positionProperties);

Em seguida, redefinimos os valores das estruturas de dados tradeRequest</i> e tradeResult</i> usando a função ZeroMemory()</i> para limpar quaisquer dados anteriores nelas. Em seguida, inicializamos os parâmetros da solicitação de negociação para fechar a posição definindo a ação de negociação como TRADE_ACTION_DEAL</i> para indicar uma operação de encerramento de negociação, o ticket da posição, o símbolo, o volume e o desvio de preço.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the trade reqiest parameters to close the position
   tradeRequest.action = TRADE_ACTION_DEAL; //-- Trade operation type for closing a position
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.volume = positionVolume;
   tradeRequest.deviation = SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD) * 2;

Agora, precisamos determinar o preço de fechamento da posição e o tipo de ordem com base em se a posição é de compra ou venda.

//--- Set the price and order type of the position being closed
   if(positionType == POSITION_TYPE_BUY)
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_BID);
      tradeRequest.type = ORDER_TYPE_SELL;
     }
   else//--- For sell type positions
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_ASK);
      tradeRequest.type = ORDER_TYPE_BUY;
     }

Finalmente, redefinimos quaisquer erros anteriores usando a função ResetLastError() e então tentamos fechar a posição enviando a solicitação de negociação. Usaremos um loop for para tentar enviar a solicitação de fechamento da posição várias vezes para garantir que a posição seja fechada mesmo com uma conexão de internet fraca ou quando ocorrem erros não críticos. Se o pedido for enviado e executado com sucesso (códigos de retorno 10008 ou 10009), imprimimos uma mensagem de sucesso e retornamos true</i>. Se o pedido falhar, chamamos a função ErrorAdvisor() para tratar o erro. Se a função ErrorAdvisor() indicar um erro crítico ou se o ExpertAdvisor for interrompido, imprimimos uma mensagem de erro e retornamos false</i> para indicar que o fechamento da posição falhou.

ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try closing the position 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("Successfully closed position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- position closing request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("ERROR closing position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }

Certifique-se de organizar todos os segmentos de código acima na seguinte sequência. Aqui estão todos os segmentos de código da função ClosePositionByTicket() juntos em sua ordem apropriada:

bool ClosePositionByTicket(ulong positionTicket) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to be closed.");
     }
   else
     {
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double positionVolume = PositionGetDouble(POSITION_VOLUME);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Print position properties before closing it
   string positionProperties;
   positionProperties += "-- "  + positionSymbol + " " + EnumToString(positionType) + " Details" +
   " -------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", PositionGetDouble(POSITION_VOLUME)) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", PositionGetDouble(POSITION_PRICE_OPEN)) + "\r\n";
   positionProperties += "SL: " + StringFormat("%G", PositionGetDouble(POSITION_SL)) + "\r\n";
   positionProperties += "TP: " + StringFormat("%G", PositionGetDouble(POSITION_TP)) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "_______________________________________________________________________________________";
   Print(positionProperties);

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the trade reqiest parameters to close the position
   tradeRequest.action = TRADE_ACTION_DEAL; //-- Trade operation type for closing a position
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.volume = positionVolume;
   tradeRequest.deviation = SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD) * 2;

//--- Set the price and order type of the position being closed
   if(positionType == POSITION_TYPE_BUY)
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_BID);
      tradeRequest.type = ORDER_TYPE_SELL;
     }
   else//--- For sell type positions
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_ASK);
      tradeRequest.type = ORDER_TYPE_BUY;
     }

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try closing the position 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("Successfully closed position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- position closing request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("ERROR closing position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }

Salve e compile o arquivo de código-fonte PositionsManager.mq5</i> da biblioteca, e você notará que um novo arquivo de biblioteca PositionsManager.ex5</i> será gerado na pasta Libraries\Toolkit onde salvamos nossa biblioteca.


Conclusão

Agora, você adquiriu uma compreensão sólida sobre as bibliotecas MQL5 ex5</i> e seu processo de criação. No próximo artigo, ampliaremos nossa biblioteca de gerenciamento de posições com funcionalidades adicionais para várias tarefas de gerenciamento de posições e, em seguida, demonstraremos como implementar bibliotecas ex5</i> em qualquer projeto MQL5 com diferentes exemplos práticos. Encontre o arquivo de código-fonte PositionsManager.mq5</i> da biblioteca, que inclui todas as funções que criamos acima, anexado ao final deste artigo.


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/14822

Arquivos anexados |
Desenvolvendo uma estratégia Martingale de Recuperação de Zona em MQL5 Desenvolvendo uma estratégia Martingale de Recuperação de Zona em MQL5
O artigo discute, de forma detalhada, os passos que precisam ser implementados para a criação de um advisor especializado baseado no algoritmo de negociação de Recuperação de Zona. Isso ajuda a automatizar o sistema, economizando tempo para os negociadores algorítmicos.
Construindo um Modelo de Restrição de Tendência de Candlestick (Parte 4): Personalizando o Estilo de Exibição para Cada Onda de Tendência Construindo um Modelo de Restrição de Tendência de Candlestick (Parte 4): Personalizando o Estilo de Exibição para Cada Onda de Tendência
Neste artigo, exploraremos as capacidades da poderosa linguagem MQL5 na criação de vários estilos de indicadores no MetaTrader 5. Também analisaremos os scripts e como eles podem ser utilizados em nosso modelo.
Criação de Previsões de Séries Temporais Usando Redes Neurais LSTM: Normalizando Preço e Tokenizando o Tempo Criação de Previsões de Séries Temporais Usando Redes Neurais LSTM: Normalizando Preço e Tokenizando o Tempo
Este artigo descreve uma estratégia simples para normalizar os dados de mercado usando o intervalo diário e treinar uma rede neural para aprimorar as previsões de mercado. Os modelos desenvolvidos podem ser utilizados em conjunto com estruturas de análise técnica existentes ou de forma independente para auxiliar na previsão da direção geral do mercado. A estrutura delineada neste artigo pode ser ainda mais refinada por qualquer analista técnico para desenvolver modelos adequados para estratégias de negociação manuais e automatizadas.
Um Guia Passo a Passo sobre a Estratégia de Quebra de Estrutura (BoS) Um Guia Passo a Passo sobre a Estratégia de Quebra de Estrutura (BoS)
Um guia abrangente para desenvolver um algoritmo de negociação automatizado baseado na estratégia de Quebra de Estrutura (BoS). Informações detalhadas sobre todos os aspectos da criação de um consultor em MQL5 e testando-o no MetaTrader 5 — desde a análise de suporte e resistência de preços até a gestão de riscos.