English Русский 中文 Español Deutsch 日本語
preview
Construindo um Modelo de Restrição de Tendência de Candlestick (Parte 5): Sistema de Notificação (Parte II)

Construindo um Modelo de Restrição de Tendência de Candlestick (Parte 5): Sistema de Notificação (Parte II)

MetaTrader 5Negociação | 5 novembro 2024, 11:10
228 0
Clemence Benjamin
Clemence Benjamin

Conteúdo


Introdução

No artigo anterior, introduzimos brevemente o processo de integração. Ao dividi-lo em subseções, buscamos simplificar o entendimento do processo passo a passo. Uma base sólida é essencial, e esperamos que o alicerce que construímos seja firme enquanto nos aprofundamos na funcionalidade dessas integrações, especialmente dentro do nosso modelo de Restrição de Tendência. Nosso objetivo final é receber confortavelmente notificações tanto no Telegram quanto no WhatsApp ao final do projeto. Essa configuração garantirá que fiquemos informados sobre os indicadores sem perder nenhum sinal, tudo enquanto interagimos com amigos e familiares nas redes sociais. Compartilhar sinais diretamente na plataforma social será fácil, eliminando a necessidade de alternar entre aplicativos.

O objetivo deste artigo é nos guiar por cada etapa de forma abrangente até atingirmos os resultados desejados. Com o conhecimento básico adquirido no artigo anterior, tudo está claro agora. Vou elucidar cada linha de código que compõe o programa integrado. Existem quatro componentes principais neste projeto relacionados à integração com o Telegram que precisam ser mantidos em mente constantemente.

  • API do bot do Telegram.
  • Script em Python.
  • Servidor dedicado para hospedar o script ao lidar com Requisições Web.
  • Nosso programa de indicador configurado para o Telegram.

Esta é uma visão geral básica dos componentes envolvidos no processo principal de integração. Embora eu tenha mencionado especificamente Telegram e WhatsApp, vale notar que outras plataformas de redes sociais também podem ser integradas, desde que haja uma linguagem de programação disponível para facilitar o processo. Reconhecendo a importância da compatibilidade de linguagem na programação, incorporamos Python e MQL5 em um único projeto. Isso destaca o benefício de estar familiarizado com várias linguagens, como Python, C++, ONNX e C#. Tal conhecimento pode ajudar significativamente os programadores de MQL5 a desenvolver funcionalidades na plataforma e integrar com outras APIs sociais.

A integração prática do Telegram nos guiará para a terceira subseção da Parte 5 da série de artigos, onde integraremos o WhatsApp, seguindo uma estrutura semelhante ao Telegram, mas utilizando uma API de Mensagens em vez da API de bot. Com base na fundação que estabelecemos, essa tarefa será mais fácil, pois agora temos os princípios em mente.


Integração do Telegram no Modelo de Restrição de Tendência

Vamos prosseguir com nosso projeto significativo: neste ponto, estamos praticamente continuando de onde paramos. Com base na minha pesquisa, existem vários métodos para alcançar isso, desde que a lógica do código seja compreendida, mas optei por concentrar-me em um método que envolve o uso de um script em Python e a função ShellExecuteW para facilitar a interação entre um indicador MetaTrader 5 e o telebot para Telegram. Vou me aprofundar mais nesses aspectos. Essa abordagem oferece uma vantagem por ser relativamente direta para aqueles familiarizados com Python e MQL5. Não exige modificações extensas no indicador MQL5. A única desvantagem é que requer dependências externas, como o script Python e bibliotecas.

O objetivo é melhorar a acessibilidade do sinal do MetaTrader 5 no Telegram para um público mais amplo e facilitar o compartilhamento e o encaminhamento fácil através da implementação de telebots.

Em um artigo anterior (Parte I), discuti o método de integração que utiliza a função WebRequest. No entanto, ao revisar a documentação do MQL5, verificou-se que este método não é ideal para integrar indicadores, mas funciona bem com robôs. 

É preciso ter cautela ao usar arquivos DLL de shell, pois eles podem representar sérios riscos quando utilizados com aplicativos não confiáveis. É crucial compreender totalmente e confiar na funcionalidade desses sistemas no seu computador para evitar ataques e invasões.


Compreendendo a API do BOT do Telegram

Presumo que você já seja um usuário ativo do Telegram. O projeto requer mais personalização e privacidade, incluindo detalhes que manterei para mim. Acompanhe para criar seu bot do Telegram com um nome único, como eu fiz usando @Botfather. Nomeei o bot como Trend Constraint, com o nome de usuário @trend_constraint_bot. Você pode criar seu próprio bot de maneira semelhante com um nome único. Aqui está uma visão geral de como começar no Botfather. Siga as instruções do Botfather para concluir o processo. Depois de concluído, você receberá um token do bot para acessar a API do bot. Em seguida, inicie uma conversa com o bot, adicione-o a um grupo ou canal para começar a conversar. Cada chat possui um ID exclusivo que o bot usará para interagir com um usuário específico. Este chat também é usado para passar sinais do indicador MetaTrader 5 para um usuário do Telegram.

Criar um bot do Telegram usando o Botfather

Após configurar tudo, visitei a API do bot usando o navegador Chrome. Lembre-se do token do bot que você recebeu do BotFather. Use o link para a API https://api.telegram.org/bot<seu-token-do-bot>/getUpdates, substituindo o texto destacado pelo seu token do bot para que funcione.

Exemplo típico aqui: https://api.telegram.org/bot9004946256 /getUpdates

Nota: O token da API destacado foi gerado aleatoriamente para fins educacionais. Por favor, use o fornecido pelo @BotFather para o seu robô. Certifique-se de iniciar uma conversa com o bot para que a API exiba o que queremos. Também atualize a aba do navegador da API para que as novas atualizações de chat possam aparecer. Marque a caixa de seleção de formatação no navegador da API para ter um layout fácil de seguir.

A API exibirá um código JSON para a comunicação em andamento com o bot. Primeiro, vamos definir JSON (JavaScript Object Notation) como um formato de intercâmbio de dados leve, fácil para humanos lerem e escreverem e fácil para as máquinas analisarem e gerarem. É comumente usado para transmissão de dados em aplicações web (por exemplo, entre um servidor e um cliente) e para configurar aplicações e armazenamento de dados. É estruturado por Objetos e Arrays.

Abaixo está um JSON da API exibido após enviar a mensagem "hey" para o bot:

{
  "ok": true,
  "result": [
    {
      "update_id": 464310132,
      "message": {
        "message_id": 12,
        "from": {
          "id": 7049213628,
          "is_bot": false,
          "first_name": "Clemence",
          "last_name": "Benjamin",
          "username": "benjc_trade_advisor",
          "language_code": "en"
        },
        "chat": {
          "id": 7049213628,
          "first_name": "Clemence",
          "last_name": "Benjamin",
          "username": "benjc_trade_advisor",
          "type": "private"
        },
        "date": 1719044625,
        "text": "hey"
      }

Aqui está uma explicação detalhada do código JSON

Estrutura de Nível Superior:

  • ok: Este é um valor booleano indicando o sucesso da solicitação da API. Neste caso, é verdadeiro, o que significa que a solicitação foi bem-sucedida.
  • result: Este é um array que contém um ou mais objetos de atualização. Neste exemplo, há um objeto de atualização.

Dentro do Array result

Cada elemento no array result representa uma atualização. Aqui, temos uma única atualização:
  • update_id: Este é um identificador exclusivo para a atualização. Ele ajuda a rastrear atualizações e garantir que nenhuma seja perdida ou processada várias vezes. Neste caso, o update_id é 
  • 464310132.

O Objeto message

Este objeto contém informações sobre a mensagem que acionou a atualização:

  • message_id: Este é um identificador exclusivo para a mensagem dentro do chat. Aqui está.
  • from: Este objeto contém informações sobre o remetente da mensagem:
  • id: O identificador exclusivo para o usuário que enviou a mensagem. Aqui, é 7049213628.
  • is_bot: Este valor booleano indica se o remetente é um bot. Neste caso, é falso, o que significa que o remetente é um humano.
  • first_name: O primeiro nome do remetente, que é Clemence.
  • last_name: O sobrenome do remetente, que é Benjamin.
  • language_code: O código de idioma representando as configurações de idioma do remetente. Aqui, é "en" para inglês.
  • chat: Este objeto contém informações sobre o chat onde a mensagem foi enviada.
  • id: O identificador exclusivo para o chat. Como este é um chat privado, ele corresponde ao ID do usuário ( 7049213628 ).
  • first_name: O primeiro nome do participante do chat, que é Clemence.
  • last_name: O sobrenome do participante do chat, que é Benjamin.
  • type: O tipo de chat. Aqui, é privado, indicando um chat individual entre o usuário e o bot.
  • date: A data e hora em que a mensagem foi enviada, representada como um timestamp Unix (segundos desde 1º de janeiro de 1970). Neste caso, o timestamp é 1719044625.
  • text: O conteúdo textual real da mensagem, que é "hey".

Decidi deixar de lado a seção de chat do JSON para que possamos focar facilmente na parte mais importante, que é o ID do chat, que precisaremos em nosso programa indicador. Confira o trecho JSON abaixo:

"chat": {
          "id": 7049213628,
          "first_name": "Clemence",
          "last_name": "Benjamin",
          "username": "benjc_trade_advisor",
          "type": "private"
        }

O objeto "chat" fornece informações detalhadas sobre o chat onde a mensagem foi enviada, incluindo o identificador exclusivo para o chat, os nomes do participante e o tipo de chat. Neste caso, especifica um chat privado envolvendo um usuário chamado Clemence Benjamin. Vamos observar uma explicação detalhada do Objeto "chat":

id:

  • Descrição: Este é o identificador exclusivo para o chat.
  • Valor: 7049213628
  • Significado: No contexto de chats privados, este ID geralmente corresponde ao ID do usuário participante do chat.

first_name:

  • Descrição: O primeiro nome do participante no chat.
  • Valor: Clemence
  • Significado: Isso ajuda a identificar o usuário pelo primeiro nome no chat.

last_name:

  • Descrição: O sobrenome do participante no chat.
  • Valor: Benjamin
  • Significado: Isso complementa o primeiro nome para identificar completamente o usuário no chat.

"username"

  • Descrição: É a chave, 
  • Valor: "benjc_trade_advisor" 
  • Significado: Esta linha indica que o nome de usuário associado ao objeto (como um usuário, bot ou chat) é "benjc_trade_advisor". Esse nome de usuário é tipicamente usado para identificar a entidade em um formato reconhecível em aplicativos ou sistemas que usam os dados JSON.

type:

  • Descrição: O tipo de chat.
  • Valor: privado
  • Significado: Indica que este chat é uma conversa individual entre o usuário e o bot (em oposição a um chat em grupo ou canal).

Resumo:

O objetivo da seção mencionada anteriormente foi desenvolver um bot do Telegram funcional e adquirir o token do bot e o ID do chat, ambos elementos cruciais para o projeto principal. Exploramos a API para um entendimento mais profundo de cada componente. Com o token do bot e o ID do Chat, podemos avançar no processo de integração, onde também exploraremos várias linguagens de programação.

Instalando módulos Python do Telegram no Windows

Precisamos ter o Python instalado no seu computador. Certifique-se de que seu computador está conectado à internet. Você pode baixá-lo em Python.org. Fiz este guia praticamente em um computador Windows. Pode ser uma abordagem diferente em Mac, Linux e outras estruturas. Após instalar o Python, o próximo passo é instalar também os módulos da API do Telegram para Python, que permitirão que os scripts em Python para o Telegram funcionem bem. Abra o cmd.exe (prompt de comando do Windows) e execute o trecho de código abaixo. Copie o código e cole no prompt de comando do Windows. Pressione Enter no teclado para que o código inicie o download e aguarde um curto tempo até que a instalação do módulo seja concluída.

pip install pyTelegramBotAPI

Uma parte da captura de tela do prompt de comando é mostrada abaixo.

Prompt de comando do Windows para instalar os módulos da API do Telegram para Python

Quando concluído, você pode fechar a janela e reiniciar o computador.

Após concluir esta etapa, seu computador está agora bem-preparado para executar os scripts Python para interação com a API do Bot do Telegram. Na próxima etapa, examinaremos o código de perto para configurar nosso sistema para a tarefa.


Compreendendo o script Python send_telegram_messages

Vamos observar a construção do script. E, então, darei o código final em um único trecho. O arquivo deve ser nomeado como send_telegram_message.py

O script começa importando os módulos necessários da seguinte forma:

  • import telebot: Importa o módulo telebot, que fornece as funções necessárias para interagir com a API do Bot do Telegram.
  • import sys: Importa o módulo sys, que permite ao script usar argumentos de linha de comando.

import telebot
import sys

    Passamos então a declarar o API_TOKEN e o ID do Chat:

    • API_TOKEN: Esta variável armazena o token de API do bot, usado para autenticar o bot com os servidores do Telegram.
    • CHAT_ID: Um valor único de identidade de cada chat entre o usuário e o telebot ou, canal e grupos.
    Certifique-se de que os valores para API_TOKEN e CHAT_ID estão entre aspas simples, conforme mostrado abaixo.

    API_TOKEN = '9004946256:shTUYuq52f8CHLt8BLdYGHYJi2QM6H3donA' #Replace the API TOKEN with your bot tokrn from @BotFather
    CHAT_ID = '7049213628'  #Replace the ID with your actual Chat ID from the telebot API
    


      Precisamos inicializar o objeto TeleBot com o token da API fornecido, para permitir a interação com a API do Bot do Telegram.

      bot = telebot.TeleBot(API_TOKEN)
      


      O próximo trecho de código Python define uma função para enviar uma mensagem via Telegram, tratando as exceções de erros associadas à API e ao sistema.

      def send_telegram_message(message):
          try:
              bot.send_message(CHAT_ID, message)
              print("Message sent successfully!")
          except telebot.apihelper.ApiTelegramException as e:
              print(f"Failed to send message: {e}")
          except Exception as e:
              print(f"An error occurred: {e}")


      A parte final do código é uma condição que garante que esse bloco de código seja executado apenas se o script for executado diretamente, e não se for importado como um módulo. Ele recupera a mensagem dos argumentos da linha de comando ou define uma mensagem padrão se nenhum argumento for fornecido.

      if __name__ == "__main__":
          message = sys.argv[1] if len(sys.argv) > 1 else "Test message"
          send_telegram_message(message)
      


      Resumindo tudo, temos nosso código final, salve o arquivo como send_telegram_message.py na sua pasta de scripts Python. Achei o acesso à pasta de scripts funcionando bem.

      import telebot
      import sys
      
      API_TOKEN = '9004946256:shTUYuq52f8CHLt8BLdYGHYJi2QM6H3donA'#Replace with your API_TOKEN given by BotFather
      CHAT_ID = '7049213628' #Replace with your CHAT_ID
      
      bot = telebot.TeleBot(API_TOKEN)
      
      def send_telegram_message(message):
          try:
              bot.send_message(CHAT_ID, message)
              print("Message sent successfully!")
          except telebot.apihelper.ApiTelegramException as e:
              print(f"Failed to send message: {e}")
          except Exception as e:
              print(f"An error occurred: {e}")
      
      if __name__ == "__main__":
         message = sys.argv[1] if len(sys.argv) > 1 else "Test message"
          send_telegram_message(message)
      

      O próximo passo importante é configurar o Indicador MQL5 para chamar o Script Python.


      Configurando o Indicador de Restrição de Tendência para Telegram

      Aqui precisamos modificar a função myAlert no indicador MQL5 para chamar o script Python usando a função ShellExecuteW. Essa função executará o script Python e passará a mensagem de alerta como um argumento.

      Aqui está como modificamos isso no Trend Constraint. Incluí dois trechos de código antes e depois da modificação:

      Antes da Modificação:

      void myAlert(string type, string message)
        {
         if(type == "print")
            Print(message);
         else if(type == "error")
           {
            Print(type+" | Trend Constraint V1.05 @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
           }
         else if(type == "order")
           {
           }
         else if(type == "modify")
           {
           }
         else if(type == "indicator")
           {
            if(Audible_Alerts) Alert(type+" | Trend Constraint V1.05 @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
            if(Push_Notifications) SendNotification(type+" | Trend Constraint V1.05 @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
           }
        }

      Depois da modificação:

      //--- ShellExecuteW declaration ----------------------------------------------
      #import "shell32.dll"
      int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
      #import
      
      
      datetime last_alert_time;
      input int alert_cooldown_seconds = 60; // Cooldown period in seconds, this helps to avoid instant continuous alerting depending on indicator conditions
      
      //Modify the myAlert Function for telegram notification
      void myAlert(string type, string message) {
          datetime current_time = TimeCurrent();
          if (current_time - last_alert_time < alert_cooldown_seconds) {
              // Skip alert if within cooldown period
              return;
          }
      
          last_alert_time = current_time;
          string full_message = type + " | Trend Constraint V1.04 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message;
          if (type == "print") {
              Print(message);
          } else if (type == "error") {
              Print(type + " | Trend Constraint V1.04 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message);
          } else if (type == "order") {
              // Add order alert handling if needed
          } else if (type == "modify") {
              // Add modify alert handling if needed
          } else if (type == "indicator") {
              if (Audible_Alerts) {
                  Alert(full_message);
              }
              if (Push_Notifications) {
                  SendNotification(full_message);
              }
      
              // Send to Telegram
              string python_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
              string script_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py";
              string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
              
              Print("Executing command to send Telegram message: ", command);
      
              // Use cmd.exe to execute the command and then wait for 5 seconds
              string final_command = "/c " + command + " && timeout 5";
              int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1);
              if (result <= 32) {
                  int error_code = GetLastError();
                  Print("Failed to execute Python script. Error code: ", error_code);
              } else {
                  Print("Successfully executed Python script. Result code: ", result);
              }
          }
      }
      
      
      //--- End of Telegram Integration functions ---------------------------------------

      Explicarei brevemente o que a modificação está fazendo.

      Primeiro, precisamos importar a função ShellExecuteW da biblioteca do Windows shell32.dll, permitindo que o programa MQL5 execute comandos externos, neste caso, executando o script send_telegram_message.py. O programa não funcionará sem declarar a função ShellExecuteW. Também implementamos um cooldown para restringir a execução contínua instantânea do cmd.exe devido a algumas condições configuradas incorretamente do indicador. No meu caso, como mencionei no artigo anterior, havia uma condição de alerta no Buffer 5 e 6 do Trend Constraint V1.04 que causava múltiplos alertas de sinal em um curto período. O resultado foi pior quando integrei o recurso do Telegram, o cmd.exe foi lançado várias vezes instantaneamente e o computador travou. Para aliviar, permiti apenas que o indicador desenhasse sem o myAlert(). Em outras palavras, silenciei-o transformando-o em comentários, como você sabe, comentários não são executados no programa.

      //--- ShellExecuteW declaration ----------------------------------------------
      #import "shell32.dll"
      int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
      #import
      


      A outra parte crucial do código é:

      // Send to Telegram
      string python_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; 
      string script_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py";
      string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
      

      O código acima constrói um comando para executar um script Python a partir de um programa de indicador do MetaTrader 5, especificando os caminhos para o interpretador Python e o script, bem como a mensagem a ser enviada. Para o texto destacado, você precisa substituir pelo seu próprio caminho, dependendo do seu computador. Isso eu dei como exemplo do meu computador.


      Código final

      Tendo explicado tudo e integrado com sucesso, agora temos um novo recurso e atualizamos para Trend Constraint V1.05

      //+------------------------------------------------------------------+
      //|                                       Trend Constraint V1.05.mq5 |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com"
      #property version   "1.05"
      #property description "A model that seeks to produce sell signals when D1 candle is Bearish only and buy signals when it is Bullish"
      
      
      //--- indicator settings
      #property indicator_chart_window
      #property indicator_buffers 6
      #property indicator_plots 6
      
      #property indicator_type1 DRAW_ARROW
      #property indicator_width1 5
      #property indicator_color1 0xFF3C00
      #property indicator_label1 "Buy"
      
      #property indicator_type2 DRAW_ARROW
      #property indicator_width2 5
      #property indicator_color2 0x0000FF
      #property indicator_label2 "Sell"
      
      #property indicator_type3 DRAW_ARROW
      #property indicator_width3 2
      #property indicator_color3 0xE8351A
      #property indicator_label3 "Buy Reversal"
      
      #property indicator_type4 DRAW_ARROW
      #property indicator_width4 2
      #property indicator_color4 0x1A1AE8
      #property indicator_label4 "Sell Reversal"
      
      #property indicator_type5 DRAW_LINE
      #property indicator_style5 STYLE_SOLID
      #property indicator_width5 2
      #property indicator_color5 0xFFAA00
      #property indicator_label5 "Buy"
      
      #property indicator_type6 DRAW_LINE
      #property indicator_style6 STYLE_SOLID
      #property indicator_width6 2
      #property indicator_color6 0x0000FF
      #property indicator_label6 "Sell"
      
      #define PLOT_MAXIMUM_BARS_BACK 5000
      #define OMIT_OLDEST_BARS 50
      
      //--- indicator buffers
      double Buffer1[];
      double Buffer2[];
      double Buffer3[];
      double Buffer4[];
      double Buffer5[];
      double Buffer6[];
      
      input double Oversold = 30;
      input double Overbought = 70;
      input int Slow_MA_period = 200;
      input int Fast_MA_period = 100;
      datetime time_alert; //used when sending alert
      input bool Audible_Alerts = true;
      input bool Push_Notifications = true;
      double myPoint; //initialized in OnInit
      int RSI_handle;
      double RSI[];
      double Open[];
      double Close[];
      int MA_handle;
      double MA[];
      int MA_handle2;
      double MA2[];
      int MA_handle3;
      double MA3[];
      int MA_handle4;
      double MA4[];
      double Low[];
      double High[];
      int MA_handle5;
      double MA5[];
      int MA_handle6;
      double MA6[];
      int MA_handle7;
      double MA7[];
      
      //--- ShellExecuteW declaration ----------------------------------------------
      #import "shell32.dll"
      int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
      #import
      
      //--- functions for telegram integration -----------------------------------------------
      datetime last_alert_time;
      input int alert_cooldown_seconds = 60; // Cooldown period in seconds
      
      void myAlert(string type, string message) {
          datetime current_time = TimeCurrent();
          if (current_time - last_alert_time < alert_cooldown_seconds) {
              // Skip alert if within cooldown period
              return;
          }
      
          last_alert_time = current_time;
          string full_message = type + " | Trend Constraint V1.05 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message;
          if (type == "print") {
              Print(message);
          } else if (type == "error") {
              Print(type + " | Trend Constraint V1.05 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message);
          } else if (type == "order") {
              // Add order alert handling if needed
          } else if (type == "modify") {
              // Add modify alert handling if needed
          } else if (type == "indicator") {
              if (Audible_Alerts) {
                  Alert(full_message);
              }
              if (Push_Notifications) {
                  SendNotification(full_message);
              }
      
              // Send to Telegram //Remember to replace the storages path with your actual path.
              string python_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
              string script_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py";
              string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
              
              // Debugging: Print the command being executed
              Print("Executing command to send Telegram message: ", command);
      
              // Use cmd.exe to execute the command and then wait for 5 seconds
              string final_command = "/c " + command + " && timeout 5";
              int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1);
              if (result <= 32) {
                  int error_code = GetLastError();
                  Print("Failed to execute Python script. Error code: ", error_code);
              } else {
                  Print("Successfully executed Python script. Result code: ", result);
              }
          }
      }
      
      //+------------------------------------------------------------------+
      //| Custom indicator initialization function                         |
      //+------------------------------------------------------------------+
      int OnInit()
        {   
         SetIndexBuffer(0, Buffer1);
         PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
         PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
         PlotIndexSetInteger(0, PLOT_ARROW, 241);
         SetIndexBuffer(1, Buffer2);
         PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
         PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
         PlotIndexSetInteger(1, PLOT_ARROW, 242);
         SetIndexBuffer(2, Buffer3);
         PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
         PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
         PlotIndexSetInteger(2, PLOT_ARROW, 236);
         SetIndexBuffer(3, Buffer4);
         PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
         PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
         PlotIndexSetInteger(3, PLOT_ARROW, 238);
         SetIndexBuffer(4, Buffer5);
         PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE);
         PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
         SetIndexBuffer(5, Buffer6);
         PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
         PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
         //initialize myPoint
         myPoint = Point();
         if(Digits() == 5 || Digits() == 3)
           {
            myPoint *= 10;
           }
         RSI_handle = iRSI(NULL, PERIOD_CURRENT, 14, PRICE_CLOSE);
         if(RSI_handle < 0)
           {
            Print("The creation of iRSI has failed: RSI_handle=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle = iMA(NULL, PERIOD_CURRENT, 7, 0, MODE_SMMA, PRICE_CLOSE);
         if(MA_handle < 0)
           {
            Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle2 = iMA(NULL, PERIOD_CURRENT, 400, 0, MODE_SMA, PRICE_CLOSE);
         if(MA_handle2 < 0)
           {
            Print("The creation of iMA has failed: MA_handle2=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle3 = iMA(NULL, PERIOD_CURRENT, 100, 0, MODE_EMA, PRICE_CLOSE);
         if(MA_handle3 < 0)
           {
            Print("The creation of iMA has failed: MA_handle3=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle4 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE);
         if(MA_handle4 < 0)
           {
            Print("The creation of iMA has failed: MA_handle4=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle5 = iMA(NULL, PERIOD_CURRENT, Fast_MA_period, 0, MODE_SMA, PRICE_CLOSE);
         if(MA_handle5 < 0)
           {
            Print("The creation of iMA has failed: MA_handle5=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle6 = iMA(NULL, PERIOD_CURRENT, Slow_MA_period, 0, MODE_SMA, PRICE_CLOSE);
         if(MA_handle6 < 0)
           {
            Print("The creation of iMA has failed: MA_handle6=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         MA_handle7 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);
         if(MA_handle7 < 0)
           {
            Print("The creation of iMA has failed: MA_handle7=", INVALID_HANDLE);
            Print("Runtime error = ", GetLastError());
            return(INIT_FAILED);
           }
         
         return(INIT_SUCCEEDED);
        }
      
      //+------------------------------------------------------------------+
      //| Custom indicator iteration function                              |
      //+------------------------------------------------------------------+
      int OnCalculate(const int rates_total,
                      const int prev_calculated,
                      const datetime& time[],
                      const double& open[],
                      const double& high[],
                      const double& low[],
                      const double& close[],
                      const long& tick_volume[],
                      const long& volume[],
                      const int& spread[])
        {
         int limit = rates_total - prev_calculated;
         //--- counting from 0 to rates_total
         ArraySetAsSeries(Buffer1, true);
         ArraySetAsSeries(Buffer2, true);
         ArraySetAsSeries(Buffer3, true);
         ArraySetAsSeries(Buffer4, true);
         ArraySetAsSeries(Buffer5, true);
         ArraySetAsSeries(Buffer6, true);
         //--- initial zero
         if(prev_calculated < 1)
           {
            ArrayInitialize(Buffer1, EMPTY_VALUE);
            ArrayInitialize(Buffer2, EMPTY_VALUE);
            ArrayInitialize(Buffer3, EMPTY_VALUE);
            ArrayInitialize(Buffer4, EMPTY_VALUE);
            ArrayInitialize(Buffer5, EMPTY_VALUE);
            ArrayInitialize(Buffer6, EMPTY_VALUE);
           }
         else
            limit++;
         datetime Time[];
         
         datetime TimeShift[];
         if(CopyTime(Symbol(), PERIOD_CURRENT, 0, rates_total, TimeShift) <= 0) return(rates_total);
         ArraySetAsSeries(TimeShift, true);
         int barshift_M1[];
         ArrayResize(barshift_M1, rates_total);
         int barshift_D1[];
         ArrayResize(barshift_D1, rates_total);
         for(int i = 0; i < rates_total; i++)
           {
            barshift_M1[i] = iBarShift(Symbol(), PERIOD_M1, TimeShift[i]);
            barshift_D1[i] = iBarShift(Symbol(), PERIOD_D1, TimeShift[i]);
         }
         if(BarsCalculated(RSI_handle) <= 0) 
            return(0);
         if(CopyBuffer(RSI_handle, 0, 0, rates_total, RSI) <= 0) return(rates_total);
         ArraySetAsSeries(RSI, true);
         if(CopyOpen(Symbol(), PERIOD_M1, 0, rates_total, Open) <= 0) return(rates_total);
         ArraySetAsSeries(Open, true);
         if(CopyClose(Symbol(), PERIOD_D1, 0, rates_total, Close) <= 0) return(rates_total);
         ArraySetAsSeries(Close, true);
         if(BarsCalculated(MA_handle) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle, 0, 0, rates_total, MA) <= 0) return(rates_total);
         ArraySetAsSeries(MA, true);
         if(BarsCalculated(MA_handle2) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle2, 0, 0, rates_total, MA2) <= 0) return(rates_total);
         ArraySetAsSeries(MA2, true);
         if(BarsCalculated(MA_handle3) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle3, 0, 0, rates_total, MA3) <= 0) return(rates_total);
         ArraySetAsSeries(MA3, true);
         if(BarsCalculated(MA_handle4) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle4, 0, 0, rates_total, MA4) <= 0) return(rates_total);
         ArraySetAsSeries(MA4, true);
         if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total);
         ArraySetAsSeries(Low, true);
         if(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total);
         ArraySetAsSeries(High, true);
         if(BarsCalculated(MA_handle5) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle5, 0, 0, rates_total, MA5) <= 0) return(rates_total);
         ArraySetAsSeries(MA5, true);
         if(BarsCalculated(MA_handle6) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle6, 0, 0, rates_total, MA6) <= 0) return(rates_total);
         ArraySetAsSeries(MA6, true);
         if(BarsCalculated(MA_handle7) <= 0) 
            return(0);
         if(CopyBuffer(MA_handle7, 0, 0, rates_total, MA7) <= 0) return(rates_total);
         ArraySetAsSeries(MA7, true);
         if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total);
         ArraySetAsSeries(Time, true);
         //--- main loop
         for(int i = limit-1; i >= 0; i--)
           {
            if (i >= MathMin(PLOT_MAXIMUM_BARS_BACK-1, rates_total-1-OMIT_OLDEST_BARS)) continue; //omit some old rates to prevent "Array out of range" or slow calculation   
            
            if(barshift_M1[i] < 0 || barshift_M1[i] >= rates_total) continue;
            if(barshift_D1[i] < 0 || barshift_D1[i] >= rates_total) continue;
            
            //Indicator Buffer 1
            if(RSI[i] < Oversold
            && RSI[i+1] > Oversold //Relative Strength Index crosses below fixed value
            && Open[barshift_M1[i]] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close
            && MA[i] > MA2[i] //Moving Average > Moving Average
            && MA3[i] > MA4[i] //Moving Average > Moving Average
            )
              {
               Buffer1[i] = Low[1+i]; //Set indicator value at Candlestick Low
               if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
               time_alert = Time[1];
              }
            else
              {
               Buffer1[i] = EMPTY_VALUE;
              }
            //Indicator Buffer 2
            if(RSI[i] > Overbought
            && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value
            && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
            && MA[i] < MA2[i] //Moving Average < Moving Average
            && MA3[i] < MA4[i] //Moving Average < Moving Average
            )
              {
               Buffer2[i] = High[1+i]; //Set indicator value at Candlestick High
               if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
               time_alert = Time[1];
              }
            else
              {
               Buffer2[i] = EMPTY_VALUE;
              }
            //Indicator Buffer 3
            if(MA5[i] > MA6[i]
            && MA5[i+1] < MA6[i+1] //Moving Average crosses above Moving Average
            )
              {
               Buffer3[i] = Low[i]; //Set indicator value at Candlestick Low
               if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy Reversal"); //Alert on next bar open
               time_alert = Time[1];
              }
            else
              {
               Buffer3[i] = EMPTY_VALUE;
              }
            //Indicator Buffer 4
            if(MA5[i] < MA6[i]
            && MA5[i+1] > MA6[i+1] //Moving Average crosses below Moving Average
            )
              {
               Buffer4[i] = High[i]; //Set indicator value at Candlestick High
               if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Reversal"); //Alert on next bar open
               time_alert = Time[1];
              }
            else
              {
               Buffer4[i] = EMPTY_VALUE;
              }
            //Indicator Buffer 5, Alert muted by turning it into a comment
            if(MA3[i] > MA7[i] //Moving Average > Moving Average
            )
              {
               Buffer5[i] = MA3[i]; //Set indicator value at Moving Average
               //if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
               //time_alert = Time[1];
              }
            else
              {
               Buffer5[i] = EMPTY_VALUE;
              }
            //Indicator Buffer 6, Alert muted by turning it into a comment
            if(MA3[i] < MA7[i] //Moving Average < Moving Average
            )
              {
               Buffer6[i] = MA3[i]; //Set indicator value at Moving Average
               //if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
               //time_alert = Time[1];
              }
            else
              {
               Buffer6[i] = EMPTY_VALUE;
              }
           }
         return(rates_total);
        }
      //+------------------------------------------------------------------+


      Tratamento de Erros

      Falha ao enviar mensagem: Uma solicitação para a API do Telegram não foi bem-sucedida. Código de erro: 401. Descrição: Não autorizado

      De acordo com meus testes, o código de erro acima se deve a um API_TOKEN disfuncional, como o que usamos anteriormente como exemplo. Você precisa usar um valor de API_TOKEN funcional. Apaguei a maioria dos erros para chegar a um código limpo e funcional para este guia. No entanto, você pode cometer erros ao editar ou alterar seu código, então você precisa verificar cuidadosamente cada etapa que tomar.


      Resultados dos Testes

      Após adicionar o indicador ao gráfico, habilitei a opção de permitir DLL para permitir que nosso indicador execute scripts via prompt de comando. A imagem animada demonstra como o indicador é adicionado e sua aparência no gráfico.

        lançamento do indicador


      Você pode testar se o script está funcionando executando o arquivo no seu caminho via prompt de comando, veja a imagem abaixo. Com o Prompt de Comando aberto na pasta com seu script, digite python send_telegram_message.py. Uma mensagem enviada com sucesso, indicando que o script está funcionando, e a mensagem de teste também foi encaminhada para o chat.

      mensagem de teste cmd


      A imagem de resultado abaixo mostra o início de uma conversa com o bot, que nos permitiu obter o ID do Chat na API do bot. Ela também mostra o sinal recebido enviado pelo bot do Trend Constraint V1.05. Os sinais chegaram imediatamente à medida que foram gerados na plataforma MetaTrader 5.

      Chat do Telegram com o telebot Trend Constraint


      Conclusão

      Integrámos com sucesso o Telegram em nosso modelo. O Trend Constraint V1.05 avançou significativamente, agora capaz de passar sinais internamente e externamente, beneficiando traders em todo o mundo com acesso ao Telegram. A transmissão de sinais é rápida, sem atrasos devido à execução eficiente do algoritmo. O sistema é dedicado a um indicador específico dentro da plataforma, garantindo que não haja interferência com outras funções. Os sinais são transmitidos com segurança diretamente para um ID especificado. Esses sistemas podem ser hospedados em um Servidor Privado Virtual para operação contínua, fornecendo aos usuários um fornecimento estável de sinais. Tais projetos podem encontrar erros durante o desenvolvimento, mas estou satisfeito por tê-los resolvido com sucesso.

      Espero que este projeto tenha inspirado você de alguma forma. Se você estiver trabalhando em um projeto e encontrar desafios com este tipo de integração, sinta-se à vontade para compartilhar suas ideias na seção de discussão abaixo. Anexei os arquivos-fonte que você pode modificar para seus projetos e explorar algumas ideias com a ajuda dos comentários fornecidos para fins educacionais. Em seguida, planejamos integrar outra plataforma social popular, o WhatsApp.


      Anexos: Descrição
      send_telegram_message.py O script para permitir que o indicador passe notificações para o Telegram contém o API_Token e o Chat ID
      Trend Constraint V1.05.mq5 O código-fonte principal do programa indicador MQL5
      Telebot_API.txt Estrutura da API do Bot do Telegram


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

      Otimização Automatizada de Parâmetros para Estratégias de Trading Usando Python e MQL5 Otimização Automatizada de Parâmetros para Estratégias de Trading Usando Python e MQL5
      Existem vários tipos de algoritmos para auto-otimização de estratégias de trading e parâmetros. Esses algoritmos são usados para melhorar automaticamente as estratégias de trading com base em dados históricos e atuais de mercado. Neste artigo, veremos um desses algoritmos com exemplos em Python e MQL5.
      Visualizações de negociações no gráfico (Parte 2): Desenho gráfico de informações Visualizações de negociações no gráfico (Parte 2): Desenho gráfico de informações
      Escreveremos do zero um script para facilitar a captura de capturas de tela (print-screens) de negociações, visando a análise de entradas. Em um único gráfico, será conveniente exibir todas as informações necessárias sobre uma negociação específica, com a possibilidade de desenhar diferentes timeframes.
      Funcionalidades do Assistente MQL5 que você precisa conhecer (Parte 16): Método de componentes principais com autovetores Funcionalidades do Assistente MQL5 que você precisa conhecer (Parte 16): Método de componentes principais com autovetores
      Este artigo discute o método de componentes principais, um método de redução da dimensionalidade ao analisar dados, e como ele pode ser implementado usando autovalores e vetores. Como sempre, vamos tentar desenvolver um protótipo da classe de sinais para EA que pode ser usado no Assistente MQL5.
      Dominando a Dinâmica do Mercado: Criando um Expert Advisor (EA) para Estratégia de Suporte e Resistência Dominando a Dinâmica do Mercado: Criando um Expert Advisor (EA) para Estratégia de Suporte e Resistência
      Um guia abrangente para desenvolver um algoritmo de negociação automatizado baseado na estratégia de Suporte e Resistência. Informações detalhadas sobre todos os aspectos da criação de um expert advisor em MQL5 e testá-lo no MetaTrader 5 – desde a análise dos comportamentos de faixa de preço até o gerenciamento de risco.