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
- Introdução
- Pré-requisitos
- Configurando o Ambiente
- Criando o Arquivo
.env
- Entendendo o Script Python
- Executando o Script
- Boas Práticas e Dicas
- Conclusão
Introdução
Neste projeto, você criará um bot em Python que realiza as seguintes tarefas:
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").
Processa Sinais: Quando um sinal é detectado, o bot interpreta para determinar qual ativo negociar e se deve comprar ou vender.
Interage com o MetaTrader5 (MT5): Com base no sinal, o bot envia ordens de negociação para sua conta MT5.
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:
Conhecimento Básico de Python: Familiaridade com a sintaxe do Python e conceitos como funções, loops e módulos.
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.
Conta no Telegram: Você precisará acessar a API do Telegram. Registre sua aplicação aqui para obter o
API_ID
e oAPI_HASH
.Conta no MetaTrader5: Acesse uma conta MT5, incluindo credenciais de login e informações do servidor.
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:
Carregar Variáveis de Ambiente: Utiliza o
python-dotenv
para carregar credenciais e configurações do arquivo.env
.Configurar Logging: Define como e onde os logs serão armazenados, facilitando o monitoramento e a depuração.
Configurar Contas MT5: Define as contas MT5 que serão utilizadas pelo bot.
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.
Processar Sinais do Telegram: Interpreta mensagens do Telegram para determinar as ações de negociação.
Monitorar Conexões: Verifica periodicamente se as conexões com o MT5 estão ativas e tenta reconectar se necessário.
Manipular Sinais de Encerramento: Garante que o bot encerre suas operações de forma suave ao receber sinais de interrupção.
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
incluiINFO
,WARNING
,ERROR
eCRITICAL
. - 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 oumt5.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
ounas100
). - Identifica a ação (
buy
para compra ousell
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
eSIGTERM
) à funçãosignal_handler
para garantir um encerramento suave.
- Associa os sinais de interrupção (
-
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.
- Itera sobre todas as contas definidas em
-
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.
- Cria uma instância do
-
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.
- Cria uma tarefa assíncrona para verificar periodicamente as conexões MT5 (
-
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:
-
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.
- Usa
-
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
.
- Configura o logging para registrar mensagens tanto em um arquivo (
-
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.
- Define uma lista
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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 emLOG_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
-
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.
-
Nunca compartilhe seu arquivo
-
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
.
-
Revise regularmente os logs (
-
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.
-
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.
-
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.
-
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.
-
Gerencie Múltiplas Contas: Se você pretende usar múltiplas contas MT5, expanda a configuração de
-
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.
Top comments (0)