Transferência de dados de ticks do MetaTrader para Python via sockets usando serviços MQL5
Introdução
Às vezes, nem tudo pode ser implementado em MQL5. Mesmo que seja possível converter bibliotecas de ponta já existentes para MQL5, isso levará muito tempo. A melhor opção é integrar bibliotecas já disponíveis ou utilizá-las para realizar a tarefa. Por exemplo, em Python existe uma grande quantidade de bibliotecas de machine learning. Não faz sentido criar cópias idênticas em MQL5 apenas para realizar uma tarefa de machine learning voltada ao trading. É melhor exportar os dados necessários para a biblioteca de machine learning em Python, executar o processamento necessário no ambiente Python e importar o resultado de volta para o programa MQL5. Neste artigo, trataremos da transferência desses dados do terminal MetaTrader para o ambiente Python.
Existe um pacote Python chamado MetaTrader 5, que pode ser usado para acessar dados dos gráficos do MetaTrader, como informações de barras, ticks, informações do usuário, informações de negociação etc. No entanto, esse pacote está disponível apenas para Windows, o que significa que, se você quiser usá-lo, precisará do sistema operacional Windows. Neste artigo, mostrarei como acessar dados de ticks do MetaTrader em um programa Python usando sockets.
Com o surgimento dos sockets de Berkeley no sistema operacional Unix 4.2BSD em 1983, a comunicação entre máquinas se tornou simples e passou a ser amplamente adotada. Bibliotecas mais avançadas e protocolos da camada de aplicação dependem dos protocolos de socket da camada de transporte, que usaremos neste artigo.
Para fins de demonstração, vou transmitir apenas dados de ticks, como bid, ask e time, para uma aplicação Python por meio de portas de socket. Usando a ideia apresentada neste artigo, podemos exportar todos os tipos de dados de gráficos, como informações de barras, informações de usuários, informações de operações etc. Para isso, podemos usar não apenas serviços, mas também scripts, indicadores ou EAs.
Fluxo de execução do programa
Este artigo aborda o uso de serviços MQL5 para exportar dados de ticks, incluindo bid, ask e time, para um servidor Python. Por sua vez, o servidor Python transmitirá esses dados a todos os sockets de clientes conectados ao servidor. Isso é ilustrado de forma mais clara na figura a seguir.

Como mostra a figura, o serviço MetaTrader se conecta ao servidor Python, que escuta na porta 9070. Todos os dados de ticks dos gráficos abertos no terminal MetaTrader 5 serão enviados ao servidor Python na porta 9070. Em seguida, o servidor Python analisa os dados recebidos do MetaTrader 5, executa o tratamento necessário dos dados e depois encaminha, ou seja, transmite esses dados de ticks aos clientes conectados. Depois disso, os clientes podem usar os dados recebidos para as tarefas necessárias, aplicar diferentes algoritmos de análise e transmitir o resultado de volta ao serviço MetaTrader para processamento posterior.
Por que serviços e Python?
Não há nenhum motivo específico para escolher os serviços do MetaTrader 5 para enviar informações de ticks. Para esse tipo de tarefa, também é possível usar scripts, indicadores e EAs, e farei isso nos próximos artigos. Eu apenas queria mostrar que dados de ticks e outras informações associadas aos gráficos podem ser transferidas para outras aplicações por meio de serviços do MetaTrader que usam programação com sockets. Em vez de Python para o servidor e os clientes, também é possível usar outras plataformas e frameworks para desenvolvê-los. O Python me pareceu uma opção conveniente.
Serviços MQL5
Como sabemos, os serviços no MetaTrader 5 têm apenas uma função OnStart, e tudo deve ser executado dentro dessa função. Os serviços não ficam vinculados a nenhuma janela, e funções internas como _Symbol, _Period, _Point e outras não estão disponíveis nos serviços, ao contrário do que ocorre em scripts, indicadores e EAs, nos quais essas funções estão disponíveis. Além disso, a inicialização e o gerenciamento dos serviços só podem ser feitos pela janela do Navegador. Os serviços não podem ser arrastados para janelas de gráficos para serem iniciados. Você pode ver como iniciar serviços na seção de demonstração abaixo, na qual incluí um arquivo GIF.
O serviço começa com a definição das variáveis do socket, do servidor e da porta.
int socket; string server = "localhost"; int port = 9070;
A função SocketInit() cria um handle do socket usando o método SocketCreate(). O handle do socket, juntamente com o endereço do servidor e a porta, é passado para a função SocketConnect() para conectar ao servidor, que retorna true em caso de conexão bem-sucedida.
void SocketInit() { socket=SocketCreate(); bool connect = SocketConnect(socket, server, port, 1000); if(connect) { Print("socket is connected", " ", server, " port ", port); } }
Ao adicionar e iniciar o serviço pela janela do Navegador no terminal MetaTrader, a função OnStart é chamada; dentro dela, chamamos a função SocketInit().
void OnStart() { SocketInit();
Em seguida, são definidas três variáveis: a primeira para armazenar as informações de ticks do gráfico aberto, a segunda para armazenar o ID do gráfico e a terceira para armazenar as informações de ticks que serão enviadas ao servidor.
MqlTick latestTick; long next; string payload;
Como precisamos enviar continuamente as informações de ticks ao servidor, é definido um loop while true e, em seguida, verifica-se se o servidor está conectado. Se a conexão via socket falhar, SocketIsConnected(socket handle) retornará false, e o serviço será interrompido.
while(true) { if(!SocketIsConnected(socket)){ Print("socket is not initialized yet so stopping the service"); break; }
As variáveis next e payload são inicializadas com o primeiro ID do gráfico e uma string vazia.
next = ChartFirst(); payload = "";
Em seguida, outro loop while é executado para percorrer todas as janelas de gráficos e obter o símbolo do gráfico.
while (next != -1) { string chartSymbol = ChartSymbol(next);
São extraídos os dados de ticks mais recentes, como bid, ask e time, para o gráfico.
SymbolInfoTick(chartSymbol, latestTick); double bid = latestTick.bid; double ask = latestTick.ask; string tickTime = TimeToString(latestTick.time, TIME_SECONDS);
O payload a ser enviado ao servidor é montado usando os valores de symbol, time, bid e ask. Tentei criar uma string JSON para o payload, para que a biblioteca JSON no servidor Python pudesse decodificar a string e extrair os dados como objeto JSON. Você pode usar qualquer formato que seja conveniente para você.
bool stringAdded = StringAdd(payload, StringFormat("{\"pair\": \"%s\", \"time\": \"%s\", \"bid\": %f, \"ask\": %f}", chartSymbol, tickTime, bid, ask));
Se várias janelas de gráficos estiverem abertas, são adicionadas as informações de ticks da janela de gráfico seguinte usando #@# como separador.
next = ChartNext(next); if (next != -1 && stringAdded) { stringAdded = StringAdd(payload, "#@#"); }
Quando todas as janelas de gráficos abertas tiverem sido percorridas em busca de dados de ticks e o payload estiver preenchido, os dados estarão prontos para serem enviados ao servidor. O método SocketSend envia os dados pela conexão de socket identificada pelo handle do socket. Certifique-se de que o valor no campo de comprimento esteja correto; caso contrário, caracteres extras serão enviados junto com os dados, criando carga adicional no servidor durante a análise dos dados.
uchar data[]; int len = StringToCharArray(payload, data); SocketSend(socket, data, len-1);
O loop então recomeça a partir da primeira janela de gráfico aberta enquanto a conexão com o servidor permanecer aberta.
Programa de servidor em Python
Esse servidor deve escutar na porta 9070 o recebimento de informações de ticks a partir do terminal MetaTrader, além de escutar na porta 9071 as conexões dos clientes aos quais enviará as informações de ticks recebidas. Assim, dois sockets são vinculados a essas portas e continuam escutando nelas.
host = '127.0.0.1' MT5_RECIEVING_PORT = 9070 CLIENT_SENDING_PORT = 9071 mt5Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) mt5Socket.bind((host, MT5_RECIEVING_PORT)) mt5Socket.listen() clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientSocket.bind((host, CLIENT_SENDING_PORT)) clientSocket.listen()
Programei o servidor de modo que apenas um MetaTrader 5 tenha permissão para se conectar e trocar dados. Isso é feito para evitar gerenciamento incorreto e inconsistências, que podem surgir quando há várias conexões do MetaTrader 5. Por outro lado, os clientes devem receber dados de ticks para processamento posterior; diferentes clientes podem usar os mesmos dados para aplicar algoritmos distintos e alcançar o resultado desejado. Assim, surge a necessidade de gerenciar as conexões de clientes.
A variável dict a seguir é usada para armazenar os clientes ativos e gerenciá-los no lado do servidor.
connectedClients = {} A thread do MetaTrader e a thread dos clientes são iniciadas separadamente, para que possam ser executadas de forma independente uma da outra.
if __name__ == "__main__": thread = threading.Thread(target=acceptFromMt5) thread.start() thread = threading.Thread(target=acceptFromClients) thread.start()
A thread do MetaTrader 5 continua aguardando até que um serviço MetaTrader estabeleça conexão. Depois que a conexão é aceita, a função processMt5Data é chamada; nela, os dados são recebidos, processados e enviados aos clientes correspondentes. Falarei sobre isso com mais detalhes mais adiante.
Se ocorrer um erro ao aceitar a conexão do serviço MetaTrader, o servidor aguardará a próxima conexão.
def acceptFromMt5(): try: print("Server is listening for mt5") mt5Client, address = mt5Socket.accept() print(f"mt5 service is connected at {str(address)}") processMt5Data(mt5Client) except Exception as ex: print(f"error in accepting mt5 client {ex}") acceptFromMt5()
Antes de abordar a seção de análise das informações de ticks, é extremamente importante entender como ocorre a conexão e o gerenciamento dos clientes. Assim como no caso do serviço MetaTrader 5, o servidor aguarda uma nova conexão de cliente. O cliente precisa enviar sua identificação para que ela possa ser gerenciada no servidor. A identificação corresponde ao símbolo da janela de gráfico. Essa identificação é usada como chave para o objeto dict, e o cliente é adicionado a esse dicionário. O loop while é usado para viabilizar a conexão de um grande número de clientes.
def acceptFromClients(): try: while True: print("Server is listening for clients") client, address = clientSocket.accept() pair = client.recv(7).decode("ascii") print(f"{pair} client service is connected at {str(address)}") clients = connectedClients[pair] if connectedClients and pair in connectedClients.keys() else [] clients.append(client) connectedClients[pair] = clients except Exception as ex: print(f"error in accepting other clients {ex}") acceptFromClients()
Agora que entendemos como os clientes recém-conectados são adicionados, fica fácil entender como os dados de ticks recebidos são retransmitidos a esses clientes. Os dados de ticks são convertidos em um objeto JSON, do qual o símbolo é extraído. As informações de ticks são então transmitidas aos clientes com base na chave de identificação do cliente enviada ao servidor durante a conexão.
Os dados de ticks são separados com o mesmo separador utilizado no serviço MetaTrader 5. Uma separação adicional é necessária quando dados de ticks de vários gráficos são enviados ao mesmo tempo. Essa segunda etapa de separação foi necessária porque a biblioteca Python usada para processar JSON não conseguia interpretar as informações de ticks quando informações de ticks de vários gráficos eram enviadas simultaneamente do serviço MetaTrader ao servidor.
def processMt5Data(mt5Client): data = "mt5 client connected" repeatativeEmpty = 0 while(len(data) > 0): try: data = mt5Client.recv(1024000).decode("ascii") if len(data) > 0: for jsn in data.split("#@#"): if "}{" in jsn: splittedTickData = jsn.split("}{") jsn = splittedTickData[0] + "}" jsonTickData = json.loads(jsn) pair = jsonTickData["pair"] if pair in connectedClients.keys(): broadcastToClients(pair, jsonTickData) repeatativeEmpty = repeatativeEmpty + 1 if len(data) == 0 else 0 if repeatativeEmpty > 10: print(f"data is not recieved for 10 times in a row {data}") break except Exception as ex: print(f"error in processing mt5 data {ex}") break time.sleep(0.1) acceptFromMt5()
O envio de dados aos clientes é simples. Se ocorrer um erro durante o envio dos dados, o cliente será removido da lista de clientes conectados, caso se constate que o cliente não está mais conectado, e o dicionário será atualizado.
def broadcastToClients(pair, message): for client in connectedClients[pair]: try: client.send(str(message).encode("ascii")) except Exception as ex: print(f"error while sending {message} to {client} for {pair}") client.close() clients = connectedClients[pair] clients.remove(client) connectedClients[pair] = clientsCom isso, o programa do servidor está concluído.
Cliente em Python
O programa cliente se conecta ao servidor na porta 9071. Para gerenciar os clientes no servidor, é necessária uma chave que represente o símbolo/par de moedas no servidor. Assim, precisamos de uma forma de exibir a lista de chaves disponíveis, para que uma delas seja selecionada como o ID a ser enviado ao servidor.
CURRENCY_PAIRS = [ "AUDUSD", "AUDJPY", "AUDCAD", "AUDNZD", "AUDCHF", "CADJPY", "CADCHF", "CHFJPY", "EURUSD", "EURJPY", "EURGBP", "EURCAD", "EURAUD", "EURNZD", "EURCHF", "GBPUSD", "GBPJPY", "GBPCAD", "GBPAUD", "GBPNZD", "GBPCHF", "NZDUSD", "NZDJPY", "NZDCAD", "USDCHF", "USDJPY", "USDCAD", "NZDCHF" ] server = "127.0.0.1" port = 9071
Como podemos ver, após a conexão, uma das chaves da lista acima é selecionada e enviada ao servidor; em seguida, o programa aguarda a chegada de dados do servidor.
if __name__ == "__main__": print(f"please choose from the currency pairs given below as name \n {', '.join(CURRENCY_PAIRS)}") name = input("enter the name for the client : ") if name in CURRENCY_PAIRS: client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if client.connect_ex((server, port)) == 0: client.send(name.encode("ascii")) receiveData(client) else: print("server could not be connected.") else: print("you didn't choose from the above list")
O programa cliente que escrevi apenas exibe os dados de ticks na tela, mas podemos processá-los conforme nossas necessidades. Por exemplo, podemos continuar processando os dados usando frameworks e bibliotecas disponíveis, aplicar bibliotecas de machine learning etc.
def receiveData(client): repeatativeEmpty = 0 while True: data = client.recv(1024).decode("ascii") print("data received ", data) repeatativeEmpty = repeatativeEmpty + 1 if len(data) == 0 else 0 if repeatativeEmpty > 10: print(f"data is not recieved for 10 times in a row {data}") break
Com isso, o programa cliente está concluído.
Demonstração do fluxo de dados
Para que os dados sejam processados corretamente, primeiro é necessário iniciar o servidor; caso contrário, nenhum dos serviços funcionará. Para fins de teste, o servidor Python é iniciado em um ambiente virtual, como mostra a figura a seguir.

Precisamos que o servidor receba os dados de ticks e depois os transmita aos clientes. Portanto, o segundo passo é iniciar o serviço MetaTrader, como mostrado na captura de tela a seguir.

Se o serviço MetaTrader não estiver em execução, os clientes conectados ficarão aguardando o recebimento de dados. Assim que o cliente envia as chaves de identificação ao servidor, se houver dados de ticks enviados pelo serviço MetaTrader, esses dados são exibidos, neste momento, no terminal do cliente. A demonstração é feita com EURUSD e GBPUSD, pois nesse momento ambas as janelas de gráficos estão abertas no terminal MetaTrader 5. Isso é ilustrado nas capturas de tela a seguir.


Tentei capturar em GIF todo o fluxo mostrado nas capturas de tela acima, para torná-lo mais visual.

Conclusão
Este artigo aborda o uso de programação com sockets para enviar dados de ticks do MetaTrader 5 para uma aplicação Python usando serviços do MetaTrader, além de um servidor e um cliente Python. Se conseguimos transferir dados de ticks como neste artigo, também podemos transferir outras informações dos gráficos. Usei os serviços, mas também é possível usar scripts, indicadores ou EAs. Além do Python, também é possível usar outras linguagens de programação.
Além disso, este artigo tenta contornar a dependência do sistema operacional Windows. O MetaTrader e a biblioteca Python do MetaTrader 5 dependem do sistema operacional Windows. Em vez de usar a biblioteca Python no MetaTrader 5, é possível usar o protocolo de sockets para transmitir dados do MetaTrader para a biblioteca desejada, para processar e analisar os dados do MetaTrader com mais flexibilidade e abrangência.
| Nome do arquivo | Descrição |
|---|---|
| TickSocketService.mq5 | Arquivo MQL5 que contém o código para conexão com o servidor de sockets na porta 9070 e posterior envio dos dados de ticks |
| tick_server.py | O servidor de sockets em Python abre a porta 9070 para o MetaTrader e a porta 9071 para outros clientes |
| tick_client.py | O socket cliente em Python se conecta à porta 9071 e recebe os dados enviados pelo servidor |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/18680
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Caminhe em novos trilhos: Personalize indicadores no MQL5
Do iniciante ao especialista: Reporting EA - Configuração do fluxo de trabalho
Está chegando o novo MetaTrader 5 e MQL5
Análise espectral singular em MQL5
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Artigo publicado Transferindo dados de ticks do MetaTrader para o Python via sockets usando serviços MQL5:
Autor: lazymesh
Artigo interessante e útil, parabéns.
Muito obrigado
Trabalho interessante. É possível criar um servidor de gerenciamento de risco dessa forma para uma rede de terminais que o escutam?
Sim, é possível
Trabalho interessante. É possível criar, dessa forma, um servidor de gerenciamento de risco para uma rede de terminais que o escutam?