"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Guia Completo: Construindo um Bot Seguro de Integração entre Telegram e MetaTraderm Python

Guia Completo: Construindo um Bot Seguro de Integração entre Telegram e MetaTraderm Python

Publicado em 2024-11-08
Navegar:490

Guia Completo: Construindo um Bot Seguro de Integração entre Telegram e MetaTraderm Python

Bem-vindo! Este guia é projetado desenvolvedores intusiastas em Python que desejam criar um bot que monitora mensagens no Telegram e interage com o MetaTrader5 (MT5) para executar ordens de negociação com base nos sinais recebidos. Vamos guiá-lo por todo o processo, desde a configuração do ambiente até a compreensão detalhada de cada parte do código.


Índice

  1. Introdução
  2. Pré-requisitos
  3. Configurando o Ambiente
    • 1. Criar um Diretório para o Projeto
    • 2. Configurar um Ambiente Virtual (Opcional, mas Recomendado)
    • 3. Instalar Dependências Necessárias
  4. Criando o Arquivo .env
  5. Entendendo o Script Python
    • Imports e Variáveis de Ambiente
    • Configuração de Logging
    • Configuração das Contas MT5
    • Função de Reconexão com MT5
    • Função para Enviar Ordens para MT5
    • Processando Sinais do Telegram
    • Monitoramento de Conexões
    • Manipulação de Sinais para Encerramento Suave
    • Função Principal (main)
  6. Executando o Script
  7. Boas Práticas e Dicas
  8. Conclusão

Introdução

Neste projeto, você criará um bot em Python que realiza as seguintes tarefas:

  1. Monitora um Grupo no Telegram: O bot monitora mensagens em um grupo específico do Telegram em busca de sinais de negociação (por exemplo, "buy US30").

  2. Processa Sinais: Quando um sinal é detectado, o bot interpreta para determinar qual ativo negociar e se deve comprar ou vender.

  3. Interage com o MetaTrader5 (MT5): Com base no sinal, o bot envia ordens de negociação para sua conta MT5.

  4. Mantém Práticas de Segurança: Informações sensíveis, como chaves de API e senhas, são gerenciadas de forma segura usando variáveis de ambiente.

Ao final deste guia, você terá um bot funcional e seguro que automatiza ações de negociação com base em mensagens do Telegram.


Pré-requisitos

Antes de começar, certifique-se de ter o seguinte:

  1. Conhecimento Básico de Python: Familiaridade com a sintaxe do Python e conceitos como funções, loops e módulos.

  2. Python Instalado: Verifique se você tem o Python 3.7 ou posterior instalado em seu computador. Você pode baixá-lo do site oficial do Python.

  3. Conta no Telegram: Você precisará acessar a API do Telegram. Registre sua aplicação aqui para obter o API_ID e o API_HASH.

  4. Conta no MetaTrader5: Acesse uma conta MT5, incluindo credenciais de login e informações do servidor.

  5. Entendimento de Ambientes Virtuais: Embora opcional, usar ambientes virtuais é recomendado para gerenciar dependências.


Configurando o Ambiente

1. Criar um Diretório para o Projeto

Escolha um local em seu computador e crie um novo diretório para o seu projeto. Por exemplo:

mkdir telegram_mt5_bot
cd telegram_mt5_bot

2. Configurar um Ambiente Virtual (Opcional, mas Recomendado)

Um ambiente virtual isola as dependências do seu projeto das de outros projetos Python em seu sistema.

  • Criar um Ambiente Virtual:

    python -m venv venv
    

    Este comando cria um ambiente virtual chamado venv dentro do diretório do seu projeto.

  • Ativar o Ambiente Virtual:

    • No Windows:

      venv\Scripts\activate
      
    • No macOS e Linux:

      source venv/bin/activate
      

    Após a ativação, o nome do ambiente virtual aparecerá no prompt do terminal, indicando que as dependências instaladas agora serão isoladas para este projeto.

3. Instalar Dependências Necessárias

Com o ambiente virtual ativado, instale as bibliotecas necessárias usando o pip:

pip install telethon MetaTrader5 python-dotenv
  • telethon: Biblioteca para interagir com a API do Telegram.
  • MetaTrader5: Biblioteca para interagir com o MetaTrader5.
  • python-dotenv: Biblioteca para carregar variáveis de ambiente a partir de um arquivo .env.

Além disso, para facilitar o gerenciamento de pacotes, é recomendável criar um arquivo requirements.txt:

pip freeze > requirements.txt

Este arquivo registra todas as dependências do projeto, permitindo que outras pessoas (ou você mesmo em outra máquina) instalem facilmente as mesmas versões com:

pip install -r requirements.txt

Criando o Arquivo .env

Para gerenciar informações sensíveis de forma segura, usaremos um arquivo .env que armazenará variáveis de ambiente. Este arquivo não deve ser versionado ou compartilhado.

1. Criar o Arquivo .env

No diretório raiz do seu projeto (telegram_mt5_bot), crie um arquivo chamado .env e adicione as seguintes linhas, substituindo os valores pelos seus próprios:

# Credenciais do Telegram
TELEGRAM_API_ID=seu_api_id
TELEGRAM_API_HASH=seu_api_hash
TELEGRAM_PHONE_NUMBER=seu_numero_de_telefone
TELEGRAM_GROUP_USERNAME=@superus30

# Credenciais do MetaTrader5 (MT5)
MT5_LOGIN=seu_login_mt5
MT5_PASSWORD=sua_senha_mt5
MT5_SERVER=seu_servidor_mt5
MT5_PATH=C:\Caminho\Para\Seu\MetaTrader5\terminal64.exe

# Configuração de Logging
LOG_FILE=app.log

Notas Importantes:

  • TELEGRAM_API_ID e TELEGRAM_API_HASH: Obtidos ao registrar sua aplicação no Telegram.
  • TELEGRAM_PHONE_NUMBER: Número de telefone associado à sua conta do Telegram.
  • TELEGRAM_GROUP_USERNAME: Nome de usuário do grupo Telegram que o bot irá monitorar.
  • MT5_LOGIN, MT5_PASSWORD, MT5_SERVER: Informações da sua conta MT5.
  • MT5_PATH: Caminho para o executável do MetaTrader5 no seu sistema.

2. Atualizar o Arquivo .gitignore

Para garantir que o arquivo .env não seja versionado (especialmente se você estiver usando o Git), crie ou atualize o arquivo .gitignore no diretório raiz com o seguinte conteúdo:

# Arquivo de variáveis de ambiente
.env

# Diretório do ambiente virtual
venv/

Isso impede que informações sensíveis sejam acidentalmente compartilhadas em repositórios públicos ou privados.


Entendendo o Script Python

Agora, vamos criar o script Python que integrará o Telegram com o MetaTrader5. Este script realizará as seguintes funções:

  1. Carregar Variáveis de Ambiente: Utiliza o python-dotenv para carregar credenciais e configurações do arquivo .env.

  2. Configurar Logging: Define como e onde os logs serão armazenados, facilitando o monitoramento e a depuração.

  3. Configurar Contas MT5: Define as contas MT5 que serão utilizadas pelo bot.

  4. Funções de Reconexão e Envio de Ordens: Gerencia a conexão com o MT5 e envia ordens de negociação com base nos sinais recebidos.

  5. Processar Sinais do Telegram: Interpreta mensagens do Telegram para determinar as ações de negociação.

  6. Monitorar Conexões: Verifica periodicamente se as conexões com o MT5 estão ativas e tenta reconectar se necessário.

  7. Manipular Sinais de Encerramento: Garante que o bot encerre suas operações de forma suave ao receber sinais de interrupção.

  8. Função Principal (main): Orquestra todas as operações acima, iniciando o cliente Telegram e gerenciando as tarefas assíncronas.

Vamos explorar cada parte do código com detalhes.

1. Imports e Variáveis de Ambiente

Primeiro, importamos todas as bibliotecas necessárias e carregamos as variáveis de ambiente.

import os
import sys
import asyncio
import logging
import signal
import pkg_resources
from datetime import datetime
from dotenv import load_dotenv
from telethon import TelegramClient, events
import MetaTrader5 as mt5

Explicação dos Imports:

  • os: Interage com o sistema operacional, acessando variáveis de ambiente e caminhos de arquivos.
  • sys: Fornece acesso a variáveis e funções que interagem com o interpretador Python.
  • asyncio: Facilita a escrita de código assíncrono, permitindo que múltiplas tarefas sejam executadas concorrentemente.
  • logging: Gerencia o registro de mensagens de log para monitoramento e depuração.
  • signal: Permite a captura de sinais do sistema (como interrupções) para realizar ações específicas.
  • pkg_resources: Gerencia recursos de pacotes Python, aqui utilizado para listar pacotes instalados.
  • datetime: Trabalha com datas e horas.
  • dotenv: Carrega variáveis de ambiente de um arquivo .env.
  • Telethon: Biblioteca para interagir com a API do Telegram de forma assíncrona.
  • MetaTrader5: Biblioteca para interagir com o MetaTrader5.

Carregando Variáveis de Ambiente:

# Carrega variáveis de ambiente do arquivo .env
load_dotenv()

Esta linha lê o arquivo .env e carrega as variáveis de ambiente nele definidas, permitindo que sejam acessadas via os.getenv().

2. Configuração de Logging

O logging é essencial para monitorar o funcionamento do seu bot e depurar problemas. Vamos configurar o logging para registrar mensagens tanto no console quanto em um arquivo.

# Acessa as variáveis de ambiente para configuração
LOG_FILE = os.getenv('LOG_FILE', 'app.log')

# Configura o logging
logging.basicConfig(
    level=logging.INFO,  # Nível mínimo de severidade para registrar
    format='%(asctime)s - %(levelname)s - %(message)s',  # Formato das mensagens de log
    handlers=[
        logging.FileHandler(LOG_FILE),  # Registra logs em um arquivo
        logging.StreamHandler(sys.stdout)  # Exibe logs no console
    ]
)
logger = logging.getLogger(__name__)

Detalhes da Configuração:

  • level: Define o nível mínimo de mensagens que serão registradas. INFO inclui INFO, WARNING, ERROR e CRITICAL.
  • format: Define como cada mensagem de log será formatada, incluindo o timestamp, o nível de severidade e a mensagem.
  • handlers: Define onde as mensagens de log serão enviadas. Aqui, estão sendo enviadas para um arquivo (app.log) e para o console.

3. Configuração das Contas MT5

Definimos as contas MT5 que o bot utilizará para enviar ordens de negociação.

# Configurações das contas MT5
CONTAS_MT5 = [
    {
        "login": os.getenv('MT5_LOGIN'),
        "senha": os.getenv('MT5_PASSWORD'),
        "servidor": os.getenv('MT5_SERVER'),
        "us30": "US30.cash",
        "nas100": "US100.cash",
        "lote": 0.01
    }
]

contas_ativas = []  # Lista para armazenar contas ativas
shutdown_event = asyncio.Event()  # Evento para sinalizar o encerramento do programa

Explicação dos Campos:

  • login: Número de login da conta MT5.
  • senha: Senha da conta MT5.
  • servidor: Nome do servidor MT5.
  • us30 e nas100: Símbolos dos ativos que serão negociados.
  • lote: Volume padrão das ordens a serem enviadas.

4. Função de Reconexão com MT5

Esta função tenta reconectar à conta MT5 caso a conexão seja perdida.

async def reconectar_mt5(conta, max_tentativas=3):
    """
    Tenta reconectar à conta MT5 com um número máximo de tentativas.

    Args:
        conta (dict): Informações da conta MT5.
        max_tentativas (int): Número máximo de tentativas de reconexão.

    Returns:
        bool: True se a reconexão for bem-sucedida, False caso contrário.
    """
    for tentativa in range(max_tentativas):
        try:
            # Tenta inicializar a conexão com MT5
            if mt5.initialize(path=os.getenv('MT5_PATH'), login=int(conta['login']), server=conta['servidor'], password=conta['senha']):
                logger.info(f"Reconexão bem-sucedida para conta {conta['login']}")
                return True
            else:
                erro = mt5.last_error()
                logger.warning(f"Tentativa {tentativa   1} de reconexão falhou para conta {conta['login']}: {erro}")
        except Exception as e:
            logger.error(f"Erro durante a tentativa {tentativa   1} de reconexão para conta {conta['login']}: {e}")
        await asyncio.sleep(5)  # Espera 5 segundos antes de tentar novamente
    logger.error(f"Falha ao reconectar à conta {conta['login']} após {max_tentativas} tentativas")
    return False

Detalhes da Função:

  • Objetivo: Garantir que o bot mantenha a conexão com o MT5, tentando reconectar em caso de falha.
  • Parâmetros:
    • conta: Dicionário contendo as informações da conta MT5.
    • max_tentativas: Número máximo de tentativas de reconexão antes de desistir.
  • Processo:
    • Tenta inicializar a conexão com MT5 usando as credenciais fornecidas.
    • Se falhar, registra um aviso e aguarda 5 segundos antes de tentar novamente.
    • Após exceder o número máximo de tentativas, registra um erro e retorna False.

5. Função para Enviar Ordens para MT5

Esta função envia ordens de negociação para o MT5 com base nos sinais processados.

async def enviar_ordem(conta, simbolo, acao, lote):
    """
    Envia uma ordem de negociação para o MT5.

    Args:
        conta (dict): Informações da conta MT5.
        simbolo (str): Símbolo do ativo a ser negociado.
        acao (int): Tipo de ação (compra ou venda).
        lote (float): Volume da ordem.

    Returns:
        bool: True se a ordem for enviada com sucesso, False caso contrário.
    """
    # Tenta reconectar à conta MT5 antes de enviar a ordem
    if not await reconectar_mt5(conta):
        logger.error(f"Não foi possível enviar ordem para {conta['login']} devido a falha na reconexão")
        return False

    # Obtém informações do símbolo
    symbol_info = mt5.symbol_info(simbolo)
    if symbol_info is None:
        logger.error(f"Símbolo {simbolo} não encontrado")
        return False

    # Verifica se o símbolo está disponível para trading
    if not symbol_info.visible:
        logger.warning(f"Símbolo {simbolo} não está visível, tentando habilitá-lo")
        if not mt5.symbol_select(simbolo, True):
            logger.error(f"Falha ao selecionar o símbolo {simbolo}")
            return False

    # Obtém o tick atual do símbolo
    tick = mt5.symbol_info_tick(simbolo)
    if tick is None:
        logger.error(f"Não foi possível obter o tick para o símbolo {simbolo}")
        return False

    # Define o preço baseado na ação (compra ou venda)
    price = tick.ask if acao == mt5.ORDER_TYPE_BUY else tick.bid

    # Prepara a estrutura do pedido
    pedido = {
        "action": mt5.TRADE_ACTION_DEAL,  # Tipo de ação de negociação
        "symbol": simbolo,                # Símbolo do ativo
        "volume": float(lote),            # Volume da ordem
        "type": acao,                     # Tipo de ordem (compra ou venda)
        "price": price,                   # Preço da ordem
        "deviation": 20,                  # Desvio permitido no preço
        "magic": 234000,                   # Identificador único para a ordem
        "comment": "python script order",  # Comentário para a ordem
        "type_time": mt5.ORDER_TIME_GTC,   # Tipo de tempo da ordem (Good Till Cancelled)
        "type_filling": mt5.ORDER_FILLING_IOC,  # Tipo de preenchimento (Immediate or Cancel)
    }

    # Envia a ordem para o MT5
    resultado = mt5.order_send(pedido)
    if resultado.retcode != mt5.TRADE_RETCODE_DONE:
        logger.error(f"Erro ao enviar ordem para {conta['login']}: {resultado.comment}")
        logger.debug(f"Detalhes do pedido: {pedido}")
        logger.debug(f"Código de retorno: {resultado.retcode}")
        return False
    else:
        logger.info(f"Ordem enviada com sucesso para {conta['login']} com lote {lote}")
        logger.debug(f"Detalhes da ordem: {resultado}")
        return True

Detalhes da Função:

  • Objetivo: Enviar uma ordem de compra ou venda para o MT5 com base nos sinais recebidos.
  • Parâmetros:
    • conta: Informações da conta MT5.
    • simbolo: Símbolo do ativo a ser negociado (por exemplo, "US30.cash").
    • acao: Tipo de ação (mt5.ORDER_TYPE_BUY para compra ou mt5.ORDER_TYPE_SELL para venda).
    • lote: Volume da ordem (por exemplo, 0.01).
  • Processo:
    • Reconecta à conta MT5 se necessário.
    • Verifica se o símbolo está disponível e visível para negociação.
    • Obtém o preço atual do símbolo (ask para compra, bid para venda).
    • Prepara a estrutura do pedido com todos os parâmetros necessários.
    • Envia a ordem para o MT5 e verifica se foi bem-sucedida.

6. Processando Sinais do Telegram

Esta função interpreta mensagens recebidas no Telegram para determinar quais ações de negociação devem ser executadas.

async def processar_sinal(mensagem):
    """
    Processa uma mensagem recebida do Telegram para determinar a ação de negociação.

    Args:
        mensagem (str): Texto da mensagem recebida.

    """
    logger.info(f"Mensagem recebida do Telegram: {mensagem}")

    palavras = mensagem.lower().split()

    ativo = None
    acao = None

    # Verifica o ativo na mensagem
    if 'us30' in palavras:
        ativo = 'us30'
    elif 'nas100' in palavras:
        ativo = 'nas100'

    # Verifica a ação na mensagem
    if 'buy' in palavras:
        acao = mt5.ORDER_TYPE_BUY
    elif 'sell' in palavras:
        acao = mt5.ORDER_TYPE_SELL

    # Se nenhum ativo for reconhecido, encerra a função
    if not ativo:
        logger.info("Nenhum ativo reconhecido na mensagem.")
        return

    # Se nenhuma ação for reconhecida, encerra a função
    if acao is None:
        logger.info("Nenhuma ação (compra/venda) reconhecida na mensagem.")
        return

    acao_str = "COMPRA" se acao == mt5.ORDER_TYPE_BUY else "VENDA"
    logger.info(f"Interpretação: Ativo: {ativo.upper()}, Ação: {acao_str}")

    # Itera sobre todas as contas ativas para enviar a ordem
    for conta in contas_ativas[:]:  # Cria uma cópia da lista para iteração segura
        try:
            simbolo = conta.get(ativo)
            if not simbolo:
                logger.warning(f"Ativo {ativo} não configurado para a conta {conta['login']}. Pulando.")
                continue
            sucesso = await enviar_ordem(conta, simbolo, acao, conta['lote'])
            if not sucesso:
                logger.warning(f"Falha ao processar sinal para conta {conta['login']}. Removendo da lista de contas ativas.")
                contas_ativas.remove(conta)
        except Exception as e:
            logger.error(f"Erro ao processar sinal para conta {conta['login']}: {e}")
            logger.warning(f"Removendo conta {conta['login']} da lista de contas ativas devido a erro")
            contas_ativas.remove(conta)

Detalhes da Função:

  • Objetivo: Interpretar a mensagem recebida do Telegram para determinar qual ativo negociar e se deve comprar ou vender.
  • Parâmetros:
    • mensagem: Texto da mensagem recebida no Telegram.
  • Processo:
    • Converte a mensagem para letras minúsculas e divide em palavras.
    • Identifica o ativo mencionado (us30 ou nas100).
    • Identifica a ação (buy para compra ou sell para venda).
    • Itera sobre todas as contas ativas e envia a ordem correspondente.
    • Se a ordem falhar, remove a conta da lista de contas ativas para evitar tentativas futuras.

7. Monitoramento de Conexões

Esta função verifica periodicamente se as conexões com as contas MT5 estão ativas e tenta reconectar se necessário.

async def verificar_conexoes():
    """
    Verifica periodicamente se as contas MT5 estão conectadas e tenta reconectar se necessário.
    """
    while not shutdown_event.is_set():
        for conta in contas_ativas[:]:
            if shutdown_event.is_set():
                break
            if not await reconectar_mt5(conta):
                logger.warning(f"Conta {conta['login']} removida da lista de contas ativas devido a falha na conexão")
                contas_ativas.remove(conta)
        await asyncio.sleep(60)  # Verifica a cada 60 segundos

Detalhes da Função:

  • Objetivo: Garantir que todas as contas MT5 ativas mantenham uma conexão estável.
  • Processo:
    • Enquanto o evento de encerramento não for sinalizado, itera sobre todas as contas ativas.
    • Para cada conta, tenta reconectar.
    • Se a reconexão falhar após as tentativas definidas, remove a conta da lista de contas ativas.
    • Aguarda 60 segundos antes da próxima verificação.

8. Manipulação de Sinais para Encerramento Suave

Esta função lida com sinais de interrupção (como Ctrl C) para encerrar o programa de forma limpa.

def signal_handler(signum, frame):
    """
    Manipula sinais de interrupção para encerrar o programa de forma suave.

    Args:
        signum: Número do sinal.
        frame: Frame atual.
    """
    logger.info("Sinal de interrupção recebido. Encerrando o programa...")
    asyncio.get_event_loop().call_soon_threadsafe(shutdown_event.set)

Detalhes da Função:

  • Objetivo: Capturar sinais de interrupção e iniciar o processo de encerramento do bot de forma ordenada.
  • Processo:
    • Quando um sinal de interrupção é recebido (como SIGINT ou SIGTERM), registra uma mensagem de log.
    • Sinaliza o evento de encerramento para que todas as tarefas assíncronas possam finalizar adequadamente.

9. Função Principal (main)

Esta é a função central que coordena todas as operações do bot.

async def main():
    """
    Função principal que inicializa o bot e gerencia suas operações.
    """
    # Configurar o manipulador de sinais
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    # Inicializar contas MT5
    for conta in CONTAS_MT5:
        if await reconectar_mt5(conta):
            contas_ativas.append(conta)

    if not contas_ativas:
        logger.error("Nenhuma conta pôde ser inicializada. Encerrando o programa.")
        return

    logger.info(f"Programa continuando com {len(contas_ativas)} conta(s) ativa(s)")

    # Inicializar o cliente Telegram
    client = TelegramClient('session', os.getenv('TELEGRAM_API_ID'), os.getenv('TELEGRAM_API_HASH'))

    @client.on(events.NewMessage(chats=os.getenv('TELEGRAM_GROUP_USERNAME')))
    async def handler(event):
        """
        Manipulador de eventos para novas mensagens no Telegram.
        """
        if not shutdown_event.is_set():
            try:
                logger.info(f"Nova mensagem recebida do grupo {os.getenv('TELEGRAM_GROUP_USERNAME')}")
                await processar_sinal(event.message.text)
            except Exception as e:
                logger.error(f"Erro ao processar mensagem do Telegram: {e}")

    # Iniciar a tarefa de verificação de conexões
    verificar_task = asyncio.create_task(verificar_conexoes())

    try:
        await client.start(phone=os.getenv('TELEGRAM_PHONE_NUMBER'))
        logger.info("Bot Telegram iniciado. Aguardando mensagens...")
        await shutdown_event.wait()  # Aguarda até que o evento de encerramento seja sinalizado
    except Exception as e:
        logger.error(f"Erro no cliente Telegram: {e}")
    finally:
        await client.disconnect()
        verificar_task.cancel()
        for conta in contas_ativas:
            if mt5.shutdown():
                logger.info(f"MT5 desligado para a conta {conta['login']}")
            else:
                logger.warning(f"Falha ao desligar MT5 para a conta {conta['login']}")
        logger.info("Programa encerrado.")

Detalhes da Função:

  • Configuração de Sinais:

    • Associa os sinais de interrupção (SIGINT e SIGTERM) à função signal_handler para garantir um encerramento suave.
  • Inicialização das Contas MT5:

    • Itera sobre todas as contas definidas em CONTAS_MT5.
    • Tenta reconectar a cada conta e adiciona à lista de contas_ativas se bem-sucedido.
    • Se nenhuma conta puder ser inicializada, registra um erro e encerra o programa.
  • Inicialização do Cliente Telegram:

    • Cria uma instância do TelegramClient usando as credenciais do Telegram.
    • Define um manipulador de eventos (handler) que será chamado sempre que uma nova mensagem for recebida no grupo especificado.
    • O manipulador chama a função processar_sinal para interpretar e agir sobre a mensagem.
  • Iniciar Tarefas Assíncronas:

    • Cria uma tarefa assíncrona para verificar periodicamente as conexões MT5 (verificar_conexoes).
    • Inicia o cliente Telegram e aguarda até que o evento de encerramento seja sinalizado.
  • Encerramento:

    • Ao receber um sinal de encerramento, desconecta o cliente Telegram.
    • Cancela a tarefa de verificação de conexões.
    • Desliga o MT5 para todas as contas ativas.
    • Registra que o programa foi encerrado.

10. Executando o Script

Para executar o script, crie um arquivo chamado bot.py no diretório do seu projeto e cole o seguinte código completo, que integra todas as partes que discutimos:

import os
import sys
import asyncio
import logging
import signal
import pkg_resources
from datetime import datetime
from dotenv import load_dotenv
from telethon import TelegramClient, events
import MetaTrader5 as mt5

# Carrega variáveis de ambiente do arquivo .env
load_dotenv()

# Acessa as variáveis de ambiente para configuração
API_ID = os.getenv('TELEGRAM_API_ID')
API_HASH = os.getenv('TELEGRAM_API_HASH')
PHONE_NUMBER = os.getenv('TELEGRAM_PHONE_NUMBER')
GROUP_USERNAME = os.getenv('TELEGRAM_GROUP_USERNAME')

MT5_LOGIN = os.getenv('MT5_LOGIN')
MT5_PASSWORD = os.getenv('MT5_PASSWORD')
MT5_SERVER = os.getenv('MT5_SERVER')
MT5_PATH = os.getenv('MT5_PATH')

LOG_FILE = os.getenv('LOG_FILE', 'app.log')

# Configura logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)

# Configurações das contas MT5
CONTAS_MT5 = [
    {
        "login": MT5_LOGIN,
        "senha": MT5_PASSWORD,
        "servidor": MT5_SERVER,
        "us30": "US30.cash",
        "nas100": "US100.cash",
        "lote": 0.01
    }
]

contas_ativas = []
shutdown_event = asyncio.Event()

async def reconectar_mt5(conta, max_tentativas=3):
    """
    Tenta reconectar à conta MT5 com um número máximo de tentativas.

    Args:
        conta (dict): Informações da conta MT5.
        max_tentativas (int): Número máximo de tentativas de reconexão.

    Returns:
        bool: True se a reconexão for bem-sucedida, False caso contrário.
    """
    for tentativa in range(max_tentativas):
        try:
            # Tenta inicializar a conexão com MT5
            if mt5.initialize(path=MT5_PATH, login=int(conta['login']), server=conta['servidor'], password=conta['senha']):
                logger.info(f"Reconexão bem-sucedida para conta {conta['login']}")
                return True
            else:
                erro = mt5.last_error()
                logger.warning(f"Tentativa {tentativa   1} de reconexão falhou para conta {conta['login']}: {erro}")
        except Exception as e:
            logger.error(f"Erro durante a tentativa {tentativa   1} de reconexão para conta {conta['login']}: {e}")
        await asyncio.sleep(5)  # Espera 5 segundos antes de tentar novamente
    logger.error(f"Falha ao reconectar à conta {conta['login']} após {max_tentativas} tentativas")
    return False

async def enviar_ordem(conta, simbolo, acao, lote):
    """
    Envia uma ordem de negociação para o MT5.

    Args:
        conta (dict): Informações da conta MT5.
        simbolo (str): Símbolo do ativo a ser negociado.
        acao (int): Tipo de ação (compra ou venda).
        lote (float): Volume da ordem.

    Returns:
        bool: True se a ordem for enviada com sucesso, False caso contrário.
    """
    # Tenta reconectar à conta MT5 antes de enviar a ordem
    if not await reconectar_mt5(conta):
        logger.error(f"Não foi possível enviar ordem para {conta['login']} devido a falha na reconexão")
        return False

    # Obtém informações do símbolo
    symbol_info = mt5.symbol_info(simbolo)
    if symbol_info is None:
        logger.error(f"Símbolo {simbolo} não encontrado")
        return False

    # Verifica se o símbolo está disponível para trading
    if not symbol_info.visible:
        logger.warning(f"Símbolo {simbolo} não está visível, tentando habilitá-lo")
        if not mt5.symbol_select(simbolo, True):
            logger.error(f"Falha ao selecionar o símbolo {simbolo}")
            return False

    # Obtém o tick atual do símbolo
    tick = mt5.symbol_info_tick(simbolo)
    if tick is None:
        logger.error(f"Não foi possível obter o tick para o símbolo {simbolo}")
        return False

    # Define o preço baseado na ação (compra ou venda)
    price = tick.ask if acao == mt5.ORDER_TYPE_BUY else tick.bid

    # Prepara a estrutura do pedido
    pedido = {
        "action": mt5.TRADE_ACTION_DEAL,  # Tipo de ação de negociação
        "symbol": simbolo,                # Símbolo do ativo
        "volume": float(lote),            # Volume da ordem
        "type": acao,                     # Tipo de ordem (compra ou venda)
        "price": price,                   # Preço da ordem
        "deviation": 20,                  # Desvio permitido no preço
        "magic": 234000,                   # Identificador único para a ordem
        "comment": "python script order",  # Comentário para a ordem
        "type_time": mt5.ORDER_TIME_GTC,   # Tipo de tempo da ordem (Good Till Cancelled)
        "type_filling": mt5.ORDER_FILLING_IOC,  # Tipo de preenchimento (Immediate or Cancel)
    }

    # Envia a ordem para o MT5
    resultado = mt5.order_send(pedido)
    if resultado.retcode != mt5.TRADE_RETCODE_DONE:
        logger.error(f"Erro ao enviar ordem para {conta['login']}: {resultado.comment}")
        logger.debug(f"Detalhes do pedido: {pedido}")
        logger.debug(f"Código de retorno: {resultado.retcode}")
        return False
    else:
        logger.info(f"Ordem enviada com sucesso para {conta['login']} com lote {lote}")
        logger.debug(f"Detalhes da ordem: {resultado}")
        return True

async def processar_sinal(mensagem):
    """
    Processa uma mensagem recebida do Telegram para determinar a ação de negociação.

    Args:
        mensagem (str): Texto da mensagem recebida.
    """
    logger.info(f"Mensagem recebida do Telegram: {mensagem}")

    palavras = mensagem.lower().split()

    ativo = None
    acao = None

    # Verifica o ativo na mensagem
    if 'us30' in palavras:
        ativo = 'us30'
    elif 'nas100' in palavras:
        ativo = 'nas100'

    # Verifica a ação na mensagem
    if 'buy' in palavras:
        acao = mt5.ORDER_TYPE_BUY
    elif 'sell' in palavras:
        acao = mt5.ORDER_TYPE_SELL

    # Se nenhum ativo for reconhecido, encerra a função
    if not ativo:
        logger.info("Nenhum ativo reconhecido na mensagem.")
        return

    # Se nenhuma ação for reconhecida, encerra a função
    if acao is None:
        logger.info("Nenhuma ação (compra/venda) reconhecida na mensagem.")
        return

    acao_str = "COMPRA" se acao == mt5.ORDER_TYPE_BUY else "VENDA"
    logger.info(f"Interpretação: Ativo: {ativo.upper()}, Ação: {acao_str}")

    # Itera sobre todas as contas ativas para enviar a ordem
    for conta in contas_ativas[:]:  # Cria uma cópia da lista para iteração segura
        try:
            simbolo = conta.get(ativo)
            if not simbolo:
                logger.warning(f"Ativo {ativo} não configurado para a conta {conta['login']}. Pulando.")
                continue
            sucesso = await enviar_ordem(conta, simbolo, acao, conta['lote'])
            if not sucesso:
                logger.warning(f"Falha ao processar sinal para conta {conta['login']}. Removendo da lista de contas ativas.")
                contas_ativas.remove(conta)
        except Exception as e:
            logger.error(f"Erro ao processar sinal para conta {conta['login']}: {e}")
            logger.warning(f"Removendo conta {conta['login']} da lista de contas ativas devido a erro")
            contas_ativas.remove(conta)

async def verificar_conexoes():
    """
    Verifica periodicamente se as contas MT5 estão conectadas e tenta reconectar se necessário.
    """
    while not shutdown_event.is_set():
        for conta in contas_ativas[:]:
            if shutdown_event.is_set():
                break
            if not await reconectar_mt5(conta):
                logger.warning(f"Conta {conta['login']} removida da lista de contas ativas devido a falha na conexão")
                contas_ativas.remove(conta)
        await asyncio.sleep(60)  # Verifica a cada 60 segundos

def signal_handler(signum, frame):
    """
    Manipula sinais de interrupção para encerrar o programa de forma suave.

    Args:
        signum: Número do sinal.
        frame: Frame atual.
    """
    logger.info("Sinal de interrupção recebido. Encerrando o programa...")
    asyncio.get_event_loop().call_soon_threadsafe(shutdown_event.set)

async def main():
    """
    Função principal que inicializa o bot e gerencia suas operações.
    """
    # Configurar o manipulador de sinais
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    # Inicializar contas MT5
    for conta in CONTAS_MT5:
        if await reconectar_mt5(conta):
            contas_ativas.append(conta)

    if not contas_ativas:
        logger.error("Nenhuma conta pôde ser inicializada. Encerrando o programa.")
        return

    logger.info(f"Programa continuando com {len(contas_ativas)} conta(s) ativa(s)")

    # Inicializar o cliente Telegram
    client = TelegramClient('session', API_ID, API_HASH)

    @client.on(events.NewMessage(chats=GROUP_USERNAME))
    async def handler(event):
        """
        Manipulador de eventos para novas mensagens no Telegram.
        """
        if not shutdown_event.is_set():
            try:
                logger.info(f"Nova mensagem recebida do grupo {GROUP_USERNAME}")
                await processar_sinal(event.message.text)
            except Exception as e:
                logger.error(f"Erro ao processar mensagem do Telegram: {e}")

    # Iniciar a tarefa de verificação de conexões
    verificar_task = asyncio.create_task(verificar_conexoes())

    try:
        await client.start(phone=PHONE_NUMBER)
        logger.info("Bot Telegram iniciado. Aguardando mensagens...")
        await shutdown_event.wait()  # Aguarda até que o evento de encerramento seja sinalizado
    except Exception as e:
        logger.error(f"Erro no cliente Telegram: {e}")
    finally:
        await client.disconnect()
        verificar_task.cancel()
        for conta in contas_ativas:
            if mt5.shutdown():
                logger.info(f"MT5 desligado para a conta {conta['login']}")
            else:
                logger.warning(f"Falha ao desligar MT5 para a conta {conta['login']}")
        logger.info("Programa encerrado.")

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except Exception as e:
        logger.critical(f"Erro crítico no programa: {e}")
        sys.exit(1)

Explicação Completa do Código:

  1. Carregamento das Variáveis de Ambiente:

    • Usa load_dotenv() para carregar as variáveis definidas no arquivo .env.
    • Acessa essas variáveis usando os.getenv() para configurar o Telegram e MT5.
  2. Configuração de Logging:

    • Configura o logging para registrar mensagens tanto em um arquivo (app.log) quanto no console.
    • Define o nível mínimo de registro como INFO.
  3. Configuração das Contas MT5:

    • Define uma lista CONTAS_MT5 contendo dicionários com as informações necessárias para cada conta MT5.
    • Inicializa uma lista vazia contas_ativas para armazenar contas que estão conectadas.
  4. Função de Reconexão (reconectar_mt5):

    • Tenta reconectar à conta MT5 até um número máximo de tentativas.
    • Registra mensagens de sucesso ou falha no log.
  5. Função para Enviar Ordens (enviar_ordem):

    • Garante que a conexão com MT5 está ativa.
    • Verifica se o símbolo está disponível e visível.
    • Obtém o preço atual do símbolo (ask para compra, bid para venda).
    • Prepara a ordem com os parâmetros necessários e a envia para o MT5.
    • Registra o resultado da ordem no log.
  6. Função para Processar Sinais (processar_sinal):

    • Interpreta a mensagem recebida no Telegram para determinar qual ativo negociar e qual ação (compra ou venda) executar.
    • Itera sobre todas as contas ativas e envia a ordem correspondente.
    • Se uma ordem falhar, remove a conta da lista de contas ativas para evitar tentativas futuras.
  7. Função de Monitoramento de Conexões (verificar_conexoes):

    • Executa em um loop contínuo, verificando a conexão de cada conta MT5 ativa a cada 60 segundos.
    • Tenta reconectar se a conexão estiver perdida e remove contas que não puderam ser reconectadas.
  8. Manipulador de Sinais (signal_handler):

    • Captura sinais de interrupção (como Ctrl C) e sinaliza o encerramento do programa.
    • Garante que todas as tarefas assíncronas sejam finalizadas corretamente.
  9. Função Principal (main):

    • Configura os manipuladores de sinais.
    • Inicializa as contas MT5 e adiciona as contas bem-sucedidas à lista de contas ativas.
    • Inicializa o cliente Telegram e define um manipulador de eventos para novas mensagens.
    • Inicia a tarefa assíncrona de monitoramento de conexões.
    • Aguarda até que um sinal de encerramento seja recebido.
    • Ao encerrar, desconecta o cliente Telegram, cancela a tarefa de monitoramento e desliga o MT5 para todas as contas ativas.
  10. Execução do Script:

    • Verifica se o script está sendo executado diretamente.
    • Executa a função principal usando asyncio.run().
    • Captura quaisquer exceções críticas, registra no log e encerra o programa com um código de erro.

Executando o Script

Após configurar o ambiente e criar o arquivo bot.py, siga os passos abaixo para executar o bot.

1. Ativar o Ambiente Virtual

Se você configurou um ambiente virtual, ative-o:

  • No Windows:

    venv\Scripts\activate
    
  • No macOS e Linux:

    source venv/bin/activate
    

2. Executar o Script

No diretório do seu projeto, execute o seguinte comando:

python bot.py

O que esperar:

  • Logs no Console: Você verá mensagens informando sobre o status da conexão com o MT5 e do cliente Telegram.
  • Arquivo de Log: Todas as mensagens de log também serão gravadas no arquivo app.log (ou outro definido em LOG_FILE).

Interagindo com o Bot:

  • Envie mensagens no grupo do Telegram especificado (@superus30) com comandos como "buy US30" ou "sell NAS100".
  • O bot interpretará esses sinais e enviará ordens de negociação correspondentes para a conta MT5 configurada.

3. Encerrando o Bot

Para encerrar o bot de forma segura, pressione Ctrl C no terminal onde o script está sendo executado. Isso acionará o manipulador de sinais, que garantirá que todas as conexões sejam fechadas corretamente antes de encerrar o programa.


Boas Práticas e Dicas

  1. Manutenção da Segurança:

    • Nunca compartilhe seu arquivo .env ou exponha suas credenciais em repositórios públicos.
    • Use Ambientes Virtuais para isolar as dependências do projeto.
  2. Monitoramento e Logs:

    • Revise regularmente os logs (app.log) para identificar e resolver possíveis problemas.
    • Implementar Rotação de Logs: Para evitar que o arquivo de log cresça indefinidamente, considere implementar rotação usando bibliotecas como logging.handlers.
  3. Testes em Ambiente de Demonstração:

    • Antes de operar em contas reais, teste seu bot em ambientes de demonstração para garantir que tudo funcione conforme o esperado.
    • Verifique as Ordens: Assegure-se de que as ordens estão sendo enviadas corretamente e que o volume está adequado.
  4. Gerenciamento de Erros:

    • Tratamento Robusto de Exceções: Certifique-se de que todas as possíveis exceções sejam tratadas para evitar que o bot pare inesperadamente.
    • Alertas: Considere implementar alertas (por exemplo, enviando mensagens de log críticas para seu email) para notificá-lo sobre falhas graves.
  5. Atualizações de Dependências:

    • Mantenha suas bibliotecas atualizadas para aproveitar melhorias de segurança e correções de bugs.
    • Verifique as Documentações: Consulte as documentações oficiais das bibliotecas para entender mudanças ou atualizações.
  6. Escalabilidade:

    • Gerencie Múltiplas Contas: Se você pretende usar múltiplas contas MT5, expanda a configuração de CONTAS_MT5 conforme necessário.
    • Adicionar Mais Ativos: Atualize o script para suportar mais ativos além de "US30" e "NAS100" conforme sua estratégia de negociação.
  7. Documentação e Comentários:

    • Comente Seu Código: Mantenha comentários atualizados e relevantes para facilitar futuras manutenções.
    • Documente Configurações: Mantenha um documento com detalhes sobre como configurar e executar o bot.

Conclusão

Parabéns! Você criou um bot em Python que integra o Telegram com o MetaTrader5, capaz de automatizar ordens de negociação com base em sinais recebidos no Telegram. Ao seguir este guia, você não apenas implementou a funcionalidade desejada, mas também adotou práticas de segurança e organização de código que são fundamentais para projetos de software robustos.

Próximos Passos:

  • Expandir Funcionalidades: Adicione mais funcionalidades, como diferentes tipos de ordens, gerenciamento de riscos ou integração com outras plataformas.
  • Interface de Usuário: Considere criar uma interface gráfica ou um dashboard para monitorar o desempenho do bot.
  • Automatizar Deployments: Se desejar que o bot funcione continuamente, explore opções de deployment em servidores ou serviços de nuvem.

Recursos Adicionais:

  • Documentação do Telethon: Para aprofundar-se nas funcionalidades da biblioteca Telethon.
  • Documentação do MetaTrader5 Python: Para entender todas as possibilidades de interação com o MT5.
  • Python Logging Best Practices: Para aprimorar suas habilidades de logging.
  • Python Asyncio Documentation: Para dominar a programação assíncrona em Python.
  • Gerenciamento de Segredos em Python: Para aprender mais sobre como gerenciar configurações e segredos de forma segura.

? Fique Seguro e Boas Operações! ?

A segurança das suas credenciais é fundamental para proteger suas contas e dados. Sempre siga as melhores práticas para garantir a integridade e confidencialidade das suas informações.

Declaração de lançamento Este artigo está reproduzido em: https://dev.to/vital7388/guia-completo-construindo-um-bot-seguro-de-integracao-entre-telegram-e-metatrader5-em-python-1lg1?1Se houver algum violação, entre em contato com [email protected] para excluir
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3