MQL5 Trading Toolkit (Parte 1): Desenvolvendo uma Biblioteca EX5 para Gerenciamento de Posições
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'.
Passo 2: Selecione a opção 'Biblioteca' e clique em 'Próximo'.
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.
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.
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:
- callingFunc (string): Este parâmetro armazena o nome ou identificador da função que chama ErrorAdvisor().
- symbol (string): Este parâmetro armazena o nome do símbolo do ativo em questão.
- 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:
- 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.
- 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:
- Função OpenBuyPositions(): Esta função exportável será responsável por abrir novas posições de compra, como seu nome sugere.
- 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:
- 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.
- string symbol: Armazena o nome do símbolo ou ativo para o qual a solicitação está sendo executada.
- double lotSize: Armazena o volume ou quantidade da posição de compra a ser aberta.
- int sl: Armazena o valor do stop loss em pontos/pips da posição de compra.
- int tp: Armazena o valor do take profit em pontos/pips da posição de compra.
- 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():
- ulong positionTicket: Este é um identificador único para a posição que iremos modificar.
- int sl: Este é o nível desejado de Stop Loss em pips (pontos) a partir do preço de abertura.
- 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
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/14822
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso