
Criando um painel de administração de trading em MQL5 (Parte VI): Painel de controle de trading (II)
Conteúdo
- Introdução
- Melhoria do painel de controle de trading
- Configuração do layout do painel de controle de trading
- Integração de funções auxiliares e novo botão
- Implementação dos manipuladores de botões
- Testes
-
Introdução
Como já passamos pelos testes de segurança e utilizamos o painel administrativo principal, onde temos acesso às funções essenciais, nosso objetivo hoje é dar continuidade à implementação em MQL5, ao mesmo tempo em que aprimoramos o desempenho do nosso painel multifuncional, em um projeto em constante desenvolvimento. Nossa publicação anterior serviu como introdução a diversas funções do painel, apresentando os recursos mais recentes sem detalhar a funcionalidade de cada botão do painel de controle de trading. Garantimos que cada botão do painel de controle de trading responderá corretamente quando pressionado.
Além disso, gostaria de levantar a seguinte questão: estamos limitados pelo layout atual dos painéis? Para responder a essa pergunta, vamos analisar ideias alternativas de layout que nos ajudem a aproveitar melhor os recursos da classe de diálogo da linguagem voltada ao desenvolvimento de algoritmos de trading no MQL5. Abaixo, apresento uma imagem que demonstra a possibilidade de ajustar a escala vertical do gráfico. Considero esse recurso útil para maximizar o espaço disponível para o nosso painel. Acredito também que seria vantajoso exibir os dois painéis ao mesmo tempo, sem comprometer a clareza da visualização do gráfico. Para implementar esse recurso, adicionaremos um botão ao painel administrativo principal, que permitirá que os usuários visualizem todos os painéis simultaneamente.
A escala vertical do gráfico do MetaTrader 5 cria espaços e torna a visualização do alcance das velas mais clara
Antes de prosseguirmos, quero dar a todos a oportunidade de revisitar os diferentes elementos do nosso painel multifuncional. Isso nos ajudará a esclarecer alguns pontos não mencionados acima e que surgiram durante o desenvolvimento desta série de artigos.- Para aqueles que enfrentam erros de compilação, isso provavelmente está relacionado à execução do código com os arquivos de biblioteca padrão, que não foram estendidos. Para resolver esse problema, é necessário sobrescrever o conteúdo do diretório MQL5/Include/controls com os arquivos da pasta Extended Header, apresentada em artigos anteriores.
- Outra tarefa importante é garantir que o identificador do chat e o token do bot estejam atualizados e correspondam aos seus dados, para que o código de confirmação seja enviado à sua conta.
Para resolver essa segunda tarefa, é preciso acessar o aplicativo Telegram e criar dois bots: um será usado para autenticação durante a inicialização do aplicativo e o outro, para comunicação em chats individuais, grupos e canais dentro do painel de comunicações do EA.
Em seguida, insira seu identificador real de chat e o token do bot. Verifique se o identificador do chat não pertence a um grupo ou canal, pois, nesse caso, o código ficaria acessível a vários usuários; ele deve ser estritamente pessoal. Para confirmar que você tem o identificador correto, inicie uma conversa com o bot e consulte a API para verificar o ID do chat. Lembre-se de que esses valores são fixados no código-fonte antes da compilação e não são os mesmos inseridos ao iniciar o aplicativo.
Já discutimos isso anteriormente, e os passos necessários são simples de executar.
PASSO 1:
Certifique-se de que você é um usuário registrado no Telegram. Caso não seja, baixe o aplicativo Telegram para seu smartphone ou computador, e siga as instruções no aplicativo para se registrar.
PASSO 2:
Inicie uma conversa com o BotFather para criar dois bots: um para autenticação de dois fatores (2FA) e outro para comunicação. Para cada bot, você receberá um token exclusivo que pode ser usado para acessar a API do Telegram e obter os identificadores de chat. Cada bot tem o seu próprio token e você pode nomeá-los como quiser, o que ajudará a identificar facilmente a finalidade de cada um.
PASSO 3:
Acesse a API pelo seguinte link: https://api.telegram.org/botReplaceMeWithTheTokenFromTheBotFather/getUpdates.
Telegram API no браузере Chrome
PASSO 4:
Inicie uma conversa com o bot responsável por entregar o código 2FA. Você também pode adicionar o bot a qualquer grupo ou canal em que pretende compartilhar informações de trading e atribuí-lo como administrador para que funcione corretamente. Após concluir a configuração, inicie a interação nos grupos para obter o identificador do chat. Em seguida, atualize a página do navegador na aba da API para visualizar as informações atualizadas.
Inicie um chat no Telegram: comece uma conversa com o bot (neste caso, eu nomeei meu bot como Admin Panel)
API do Telegram: Aqui podemos obter o identificador do chat para 2FA
Na imagem acima, iniciei um chat pessoal, principalmente para obter meu identificador de chat para entrega do 2FA. Você também pode adicionar esse mesmo bot a grupos e canais; quando as conversas começam nesses locais, os identificadores de chat são preenchidos automaticamente na API, como mostrado acima, e geralmente aparecem logo abaixo da primeira conversa. Por exemplo, adicionei o bot a um dos meus canais educacionais. A mensagem da API que recebi está mostrada na imagem abaixo.
Resumindo, é possível usar o mesmo bot em vários canais, sendo que cada um terá seu próprio identificador exclusivo de chat. Se preferir, você pode usar bots diferentes para cada canal.
Adicionando o bot como administrador em um canal do Telegram
Por fim, abaixo está exibido o JSON da API com o identificador de chat do canal.
JSON gerado pelo chat do canal
Nesse trecho de código, você deve inserir as credenciais do bot para entrega do código 2FA:// Constants for 2FA. Here put the chat ID dedicated for verification code delivery. It must be kept secure always. const string Hardcoded2FAChatId = "REPLACE WITH YOUR CHAT ID"; const string Hardcoded2FABotToken = "REPLACE WITH YOU BOT TOKEN"; //Obtain your bot token from a telegram bot father. //If you don't already have a bot you can start the chat with the bot father to create one.
A seguir, apresento uma imagem ilustrando onde inserir as credenciais do Telegram para as comunicações de trading durante a inicialização do aplicativo. Se necessário, você também pode configurar mensagens rápidas:
Parâmetros de inicialização
Note que o trecho de código e a imagem acima exibem dois identificadores de chat diferentes, como já mencionado. Um é usado para a entrega do código de confirmação, e o outro para o envio de mensagens administrativas em chats pessoais ou grupos no Telegram. Cada usuário, grupo e canal conectados ao bot possuem um identificador de chat único.
Durante todas as explicações, usamos o mesmo PIN — 2024. Você pode configurá-lo no código-fonte para uso próprio. No caso do 2FA, o algoritmo do gerador de códigos de seis dígitos cuidará disso automaticamente. Você só precisa garantir que possui um identificador exclusivo de chat para que o código seja entregue de forma segura.
Resumindo, vamos nos concentrar nos manipuladores de botões do painel de controle de trading e, em seguida, melhorar o layout em termos de posição e tamanho no gráfico.
Melhoria do painel de controle de trading
Primeiramente, quero que você entenda a diferença entre os botões do painel de comunicações (Communication Panel) e do painel de controle de trading (Trading Management). Acredito que você já esteja familiarizado com a funcionalidade de operações em massa, disponível tanto na versão desktop quanto na versão mobile do MetaTrader 5.
As funções do painel podem parecer confusas devido à semelhança entre nomes ou descrições. Por exemplo, os botões do painel de comunicações são destinados exclusivamente à troca de mensagens, enquanto os botões do painel de controle de trading são programados especificamente para lidar com operações de negociação.
Você pode implementar manipuladores de operações de trading nos mesmos botões usados no painel de comunicações, permitindo que ambas as tarefas sejam executadas simultaneamente ao pressionar um único botão. No entanto, existem algumas razões pelas quais os botões de comunicação não devem coincidir com os botões de controle de trading. Uma das principais razões é que o administrador pode precisar gerenciar negociações que não necessariamente devem ser transmitidas.
As principais diferenças estão ilustradas nas imagens abaixo.
Painel de comunicações: botões para troca de mensagens
Quase todos os botões aqui são destinados à comunicação, com exceção dos botões de navegação. Além disso, as mensagens rápidas que eles enviam podem ser configuradas durante a inicialização. O botão de mensagem rápida Close All (fechar tudo) serve apenas para enviar uma mensagem ao cliente especificado no Telegram e não executa nenhuma outra ação.
Painel de controle de trading: (Estes botões devem executar operações de trading ao serem pressionados)
Agora precisamos trabalhar de forma mais aprofundada no painel de controle de trading. Atualmente, nosso painel possui um número limitado de botões, e nosso objetivo é aprimorá-lo para maximizar o espaço de visualização dos gráficos. Novos botões são necessários para algumas operações críticas de trading, como Close All Orders (fechar todas as ordens).
Configuração do layout do controle de trading
O novo layout da interface tem como objetivo criar mais espaço para a análise do gráfico, ao mesmo tempo em que permite a execução das operações administrativas usuais dentro desta série de desenvolvimentos. Essa abordagem utiliza as coordenadas da posição dos painéis em relação ao gráfico do MetaTrader 5.
Atualmente, o painel administrativo principal é muito grande para o conteúdo que exibe, e também o ajustaremos. Para tornar o processo mais claro, vamos dividi-lo em três etapas.
Configuração do painel de controle de trading
Durante a inicialização, nosso painel é criado utilizando o seguinte trecho de código:
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 500, 500))
{
Print("Failed to create Communictions panel dialog");
return INIT_FAILED;
}
No código acima:
x1 = 30
x2 = 500
Portanto, largura = X2 - X1 = 500 - 30 = 470.
Se aumentarmos o valor de X2, a nova largura crescerá de acordo com o design. Por exemplo, aumentarei em 50% da largura original. Veja o trecho de código abaixo.
// Let's increase our panel width by 50% of the former panel and is likely to be 250px but we will add an extra 30px to cover for the initial px on x1 co-ordinate.
// For the y co-ordinate we will reduce so much to 150px
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 780, 150))
{
Print("Failed to create Communictions panel dialog");
return INIT_FAILED;
}
O resultado após compilação e testes é o seguinte:
Novo layout do painel de trading
A recente modificação fez com que alguns botões fossem posicionados fora da ordem desejada, aparecendo sobre o gráfico em vez de permanecerem dentro do painel. Para corrigir esse problema, acessamos a função CreateTradeManagementControls() e ajustamos as coordenadas dos botões, conforme mostrado no trecho de código abaixo.
// Create the Trade Management Panel controls
// Here we adjusted our button coordinates to fit well in the new Trade Management Panel
bool CreateTradeManagementControls()
{
long chart_id = ChartID();
// Buy Button
if (!buyButton.Create(chart_id, "BuyButton", 0, 130, 5, 210, 40))
{
Print("Failed to create Buy button");
return false;
}
buyButton.Text("Buy");
tradeManagementPanel.Add(buyButton);
// Sell Button
if (!sellButton.Create(chart_id, "SellButton", 0, 220, 5, 320, 40))
{
Print("Failed to create Sell button");
return false;
}
sellButton.Text("Sell");
tradeManagementPanel.Add(sellButton);
// Close Position Button
if (!closePosButton.Create(chart_id, "ClosePosButton", 0, 130, 50, 260, 70))
{
Print("Failed to create Close Position button");
return false;
}
closePosButton.Text("Close Position");
tradeManagementPanel.Add(closePosButton);
// Modify Position Button
if (!modifyPosButton.Create(chart_id, "ModifyPosButton", 0, 270, 50, 410, 70))
{
Print("Failed to create Modify Position button");
return false;
}
modifyPosButton.Text("Modify Position");
tradeManagementPanel.Add(modifyPosButton);
// Set Stop-Loss Button
if (!setSLButton.Create(chart_id, "SetSLButton", 0, 330, 5, 430, 40))
{
Print("Failed to create Set Stop-Loss button");
return false;
}
setSLButton.Text("Set SL");
tradeManagementPanel.Add(setSLButton);
// Set Take-Profit Button
if (!setTPButton.Create(chart_id, "SetTPButton", 0, 440, 5, 540, 40))
{
Print("Failed to create Set Take-Profit button");
return false;
}
setTPButton.Text("Set TP");
tradeManagementPanel.Add(setTPButton);
return true;
}
Após compilar e executar o código, obtivemos a seguinte imagem, onde todos os botões agora estão corretamente posicionados.
Painel de controle de trading: os botões estão dispostos de forma conveniente
Com a alteração do layout concluída e os botões devidamente organizados, podemos seguir para a edição dos botões existentes e a adição de novos.
Outro ponto importante é evitar conflitos com os botões internos. A imagem abaixo mostra como os botões nativos se sobrepunham ao nosso painel administrativo.
Botões rápidos se sobrepõem ao painel administrativo
Para resolver esse problema, aplicamos um deslocamento nas coordenadas X, movendo os botões para a direita e ajustando a posição do painel. No trecho de código abaixo, apresentamos o novo valor atualizado:
//1
//2
//2
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 260, 30, 1040, 150))
{
Print("Failed to create Communictions panel dialog");
return INIT_FAILED;
}
A compilação foi concluída com sucesso, e o problema foi solucionado. Veja o novo layout abaixo:
Os botões de acesso rápido estão convenientemente posicionados e não se sobrepõem.
Função auxiliar e criação de novo botão
Nas minhas aulas de C++, meu professor sempre enfatizava a importância da função de repetição (repeat), considerando-a um conceito fundamental para qualquer pessoa que desejasse se chamar de programador. Mais tarde, ao explorar as funções auxiliares no MQL5, inicialmente pensei ter encontrado algo semelhante à função repeat em C++. Contudo, logo percebi que, embora apresentem certas vantagens, essas funções atendem a propósitos distintos.
Em resumo, as funções auxiliares no MQL5 podem ser comparadas a "objetos funcionais", que parametrizam comportamentos, enquanto as utilidades de repetição em C++ estão mais ligadas à aplicação de transformações sequenciais ou padrões.
Aqui estão algumas semelhanças entre elas:
- Ambas têm como objetivo reduzir código repetitivo.
- Ambas aumentam a clareza e a facilidade de manutenção do código.
Neste ponto da discussão, apresentamos o conceito de funções auxiliares porque estamos prestes a implementar vários botões, e queremos que nosso trabalho seja estruturado e apresentável.
Uma das funcionalidades que considero particularmente útil ao gerenciar múltiplas operações é a possibilidade de otimizar o processo, permitindo lidar com várias negociações de forma eficiente.
Operações em massa: são usadas no MetaTrader 5 para gerenciar grande quantidade de trades
Daremos prioridade às operações de trading que possam ser executadas com um único clique. Mais adiante, nos concentraremos em funções de modificação de trades, como ajuste de níveis de stop e sua configuração diretamente a partir do painel administrativo. A partir da imagem acima, também podemos tirar inspiração para os nomes dos botões que poderão ser usados em nosso projeto.
Abaixo está a descrição dos botões:
- Close all Positions (fechar todas as posições)
- Close Profitable Positions (fechar posições lucrativas)
- Close Losing Positions (fechar posições perdedoras)
- Close Buy Positions (fechar posições de compra)
- Close Sell Positions (fechar posições de venda)
- Delete All Orders (excluir todas as ordens)
- Delete Limit Orders (excluir ordens limit)
- Delete Stop Orders (excluir ordens stop)
- Delete Stop Limit Orders (excluir ordens stop limit)
Até este ponto, já estamos familiarizados com os botões a partir de nossas discussões anteriores. O procedimento de criação de botões individuais é praticamente o mesmo, variando apenas pelas suas coordenadas. Para minimizar tarefas repetitivas, utilizamos uma função auxiliar criada especificamente para otimizar o processo. Essa abordagem reduz o tamanho do código e combina de forma eficiente todos os novos botões.
Primeiro, declaramos nossos botões como variáveis globais:
// Button Declarations CButton buyButton; // Button for Buy operations CButton sellButton; // Button for Sell operations CButton closeAllButton; // Button for closing all positions CButton closeProfitButton; // Button for closing profitable positions CButton closeLossButton; // Button for closing losing positions CButton closeBuyButton; // Button for closing Buy positions CButton closeSellButton; // Button for closing Sell positions CButton deleteAllOrdersButton; // Button for deleting all orders CButton deleteLimitOrdersButton; // Button for deleting limit orders CButton deleteStopOrdersButton; // Button for deleting stop orders CButton deleteStopLimitOrdersButton; // Button for deleting stop limit orders
Após concluir as declarações, é hora de implementar a função auxiliar responsável pela criação dos botões. A função auxiliar CreateButton é uma utilidade otimizada projetada para simplificar a tarefa repetitiva de criar e configurar botões no painel de controle de trading.
Essa função recebe parâmetros que incluem a referência ao botão, seu nome, o texto do rótulo e as coordenadas, ao mesmo tempo em que processa a lógica básica de criação e configuração, incluindo o tratamento de erros. Ao centralizar esse processo, eliminamos redundâncias no código e garantimos que todos os botões sejam criados de forma padronizada e com o mínimo de esforço.
Essa modularidade é fundamental, pois melhora a legibilidade e a manutenção do código, permitindo expandir facilmente o painel com botões adicionais ou ajustar funcionalidades em um único ponto, em vez de em várias partes do código. Em essência, a função auxiliar atua como uma ponte entre o design do painel e o processo de criação dos botões, garantindo uma integração fluida.
Aqui está o código da nossa função auxiliar:
//Helper Function For seamless Button creation bool CreateButton(CButton &button, const string name, const string text, int x1, int y1, int x2, int y2) { long chart_id = ChartID(); if (!button.Create(chart_id, name, 0, x1, y1, x2, y2)) { Print("Failed to create button: ", name); return false; } button.Text(text); tradeManagementPanel.Add(button); return true; }
O trecho de código acima encapsula todo o processo de criação de um botão. A função CreateTradeManagementControls atua como o organizador principal, chamando repetidamente a função CreateButton para definir e posicionar logicamente cada botão no painel de controle de trading. Em vez de duplicar a lógica de criação de botões para cada controle, essa função se concentra exclusivamente em especificar os dados únicos, como coordenadas, rótulos e tipos de botões.
A construção modular implementada com a função CreateButton mantém essa função de alto nível concisa e focada em seu objetivo principal: estruturar o layout do painel. Juntas, essas duas funções trabalham de forma harmoniosa — CreateTradeManagementControls organiza a estrutura, enquanto delega as tarefas repetitivas à CreateButton — resultando em uma implementação limpa, eficiente e adaptável do painel de controle de trading. O código de todos os botões é apresentado abaixo.
//+------------------------------------------------------------------+ //| Create Trade Management Controls | //+------------------------------------------------------------------+ bool CreateTradeManagementControls() { // Coordinates for buttons (adjust as needed) const int Y1_TOP = 5, Y2_TOP = 40; const int Y1_MID = 50, Y2_MID = 70; const int Y1_BOTTOM = 80, Y2_BOTTOM = 100; // Buy Button if (!CreateButton(buyButton, "BuyButton", "Buy", 130, Y1_TOP, 210, Y2_TOP)) return false; // Sell Button if (!CreateButton(sellButton, "SellButton", "Sell", 220, Y1_TOP, 320, Y2_TOP)) return false; // Close All Positions Button if (!CreateButton(closeAllButton, "CloseAllButton", "Close All", 130, Y1_MID, 230, Y2_MID)) return false; // Close Profitable Positions Button if (!CreateButton(closeProfitButton, "CloseProfitButton", "Close Profitable", 240, Y1_MID, 380, Y2_MID)) return false; // Close Losing Positions Button if (!CreateButton(closeLossButton, "CloseLossButton", "Close Losing", 390, Y1_MID, 510, Y2_MID)) return false; // Close Buy Positions Button if (!CreateButton(closeBuyButton, "CloseBuyButton", "Close Buys", 520, Y1_MID, 620, Y2_MID)) return false; // Close Sell Positions Button if (!CreateButton(closeSellButton, "CloseSellButton", "Close Sells", 630, Y1_MID, 730, Y2_MID)) return false; // Delete All Orders Button if (!CreateButton(deleteAllOrdersButton, "DeleteAllOrdersButton", "Delete All Orders", 130, Y1_BOTTOM , 270, Y2_BOTTOM )) return false; // Delete Limit Orders Button if (!CreateButton(deleteLimitOrdersButton, "DeleteLimitOrdersButton", "Delete Limits", 275, Y1_BOTTOM , 385, Y2_BOTTOM )) return false; // Delete Stop Orders Button if (!CreateButton(deleteStopOrdersButton, "DeleteStopOrdersButton", "Delete Stops", 390, Y1_BOTTOM , 515, Y2_BOTTOM )) return false; // Delete Stop Limit Orders Button if (!CreateButton(deleteStopLimitOrdersButton, "DeleteStopLimitOrdersButton", "Delete Stop Limits", 520, Y1_BOTTOM , 660, Y2_BOTTOM )) return false; return true; // All buttons created successfully }
Aqui está o resultado obtido com a aplicação do novo layout
Novo layout após a integração dos novos botões
Ao adicionar novos botões, alguns dos antigos foram removidos para manter a uniformidade. Neste momento, estamos focados em operações que exigem execução imediata, sem a necessidade de inserir dados adicionais, como no caso da modificação de ordens.
Implementação dos manipuladores de botões
Para ampliar a funcionalidade do painel de controle de trading, implementamos funções específicas de manipulação para cada botão, permitindo executar determinadas operações de trading. A seguir estão as explicações de cada trecho de código:
1. Manipulador do botão Buy (OnBuyButtonClick)
A função OnBuyButtonClick permite criar uma ordem a mercado de compra do ativo especificado. Utilizando a classe CTrade, ela processa os principais parâmetros da negociação, como tamanho do lote, slippage, stop loss e take profit, garantindo execução precisa. Isso é fundamental para traders que desejam abrir rapidamente posições de compra em um ambiente controlado por programação.
//+------------------------------------------------------------------+ //| Handle Buy button click | //+------------------------------------------------------------------+ void OnBuyButtonClick() { CTrade trade; double lotSize = 0.1; // Example lot size double slippage = 3; // Example slippage double stopLoss = 0; // Example stop loss (in points) double takeProfit = 0; // Example take profit (in points) // Open Buy order double askPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, askPrice) && askPrice > 0) { if (trade.Buy(lotSize, Symbol(), askPrice, slippage, stopLoss, takeProfit)) { Print("Buy order executed successfully."); } else { Print("Failed to execute Buy order. Error: ", GetLastError()); } } else { Print("Failed to retrieve Ask price. Error: ", GetLastError()); } // Execute Buy order logic here Print("Executing Buy operation"); }
2. Manipulador do botão Sell (OnSellButtonClick)
A função OnSellButtonClick é o reflexo do manipulador de compra, permitindo que o usuário venda um ativo por meio de uma ordem a mercado. Estruturando a lógica de venda, ela garante o processamento consistente de parâmetros como tamanho do lote e slippage, tornando o painel de trading eficiente para a abertura de ordens de venda.
//+------------------------------------------------------------------+ //| Handle Sell button click | //+------------------------------------------------------------------+ void OnSellButtonClick() { CTrade trade; double lotSize = 0.1; // Example lot size double slippage = 3; // Example slippage double stopLoss = 0; // Example stop loss (in points) double takeProfit = 0; // Example take profit (in points) double bidPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_BID, bidPrice) && bidPrice > 0) { // Open Sell order if (trade.Sell(lotSize, Symbol(), bidPrice, slippage, stopLoss, takeProfit)) { Print("Sell order opened successfully."); } else { Print("Error opening sell order: ", trade.ResultRetcode()); } } else { Print("Failed to retrieve Bid price. Error: ", GetLastError()); } }
3. Manipulador Close All Positions (OnCloseAllButtonClick)
Essa função automatiza o fechamento de todas as posições ativas, percorrendo as negociações abertas e utilizando CTrade. PositionClose para execução. É especialmente útil para traders que precisam encerrar rapidamente todas as operações, seja para se proteger contra uma súbita volatilidade do mercado ou para atender a exigências de uma estratégia de saída.
//+------------------------------------------------------------------+ //| Handle Close All button click | //+------------------------------------------------------------------+ void OnCloseAllButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i)) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("All positions closed."); }
4. Manipulador Close Profitable Positions (OnCloseProfitButtonClick)
Com a função OnCloseProfitButtonClick, os traders podem realizar o fechamento seletivo de posições lucrativas. A função percorre as negociações, avalia o lucro e fecha apenas aquelas que estão positivas, permitindo estratégias voltadas para garantir os ganhos, enquanto mantém posições perdedoras abertas para possível recuperação ou avaliação posterior.
//+------------------------------------------------------------------+ //| Handle Close Profitable button click | //+------------------------------------------------------------------+ void OnCloseProfitButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() > 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Profitable positions closed."); }
5. Manipulador Close Losing Positions (OnCloseLossButtonClick)
Esse manipulador atua como uma ferramenta de gestão de risco, fechando todas as posições que apresentam prejuízo. O foco exclusivo em operações com resultado negativo ajuda a reduzir novas perdas, o que é essencial para preservar o capital da conta e respeitar limites de perda previamente estabelecidos.
//+------------------------------------------------------------------+ //| Handle Close Losing button click | //+------------------------------------------------------------------+ void OnCloseLossButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() < 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Losing positions closed."); } void OnCloseBuyButtonClick() { // Close Buy positions logic Print("Closing Buy positions"); } void OnCloseSellButtonClick() { // Close Sell positions logic Print("Closing Sell positions"); }
6. Manipulador Delete All Orders (OnDeleteAllOrdersButtonClick)
A função remove todas as ordens pendentes, garantindo que ordens limit ou stop remanescentes não afetem a conta. O uso da classe COrderInfo para localizar e cancelar as ordens mantém a organização da carteira de ordens e previne execuções não intencionais.
//+------------------------------------------------------------------+ //| Handle Delete All Orders button click | //+------------------------------------------------------------------+ void OnDeleteAllOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i)) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All orders deleted."); }
7. Manipulador Delete Limit Orders (OnDeleteLimitOrdersButtonClick)
A função OnDeleteLimitOrdersButtonClick é voltada especificamente para o cancelamento de ordens limit. Isso é útil para traders que desejam ajustar sua estratégia mantendo ativas ordens stop ou outros tipos, oferecendo um controle preciso sobre a gestão de ordens.
//+------------------------------------------------------------------+ //| Handle Delete Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All limit orders deleted."); }
8. Manipulador Delete Stop Orders (OnDeleteStopOrdersButtonClick)
Esse manipulador tem como objetivo excluir todas as ordens stop, garantindo que não resultem em operações indesejadas em mercados voláteis. Ao isolar as ordens stop, fornece ao trader um nível mais detalhado de controle sobre o gerenciamento de ordens pendentes.
//+------------------------------------------------------------------+ //| Handle Delete Stop Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP && ORDER_TYPE_SELL_STOP) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop orders deleted."); }
9. Manipulador Delete Stop Limit Orders (OnDeleteStopLimitOrdersButtonClick)
A função lida com a remoção de ordens stop limit, sendo especialmente útil em estratégias que utilizam tipos híbridos de ordens. Isso amplia a flexibilidade e mantém a execução das ordens alinhada com as mudanças nas condições de mercado ou nas estratégias do trader.
//+------------------------------------------------------------------+ //| Handle Delete Stop Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_LIMIT && ORDER_TYPE_SELL_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop limit orders deleted."); }
Integração no OnChartEvent:
Para vincular os cliques dos botões a essas funções, integramos suas chamadas dentro da função OnChartEvent. Associando o sparam do botão ao manipulador correspondente, o programa garante uma interação fluida entre a interface gráfica e a lógica interna de trading, tornando o painel responsivo e prático para o usuário.
//+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { // Panel navigation buttons if (sparam == "HomeButtonComm") { adminHomePanel.Show(); communicationsPanel.Hide(); } else if (sparam == "HomeButtonTrade") { adminHomePanel.Show(); tradeManagementPanel.Hide(); } if (sparam == "TradeMgmtAccessButton") { tradeManagementPanel.Show(); adminHomePanel.Hide(); } else if (sparam == "CommunicationsPanelAccessButton") { communicationsPanel.Show(); adminHomePanel.Hide(); } // Control buttons for panel resizing and closing else if (sparam == "MinimizeButton") { OnMinimizeButtonClick(); } else if (sparam == "MaximizeButton") { OnMaximizeButtonClick(); } else if (sparam == "CloseButton") { ExpertRemove(); } { if (sparam == "LoginButton") { OnLoginButtonClick(); } else if (sparam == "CloseAuthButton") { OnCloseAuthButtonClick(); } else if (sparam == "TwoFALoginButton") { OnTwoFALoginButtonClick(); } else if (sparam == "Close2FAButton") { OnClose2FAButtonClick(); } } switch (id) { case CHARTEVENT_OBJECT_CLICK: if (sparam == "SendButton") OnSendButtonClick(); else if (sparam == "ClearButton") OnClearButtonClick(); else if (sparam == "ChangeFontButton") OnChangeFontButtonClick(); else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick(); else if (sparam == "MinimizeButton") OnMinimizeButtonClick(); else if (sparam == "MaximizeButton") OnMaximizeButtonClick(); else if (sparam == "CloseButton") OnCloseButtonClick(); else if (StringFind(sparam, "QuickMessageButton") != -1) { long index = StringToInteger(StringSubstr(sparam, 18)); OnQuickMessageButtonClick(index - 1); } break; case CHARTEVENT_OBJECT_ENDEDIT: if (sparam == "InputBox") OnInputChange(); break; } } // Trade management buttons if (sparam == "BuyButton") OnBuyButtonClick(); else if (sparam == "SellButton") OnSellButtonClick(); else if (sparam == "CloseAllButton") OnCloseAllButtonClick(); else if (sparam == "CloseProfitButton") OnCloseProfitButtonClick(); else if (sparam == "CloseLossButton") OnCloseLossButtonClick(); else if (sparam == "CloseBuyButton") OnCloseBuyButtonClick(); else if (sparam == "CloseSellButton") OnCloseSellButtonClick(); else if (sparam == "DeleteAllOrdersButton") OnDeleteAllOrdersButtonClick(); else if (sparam == "DeleteLimitOrdersButton") OnDeleteLimitOrdersButtonClick(); else if (sparam == "DeleteStopOrdersButton") OnDeleteStopOrdersButtonClick(); else if (sparam == "DeleteStopLimitOrdersButton") OnDeleteStopLimitOrdersButtonClick(); }
Assim, nosso código final ficou consideravelmente extenso:
//+------------------------------------------------------------------+ //| Admin Panel.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property description "A secure and responsive Admin Panel. Send messages to your telegram clients without leaving MT5" #property version "1.22" #include <Trade\Trade.mqh> #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> // Input parameters for quick messages input string QuickMessage1 = "Updates"; input string QuickMessage2 = "Close all"; input string QuickMessage3 = "In deep profits"; input string QuickMessage4 = "Hold position"; input string QuickMessage5 = "Swing Entry"; input string QuickMessage6 = "Scalp Entry"; input string QuickMessage7 = "Book profit"; input string QuickMessage8 = "Invalid Signal"; input string InputChatId = "YOUR_CHAT_ID"; input string InputBotToken = "YOUR_BOT_TOKEN"; // Constants for 2FA const string Hardcoded2FAChatId = "Replace chat ID with yours"; const string Hardcoded2FABotToken = "Replace with your bot token"; // Global variables CDialog adminHomePanel, tradeManagementPanel, communicationsPanel; CDialog authentication, twoFactorAuth; CButton homeButtonComm, homeButtonTrade; CButton sendButton, clearButton, changeFontButton, toggleThemeButton; CButton loginButton, closeAuthButton, twoFALoginButton, close2FAButton; CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton; CButton tradeMgmtAccessButton, communicationsPanelAccessButton; CEdit inputBox, passwordInputBox, twoFACodeInput; CLabel charCounter, passwordPromptLabel, feedbackLabel, twoFAPromptLabel, twoFAFeedbackLabel; bool minimized = false; bool darkTheme = false; int MAX_MESSAGE_LENGTH = 4096; string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman" }; int currentFontIndex = 0; string Password = "2024"; // Hardcoded password string twoFACode = ""; // Button Declarations for Trade Management CButton buyButton; // Button for Buy operations CButton sellButton; // Button for Sell operations CButton closeAllButton; // Button for closing all positions CButton closeProfitButton; // Button for closing profitable positions CButton closeLossButton; // Button for closing losing positions CButton closeBuyButton; // Button for closing Buy positions CButton closeSellButton; // Button for closing Sell positions CButton deleteAllOrdersButton; // Button for deleting all orders CButton deleteLimitOrdersButton; // Button for deleting limit orders CButton deleteStopOrdersButton; // Button for deleting stop orders CButton deleteStopLimitOrdersButton; // Button for deleting stop limit orders //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if (!ShowAuthenticationPrompt()) { Print("Authorization failed. Exiting..."); return INIT_FAILED; } if (!adminHomePanel.Create(ChartID(), "Admin Home Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Admin Home Panel"); return INIT_FAILED; } if (!CreateAdminHomeControls()) { Print("Home panel control creation failed"); return INIT_FAILED; } if (!communicationsPanel.Create(ChartID(), "Communications Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Communications panel dialog"); return INIT_FAILED; } if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0,260, 30, 1040, 170)) { Print("Failed to create Trade Management panel dialog"); return INIT_FAILED; } if (!CreateControls()) { Print("Control creation failed"); return INIT_FAILED; } if (!CreateTradeManagementControls()) { Print("Trade management control creation failed"); return INIT_FAILED; } adminHomePanel.Hide(); // Hide home panel by default on initialization communicationsPanel.Hide(); // Hide the Communications Panel tradeManagementPanel.Hide(); // Hide the Trade Management Panel return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { // Panel navigation buttons if (sparam == "HomeButtonComm") { adminHomePanel.Show(); communicationsPanel.Hide(); } else if (sparam == "HomeButtonTrade") { adminHomePanel.Show(); tradeManagementPanel.Hide(); } if (sparam == "TradeMgmtAccessButton") { tradeManagementPanel.Show(); adminHomePanel.Hide(); } else if (sparam == "CommunicationsPanelAccessButton") { communicationsPanel.Show(); adminHomePanel.Hide(); } // Control buttons for panel resizing and closing else if (sparam == "MinimizeButton") { OnMinimizeButtonClick(); } else if (sparam == "MaximizeButton") { OnMaximizeButtonClick(); } else if (sparam == "CloseButton") { ExpertRemove(); } { if (sparam == "LoginButton") { OnLoginButtonClick(); } else if (sparam == "CloseAuthButton") { OnCloseAuthButtonClick(); } else if (sparam == "TwoFALoginButton") { OnTwoFALoginButtonClick(); } else if (sparam == "Close2FAButton") { OnClose2FAButtonClick(); } } switch (id) { case CHARTEVENT_OBJECT_CLICK: if (sparam == "SendButton") OnSendButtonClick(); else if (sparam == "ClearButton") OnClearButtonClick(); else if (sparam == "ChangeFontButton") OnChangeFontButtonClick(); else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick(); else if (sparam == "MinimizeButton") OnMinimizeButtonClick(); else if (sparam == "MaximizeButton") OnMaximizeButtonClick(); else if (sparam == "CloseButton") OnCloseButtonClick(); else if (StringFind(sparam, "QuickMessageButton") != -1) { long index = StringToInteger(StringSubstr(sparam, 18)); OnQuickMessageButtonClick(index - 1); } break; case CHARTEVENT_OBJECT_ENDEDIT: if (sparam == "InputBox") OnInputChange(); break; } } // Trade management buttons if (sparam == "BuyButton") OnBuyButtonClick(); else if (sparam == "SellButton") OnSellButtonClick(); else if (sparam == "CloseAllButton") OnCloseAllButtonClick(); else if (sparam == "CloseProfitButton") OnCloseProfitButtonClick(); else if (sparam == "CloseLossButton") OnCloseLossButtonClick(); else if (sparam == "CloseBuyButton") OnCloseBuyButtonClick(); else if (sparam == "CloseSellButton") OnCloseSellButtonClick(); else if (sparam == "DeleteAllOrdersButton") OnDeleteAllOrdersButtonClick(); else if (sparam == "DeleteLimitOrdersButton") OnDeleteLimitOrdersButtonClick(); else if (sparam == "DeleteStopOrdersButton") OnDeleteStopOrdersButtonClick(); else if (sparam == "DeleteStopLimitOrdersButton") OnDeleteStopLimitOrdersButtonClick(); } //+------------------------------------------------------------------+ //| Trade management button handlers | //+------------------------------------------------------------------+ void OnBuyButtonClick() { CTrade trade; double lotSize = 0.1; // lot size double slippage = 3; // slippage double stopLoss = 0; // stop loss (in points) double takeProfit = 0; // take profit (in points) // Open Buy order double askPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, askPrice) && askPrice > 0) { if (trade.Buy(lotSize, Symbol(), askPrice, slippage, stopLoss, takeProfit)) { Print("Buy order executed successfully."); } else { Print("Failed to execute Buy order. Error: ", GetLastError()); } } else { Print("Failed to retrieve Ask price. Error: ", GetLastError()); } // Execute Buy order logic here Print("Executing Buy operation"); } //+------------------------------------------------------------------+ //| Handle Sell button click | //+------------------------------------------------------------------+ void OnSellButtonClick() { CTrade trade; double lotSize = 0.1; // lot size double slippage = 3; // slippage double stopLoss = 0; // stop loss (in points) double takeProfit = 0; // take profit (in points) double bidPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_BID, bidPrice) && bidPrice > 0) { // Open Sell order if (trade.Sell(lotSize, Symbol(), bidPrice, slippage, stopLoss, takeProfit)) { Print("Sell order opened successfully."); } else { Print("Error opening sell order: ", trade.ResultRetcode()); } } else { Print("Failed to retrieve Bid price. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Handle Close All button click | //+------------------------------------------------------------------+ void OnCloseAllButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i)) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("All positions closed."); } //+------------------------------------------------------------------+ //| Handle Close Profitable button click | //+------------------------------------------------------------------+ void OnCloseProfitButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() > 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Profitable positions closed."); } //+------------------------------------------------------------------+ //| Handle Close Losing button click | //+------------------------------------------------------------------+ void OnCloseLossButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() < 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Losing positions closed."); } void OnCloseBuyButtonClick() { // Close Buy positions logic Print("Closing Buy positions"); } void OnCloseSellButtonClick() { // Close Sell positions logic Print("Closing Sell positions"); } //+------------------------------------------------------------------+ //| Handle Delete All Orders button click | //+------------------------------------------------------------------+ void OnDeleteAllOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i)) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All orders deleted."); } //+------------------------------------------------------------------+ //| Handle Delete Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All limit orders deleted."); } //+------------------------------------------------------------------+ //| Handle Delete Stop Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP && ORDER_TYPE_SELL_STOP) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop orders deleted."); } //+------------------------------------------------------------------+ //| Handle Delete Stop Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_LIMIT && ORDER_TYPE_SELL_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop limit orders deleted."); } //+------------------------------------------------------------------+ //| Show authentication input dialog | //+------------------------------------------------------------------+ bool ShowAuthenticationPrompt() { if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create authentication dialog"); return false; } if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95)) { Print("Failed to create password input box"); return false; } authentication.Add(passwordInputBox); if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 40)) { Print("Failed to create password prompt label"); return false; } passwordPromptLabel.Text("Enter password: Access Admin Panel"); authentication.Add(passwordPromptLabel); if (!feedbackLabel.Create(ChartID(), "FeedbackLabel", 0, 20, 140, 380, 160)) { Print("Failed to create feedback label"); return false; } feedbackLabel.Text(""); feedbackLabel.Color(clrRed); // Red color for incorrect attempts authentication.Add(feedbackLabel); if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140)) { Print("Failed to create login button"); return false; } loginButton.Text("Login"); authentication.Add(loginButton); if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140)) { Print("Failed to create close button for authentication"); return false; } closeAuthButton.Text("Close"); authentication.Add(closeAuthButton); authentication.Show(); ChartRedraw(); return true; } //+------------------------------------------------------------------+ //| Show two-factor authentication input dialog | //+------------------------------------------------------------------+ void ShowTwoFactorAuthPrompt() { if (!twoFactorAuth.Create(ChartID(), "Two-Factor Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create 2FA dialog"); return; } if (!twoFACodeInput.Create(ChartID(), "TwoFACodeInput", 0, 20, 70, 260, 95)) { Print("Failed to create 2FA code input box"); return; } twoFactorAuth.Add(twoFACodeInput); if (!twoFAPromptLabel.Create(ChartID(), "TwoFAPromptLabel", 0, 20, 20, 380, 40)) { Print("Failed to create 2FA prompt label"); return; } twoFAPromptLabel.Text("Enter the 2FA code sent to your Telegram:"); twoFactorAuth.Add(twoFAPromptLabel); if (!twoFAFeedbackLabel.Create(ChartID(), "TwoFAFeedbackLabel", 0, 20, 140, 380, 160)) { Print("Failed to create 2FA feedback label"); return; } twoFAFeedbackLabel.Text(""); twoFAFeedbackLabel.Color(clrRed); // Red color for incorrect 2FA attempts twoFactorAuth.Add(twoFAFeedbackLabel); if (!twoFALoginButton.Create(ChartID(), "TwoFALoginButton", 0, 20, 120, 100, 140)) { Print("Failed to create 2FA login button"); return; } twoFALoginButton.Text("Verify"); twoFactorAuth.Add(twoFALoginButton); if (!close2FAButton.Create(ChartID(), "Close2FAButton", 0, 120, 120, 200, 140)) { Print("Failed to create close button for 2FA"); return; } close2FAButton.Text("Close"); twoFactorAuth.Add(close2FAButton); twoFactorAuth.Show(); ChartRedraw(); } //+------------------------------------------------------------------+ //| Admin Home Panel controls creation | //+------------------------------------------------------------------+ bool CreateAdminHomeControls() { long chart_id = ChartID(); if (!tradeMgmtAccessButton.Create(chart_id, "TradeMgmtAccessButton", 0, 50, 50, 250, 90)) { Print("Failed to create Trade Management Access button"); return false; } tradeMgmtAccessButton.Text("Trade Management Panel"); adminHomePanel.Add(tradeMgmtAccessButton); if (!communicationsPanelAccessButton.Create(chart_id, "CommunicationsPanelAccessButton", 0, 50, 100, 250, 140)) { Print("Failed to create Communications Panel Access button"); return false; } communicationsPanelAccessButton.Text("Communications Panel"); adminHomePanel.Add(communicationsPanelAccessButton); if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) { Print("Failed to create minimize button"); return false; } minimizeButton.Text("_"); adminHomePanel.Add(minimizeButton); if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) { Print("Failed to create maximize button"); return false; } maximizeButton.Text("[ ]"); adminHomePanel.Add(maximizeButton); if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) { Print("Failed to create close button"); return false; } closeButton.Text("X"); adminHomePanel.Add(closeButton); return true; } //Helper Function seamless Button creation bool CreateButton(CButton &button, const string name, const string text, int x1, int y1, int x2, int y2) { long chart_id = ChartID(); if (!button.Create(chart_id, name, 0, x1, y1, x2, y2)) { Print("Failed to create button: ", name); return false; } button.Text(text); tradeManagementPanel.Add(button); return true; } //+------------------------------------------------------------------+ //| Create Trade Management Controls (Buttons) | //+------------------------------------------------------------------+ bool CreateTradeManagementControls() { // Coordinates for buttons (adjust as needed) const int Y1_TOP = 5, Y2_TOP = 40; const int Y1_MID = 50, Y2_MID = 70; const int Y1_BOTTOM = 80, Y2_BOTTOM = 100; // Create Buttons if (!CreateButton(buyButton, "BuyButton", "Buy", 130, Y1_TOP, 210, Y2_TOP)) return false; if (!CreateButton(sellButton, "SellButton", "Sell", 220, Y1_TOP, 320, Y2_TOP)) return false; if (!CreateButton(closeAllButton, "CloseAllButton", "Close All", 130, Y1_MID, 230, Y2_MID)) return false; if (!CreateButton(closeProfitButton, "CloseProfitButton", "Close Profitable", 240, Y1_MID, 380, Y2_MID)) return false; if (!CreateButton(closeLossButton, "CloseLossButton", "Close Losing", 390, Y1_MID, 510, Y2_MID)) return false; if (!CreateButton(closeBuyButton, "CloseBuyButton", "Close Buys", 520, Y1_MID, 620, Y2_MID)) return false; if (!CreateButton(closeSellButton, "CloseSellButton", "Close Sells", 630, Y1_MID, 730, Y2_MID)) return false; if (!CreateButton(deleteAllOrdersButton, "DeleteAllOrdersButton", "Delete All Orders", 130, Y1_BOTTOM , 270, Y2_BOTTOM )) return false; if (!CreateButton(deleteLimitOrdersButton, "DeleteLimitOrdersButton", "Delete Limits", 275, Y1_BOTTOM , 385, Y2_BOTTOM )) return false; if (!CreateButton(deleteStopOrdersButton, "DeleteStopOrdersButton", "Delete Stops", 390, Y1_BOTTOM , 515, Y2_BOTTOM )) return false; if (!CreateButton(deleteStopLimitOrdersButton, "DeleteStopLimitOrdersButton", "Delete Stop Limits", 520, Y1_BOTTOM , 660, Y2_BOTTOM )) return false; return true; // All buttons created successfully } //+------------------------------------------------------------------+ //| Handle login button click | //+------------------------------------------------------------------+ void OnLoginButtonClick() { string enteredPassword = passwordInputBox.Text(); if (enteredPassword == Password) { twoFACode = GenerateRandom6DigitCode(); SendMessageToTelegram("A login attempt was made on the Admin Panel. Please use this code to verify your identity: " + twoFACode, Hardcoded2FAChatId, Hardcoded2FABotToken); authentication.Destroy(); ShowTwoFactorAuthPrompt(); Print("Password authentication successful. A 2FA code has been sent to your Telegram."); } else { feedbackLabel.Text("Wrong password. Try again."); passwordInputBox.Text(""); } ///Handlers for the trade management } //+------------------------------------------------------------------+ //| Handle 2FA login button click | //+------------------------------------------------------------------+ void OnTwoFALoginButtonClick() { // If 2FA is successful, show the trade management panel string enteredCode = twoFACodeInput.Text(); if (enteredCode == twoFACode) { twoFactorAuth.Destroy(); adminHomePanel.Show(); Print("2FA authentication successful. Access granted to Trade Management Panel."); } else { twoFAFeedbackLabel.Text("Wrong code. Try again."); twoFACodeInput.Text(""); } } //+------------------------------------------------------------------+ //| Handle close button for authentication | //+------------------------------------------------------------------+ void OnCloseAuthButtonClick() { authentication.Destroy(); ExpertRemove(); // Exit the expert Print("Authentication dialog closed."); } //+------------------------------------------------------------------+ //| Handle close button for 2FA | //+------------------------------------------------------------------+ void OnClose2FAButtonClick() { twoFactorAuth.Destroy(); ExpertRemove(); Print("2FA dialog closed."); } //+------------------------------------------------------------------+ //| Create necessary UI controls | //+------------------------------------------------------------------+ bool CreateControls() { long chart_id = ChartID(); if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95)) { Print("Failed to create input box"); return false; } communicationsPanel.Add(inputBox); // Create Home Button for Communications Panel if (!homeButtonComm.Create(chart_id, "HomeButtonComm", 0, 20, 120, 120,150)) { Print("Failed to create Home button for Communications Panel"); return false; } homeButtonComm.Text("Home 🏠"); communicationsPanel.Add(homeButtonComm); // Create Home Button for Trade Management Panel if (!homeButtonTrade.Create(chart_id, "HomeButtonTrade", 0, 20, 10, 120, 30)) { Print("Failed to create Home button for Trade Management Panel"); return false; } homeButtonTrade.Text("Home 🏠"); tradeManagementPanel.Add(homeButtonTrade); if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25)) { Print("Failed to create character counter"); return false; } charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH)); communicationsPanel.Add(charCounter); if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125)) { Print("Failed to create clear button"); return false; } clearButton.Text("Clear"); communicationsPanel.Add(clearButton); if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125)) { Print("Failed to create send button"); return false; } sendButton.Text("Send"); communicationsPanel.Add(sendButton); if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115)) { Print("Failed to create change font button"); return false; } changeFontButton.Text("Font<>"); communicationsPanel.Add(changeFontButton); if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115)) { Print("Failed to create toggle theme button"); return false; } toggleThemeButton.Text("Theme<>"); communicationsPanel.Add(toggleThemeButton); if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) { Print("Failed to create minimize button"); return false; } minimizeButton.Text("_"); communicationsPanel.Add(minimizeButton); if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) { Print("Failed to create maximize button"); return false; } maximizeButton.Text("[ ]"); communicationsPanel.Add(maximizeButton); if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) { Print("Failed to create close button"); return false; } closeButton.Text("X"); communicationsPanel.Add(closeButton); return CreateQuickMessageButtons(); } //+------------------------------------------------------------------+ //| Create quick message buttons | //+------------------------------------------------------------------+ bool CreateQuickMessageButtons() { string quickMessages[] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; int startX = 5, startY = 160, width = 222, height = 65, spacing = 5; for (int i = 0; i < ArraySize(quickMessages); i++) { bool created = quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height); if (!created) { Print("Failed to create quick message button ", i + 1); return false; } quickMessageButtons[i].Text(quickMessages[i]); communicationsPanel.Add(quickMessageButtons[i]); } return true; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { communicationsPanel.Destroy(); Print("Deinitialization complete"); } //+------------------------------------------------------------------+ //| Handle custom message send button click | //+------------------------------------------------------------------+ void OnSendButtonClick() { string message = inputBox.Text(); if (StringLen(message) > 0) { if (SendMessageToTelegram(message, InputChatId, InputBotToken)) Print("Custom message sent: ", message); else Print("Failed to send custom message."); } else { Print("No message entered."); } } //+------------------------------------------------------------------+ //| Handle clear button click | //+------------------------------------------------------------------+ void OnClearButtonClick() { inputBox.Text(""); OnInputChange(); Print("Input box cleared."); } //+------------------------------------------------------------------+ //| Handle quick message button click | //+------------------------------------------------------------------+ void OnQuickMessageButtonClick(long index) { string quickMessages[] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; string message = quickMessages[(int)index]; if (SendMessageToTelegram(message, InputChatId, InputBotToken)) Print("Quick message sent: ", message); else Print("Failed to send quick message."); } //+------------------------------------------------------------------+ //| Update character counter | //+------------------------------------------------------------------+ void OnInputChange() { int currentLength = StringLen(inputBox.Text()); charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH)); ChartRedraw(); } //+------------------------------------------------------------------+ //| Handle toggle theme button click | //+------------------------------------------------------------------+ void OnToggleThemeButtonClick() { darkTheme = !darkTheme; UpdateThemeColors(); Print("Theme toggled: ", darkTheme ? "Dark" : "Light"); } //+------------------------------------------------------------------+ //| Update theme colors for the panel | //+------------------------------------------------------------------+ void UpdateThemeColors() { color textColor = darkTheme ? clrWhite : clrBlack; color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro; color borderColor = darkTheme ? clrSlateGray : clrGray; color bgColor = darkTheme ? clrDarkBlue : clrWhite; UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor); for (int i = 0; i < ArraySize(quickMessageButtons); i++) { UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor); } ChartRedraw(); } //+------------------------------------------------------------------+ //| Apply theme settings to a button | //+------------------------------------------------------------------+ void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor) { button.SetTextColor(textColor); button.SetBackgroundColor(bgColor); button.SetBorderColor(borderColor); } //+------------------------------------------------------------------+ //| Handle change font button click | //+------------------------------------------------------------------+ void OnChangeFontButtonClick() { currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts); SetFontForAll(availableFonts[currentFontIndex]); Print("Font changed to: ", availableFonts[currentFontIndex]); ChartRedraw(); } //+------------------------------------------------------------------+ //| Set font for all input boxes and buttons | //+------------------------------------------------------------------+ void SetFontForAll(string fontName) { inputBox.Font(fontName); clearButton.Font(fontName); sendButton.Font(fontName); toggleThemeButton.Font(fontName); changeFontButton.Font(fontName); minimizeButton.Font(fontName); maximizeButton.Font(fontName); closeButton.Font(fontName); for (int i = 0; i < ArraySize(quickMessageButtons); i++) { quickMessageButtons[i].Font(fontName); } } //+------------------------------------------------------------------+ //| Generate a random 6-digit code for 2FA | //+------------------------------------------------------------------+ string GenerateRandom6DigitCode() { int code = MathRand() % 1000000; // Produces a 6-digit number return StringFormat("%06d", code); // Ensures leading zeros } //+------------------------------------------------------------------+ //| Handle minimize button click | //+------------------------------------------------------------------+ void OnMinimizeButtonClick() { minimized = true; communicationsPanel.Hide(); minimizeButton.Hide(); maximizeButton.Show(); closeButton.Show(); Print("Panel minimized."); } //+------------------------------------------------------------------+ //| Handle maximize button click | //+------------------------------------------------------------------+ void OnMaximizeButtonClick() { if (minimized) { communicationsPanel.Show(); minimizeButton.Show(); maximizeButton.Hide(); closeButton.Hide(); minimized = false; Print("Panel maximized."); } } //+------------------------------------------------------------------+ //| Handle close button click for admin panel | //+------------------------------------------------------------------+ void OnCloseButtonClick() { ExpertRemove(); Print("Admin panel closed."); } //+------------------------------------------------------------------+ //| Send the message to Telegram | //+------------------------------------------------------------------+ bool SendMessageToTelegram(string message, string chatId, string botToken) { string url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}"; char postData[]; ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1); int timeout = 5000; char result[]; string responseHeaders; int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders); if (responseCode == 200) { Print("Message sent successfully: ", message); return true; } else { Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); return false; } } //+------------------------------------------------------------------+
Testes
Durante os testes, todos os botões funcionaram corretamente e responderam conforme programado, tornando o painel de controle de trading um componente valioso no conjunto de ferramentas do administrador de trading. Conseguimos integrar com sucesso os novos botões e implementar seus manipuladores de eventos.
Funcionamento dos manipuladores de botões do painel de controle de trading
Os comentários na aba Experts informam sobre a execução bem-sucedida da ordem e o fechamento
Considerações finais
Revisamos as configurações do Telegram e aprimoramos o painel de controle de trading, adicionando novos botões. Também melhoramos o layout, reduzindo a escala vertical e ampliando a horizontal, incluindo o deslocamento posicional no eixo X. Esse ajuste eliminou os problemas de sobreposição entre os botões do painel e os botões nativos de negociação rápida, melhorando a visibilidade do gráfico e mantendo o acesso fácil aos botões de trading.
Após finalizar o layout, integramos os manipuladores de eventos dos botões para garantir sua resposta correta ao serem pressionados.
Espero que esta discussão tenha esclarecido o amplo potencial de programação de interfaces gráficas na linguagem MQL5, demonstrando a criatividade possível no design de diferentes componentes visuais. O artigo apresentado é apenas uma base para desenvolvimentos futuros.
Boa programação, colegas traders!
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16328
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- 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