preview
Simulação de mercado (Parte 13): Sockets (VII)

Simulação de mercado (Parte 13): Sockets (VII)

MetaTrader 5Testador |
334 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Simulação de mercado (Parte 12): Sockets (VI), fizemos um servidor em Python que seria capaz de responder a diversos clientes. Este funcionava devido ao uso de threads. Acredito que foi passado a ideia básica de como uma thread funciona. Mas principalmente, espero que você caro leitor, tenha conseguido entender como um servidor, consegue de alguma maneira, fazer um mini chat funcionar. A ideia base destas explicações sobre soquetes, não visa de fato ensinar tudo sobre eles. O que queremos e precisamos é que você, caro leitor entenda como os soquetes funcionam. Isto por que este conhecimento será necessário depois, aqui no replay/simulador.


Voltando ao problema no Excel

Mas apesar do servidor em Python, estar funcionando, a fim de que diversos clientes consigam ser mantidos por ele. Ele não está de fato adequado para que o usemos no Excel. Ou melhor dizendo, se você usar o xlwings para colocar este servidor no Excel, você terá problemas, com relação a interação com o Excel. Mas por que, se estamos usando threads a fim de não bloquear a execução do código?

Aqui mora uma questão um pouco complicada de ser observada e notada. Bem, pelo menos para quem tem menos experiência no que rege um programa usar outro que não faz parte dele. Esta frase talvez ficou complicada. Mas vamos tentar entendê-la. Quando usamos um script em VBA, isto dentro do Excel. Estamos na verdade rodando um programa. Ok, se este script, chamar alguma outra aplicação, muitas das vezes, ele irá de fato fazer com que o Excel, espere a aplicação terminar. Mas isto não é sempre. Para que o Excel, não precise esperar a aplicação terminar. Você pode usar uma aplicação que não se ligue de fato ao Excel. Ou seja, você terá o Excel aberto, ao mesmo tempo uma outra aplicação que foi aberta pelo VBA. Ambos podem conviver sem competir pela CPU. Seria mais ou menos você pedir ao Excel para abrir o Word. Não importa se o Word ficar ali parado. Ele não irá interferir com o Excel.

No entanto, o mesmo não se aplica, ao que está sendo proposto. Que é justamente fazer com que o MetaTrader 5 e o Excel troquem informações. Mesmo que o MetaTrader 5 ou o Excel não venham a competir pela CPU. O mesmo não acontece com o script em Python que criar um soquete para esta comunicação ocorrer. Isto pelos modelos que foram mostrados até aqui de servidores. Para deixar isto mais simples de explicar aqui. Vamos pegar o último script visto no artigo anterior. Este pode ser visto logo abaixo:

01. import socket as sock
02. import threading as thread
03. 
04. CONN_LIST = []
05. 
06. def NewClientHandler(conn, addr):
07.     global CONN_LIST
08.     CONN_LIST.append(conn)
09.     print(f"Client [%s:%s] is online..." % addr)
10.     while True:
11.         msg = conn.recv(512).decode().rstrip(None)
12.         if msg:
13.             print(f"Message from [%s:%s]: {msg}" % addr)
14.             for slave in CONN_LIST:
15.                 if slave != conn:
16.                     slave.send(f"[{addr[0]}]: {msg}\n\r".encode())
17.             if msg.lower() == "/see you later":
18.                 break
19.     print(f"Client [%s:%s] is disconnecting..." % addr)
20.     conn.close()
21.     CONN_LIST.remove(conn)
22. 
23. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
24. server.bind(('0.0.0.0', 27015))
25. server.listen()
26. print("Waiting connections...")
27. while True:
28.     conn, addr = server.accept()
29.     conn.send("Wellcome to Server.\n\r".encode())
30.     thread.Thread(target=NewClientHandler, args=(conn, addr)).start()

Código em Python

Apesar deste script não poder ser usado diretamente no xlwings. Isto para que o servidor seja criado. Podemos fazer algumas poucas mudanças e ele passará a poder ser utilizado. Então o mesmo código visto acima passará a ser o que é visto logo abaixo:

01. import socket as sock
02. import threading as thread
03. 
04. CONN_LIST = []
05. 
06. def NewClientHandler(conn, addr):
07.     global CONN_LIST
08.     CONN_LIST.append(conn)
09.     print(f"Client [%s:%s] is online..." % addr)
10.     while True:
11.         msg = conn.recv(512).decode().rstrip(None)
12.         if msg:
13.             print(f"Message from [%s:%s]: {msg}" % addr)
14.             for slave in CONN_LIST:
15.                 if slave != conn:
16.                     slave.send(f"[{addr[0]}]: {msg}\n\r".encode())
17.             if msg.lower() == "good bye":
18.                 break
19.     print(f"Client [%s:%s] is disconnecting..." % addr)
20.     conn.close()
21.     CONN_LIST.remove(conn)
22. 
23. def InitServer(HOST, PORT):
24.     server = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
25.     server.bind((HOST, PORT))
26.     server.listen()
27.     print("Waiting connections...")
28.     while True:
29.         conn, addr = server.accept()
30.         conn.send("Wellcome to Server.\n\r".encode())
31.         thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
32. 
33. if __name__ == '__main__':
34.     InitServer('localhost', 27015)

Código em Python

Muito bem. Note que adicionamos muito pouco código. Mas este nos permite tanto usar o script diretamente no Python. Ou usar o xlwings para poder executar ele pelo VBA do Excel. Então no VBA, você pode usar o seguinte comando a fim de fazer com que o servidor seja acionado pelo Excel:

Public Sub ExecuteServer()
    RunPython ("import Server_Thread; Server_Thread.InitServer('127.0.0.1', 27014)")
End Sub

Código em VBA

Vamos ver o que está acontecendo aqui. Isto para quem não tem acompanhado esta sequência, ou não sabe trabalhar no VBA. Este pequeno script acima em VBA deverá ser chamado por algum controle que você deverá adicionar no Excel. Isto é uma tarefa extremamente simples de ser feita. Você adiciona um controle, por exemplo um botão no Excel. E nas propriedades deste botão, você diz que ele deverá executar este script visto acima.

Mas o que este script em VBA está fazendo? Bem, ele diz ao xlwings para executar o script em Python visto acima. Note que no script em Python, é possível dizer qual será a porta e o host onde o servidor estará. Então os argumentos que vemos em Server_Thread.InitServer, indicam justamente isto. Qual o host, ou melhor para ficar mais claro. Qual é o endereço que o servidor espera as conexões. Assim como qual é a porta. Note que estou definindo uma porta diferente da usada no script em Python. Isto para realmente ficar claro que o servidor estará sendo definido e chamado pelo Excel.

Ok, se você fizer tudo corretamente, irá no final perceber que o Excel abrirá o Python e o servidor estará disponível. Assim ele funcionará da mesma forma que funciona quando o rodamos diretamente no Python. Mas existe um detalhe. Você notará que o Excel irá ficar bastante estranho. Sendo bastante difícil de fazer as coisas nele. Isto por que o servidor em Python estará competindo com o Excel pelo uso da CPU. Mas por que? Nosso servidor não está usando Thread? Sim. O servidor está usando threads. Mas estas não estão em todo o servidor. Existe uma parte no código que bloqueia. Esta parte é justamente a linha 29 ou 28 no código original. Se você olhar irá ver que nesta linha temos uma chamada a função accept. Esta função é bloqueante, e é justamente ela que fica competindo com o Excel no uso da CPU.

Mas existe alguma forma de podermos resolver isto? Sim, existe de fato meios de resolver este ponto. O grande detalhe é: Quanto você pretende investir na solução. Digo isto pois, você pode criar uma thread dentro do VBA a fim de que o script em Python venha a rodar diretamente dentro de uma thread. Ou, você pode procurar uma outra solução para este mesmo problema.

De qualquer forma alguma coisa deverá ser feita. Isto para impedir que a função accept, fique competindo com o Excel no uso da CPU.


Começando a usar a função SELECT

Uma das soluções mais simples e que ao mesmo tempo, não nos força a fazer um verdadeiro malabarismo em termos de programação. É usar a função select no código do servidor. Esta função foi vista em um outro código, aqui nesta parte sobre soquetes. Você pode conferir isto ao olhar na linha 69 do código do servidor do Mini Chat feito em C++. Tal código se encontra no artigo: Simulação de mercado (Parte 09): Sockets (III).

Veja que não é uma solução assim tão complicada. Porém para fazermos algo parecido em Python, o código será ligeiramente diferente do que foi visto no C++. Se você fizer o uso de um código idêntico ao C++, o programa irá bloquear na chamada de select e não mais em accept. Para que as coisas fiquem um pouco mais claras, vamos ver como o código do servidor em Python deverá ser feito. Isto pode ser visto no script logo abaixo:

01. import socket as sock
02. import threading as thread
03. import select
04. import xlwings as xl
05. import time
06. 
07. CONN_LIST = []
08. INPUTS = []
09. WB = None
10. LINE = 1
11. 
12. def WriteMessageInExcel(msg):
13.     global WB
14.     global LINE
15.     if WB == None:
16.         WB = xl.Book.caller()
17.     WB.sheets[0].cells(LINE, 1).value = msg
18.     LINE = LINE + 1
19. 
20. def NewClientHandler(conn, addr):
21.     global CONN_LIST
22.     CONN_LIST.append(conn)
23.     print(f"Client [%s:%s] is online..." % addr)
24.     while True:
25.         msg = conn.recv(512).decode().rstrip(None)
26.         if msg:
27.             print(f"Message from [%s:%s]: {msg}" % addr)
28.             for slave in CONN_LIST:
29.                 if slave != conn:
30.                     slave.send(f"[{addr[0]}]: {msg}\n\r".encode())
31.             if msg.lower() == "good bye":
32.                 break
33.     print(f"Client [%s:%s] is disconnecting..." % addr)
34.     conn.close()
35.     CONN_LIST.remove(conn)
36. 
37. def InitServer(HOST, PORT):
38.     global INPUTS
39.     server = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
40.     server.bind((HOST, PORT))
41.     server.listen()
42.     INPUTS.append(server)
43.     WriteMessageInExcel("Waiting connections...")
44.     while True:
45.         read, write, err = select.select(INPUTS, [], [], 1)
46.         for slave in read:
47.             if slave is server:
48.                 conn, addr = slave.accept()
49.                 conn.send("Wellcome to Server in Python.\n\r".encode())
50.                 thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
51.                 WriteMessageInExcel(f"Client [%s:%s] is online..." % addr)
52.         WriteMessageInExcel("Ping...")
53.         time.sleep(1)        
54. 
55. if __name__ == '__main__':
56.     InitServer('localhost', 27015)

Código em Python

Note que foram feitas algumas poucas mudanças. Estou fazendo as coisas aos poucos para que você caro leitor, que não tiver experiência de usar soquetes em Python, consiga entender o que está sendo mudando. Perceba que agora temos algumas coisas extras nas linhas oito até dez. Tais coisas servem para dar apoio ao select e ao Excel via xlwings.

Muito bem, para facilitar a codificação, e também debugar e apresentar algumas informações. Temos um pequeno procedimento na linha 12. Este visa colocar dados no Excel. Note que na linha 15, verifico se a variável foi inicializada, como na primeira chamada ela não estará inicializada, a linha 16 será executada. Mas somente uma única vez. Já na linha 17, colocamos uma mensagem na corrente linha indicada na planilha. E na linha 18, incrementamos para podermos colocar a próxima chamada na próxima linha.

Sei que este código não está nada bonito. Mas a ideia aqui, não é beleza e sim funcionalidade. Precisamos que ele funcione, e não seja bonito ou agradável de ser utilizado pelo VBA.

Agora quero que você preste atenção a linha 45 deste script. Note que ali, estamos fazendo uma chamada a select. Diferente do código em C++ que mencionei acima. Aqui temos um parâmetro extra. Tal parâmetro é justamente o valor um. Este indica quanto tempo select ficará esperando algo acontecer. Tal tempo é dado em segundos, no caso select ficará esperando um segundo antes do script continuar. Você pode usar um valor menor. Para isto bastará usar um número em ponto flutuante. Mas se você não sabe como fazer isto, ou está na dúvida. Não se preocupe, depois irei mostrar como fazer isto. Mas é preciso se atentar ao seguinte fato: Se você não colocar este valor, select bloqueará, até que algum evento aconteça no soquete do servidor.

Para entender, isto de forma mais clara, você pode ver que na linha 52, imprimimos uma mensagem no Excel. E na linha 53, esperamos mais um segundo. Dando assim um delay no loop de dois segundos. Para o que queremos é mais do que o suficiente. Então é isto. Se a linha 45 bloquear a execução, aguardando um evento no soquete, não teremos um ping a cada dois segundos. Caso não exista o bloqueio, teremos o ping. Pois bem ao executar este script via VBA, você terá o que pode ser visto abaixo:

Muito bom, mas por que precisamos do laço for da linha 46? Bem, este laço é necessário para que possamos verificar que tipo de coisa aconteceu no soquete. Isto considerando que ainda não entramos na thread. Então se alguma conexão for tentada por parte de algum usuário, este laço for servirá para que tenhamos ciência do que ocorreu. Assim todo o restante permanece igual ao que era antes. Um detalhe importante: Sem este laço for da linha 46, o servidor irá bloquear esperando que algo aconteça na função accept, que está presente na linha 48. Mas com este teste na linha 47 dentro do laço. Podemos perceber que um cliente está tentando se conectar. Apesar de que este servidor ainda assim não permitir que o Excel funcione sem competir com o Python. Já temos uma melhora considerável no sistema.

Mas todo o problema que ainda temos está na linha 44. O fato de que ali entramos no loop infinito no script Python, é que faz com que o script fique competindo com o Excel no uso da CPU. Mas não podemos simplesmente, remover este laço. Isto por que se isto for feito da forma como o script está implementado, o servidor irá ser encerrado. Então precisamos fazer algumas mudanças a fim de evitar que isto aconteça. Ou seja, precisamos entrar e sair do script sem que o servidor encerre suas conexões. Mas antes de fazermos isto, vamos ver um outro código intermediário ao que precisamos de fato criar. Para separar um pouco as coisas, vamos a um novo tópico.


Servidor de Mini Chat em uma classe

Apesar de que muitos não gostam de desenvolver ou implementar os códigos, fazendo com que eles sejam orientados a objeto. Existem grandes vantagens em se fazer tal coisa. Primeiramente podemos fazer com que o servidor implementado em Python, tenha um funcionamento mais simples. Pelo menos em termos de programação. Isto por que podemos remover muito facilmente a Thread da versão vista no tópico anterior. Assim ficaremos com um funcionamento muito parecido com o que foi visto na implementação em C++. Para fazer isto, um novo código surgirá. Mas ele é completamente compatível com o que foi visto até aqui. Claro, neste primeiro momento não o lançaremos via Excel. Vamos usar o Python puro e simples. Então o novo script, mas desta vez usando uma programação orientada a objetos, pode ser visto logo abaixo:

01. import socket as sock
02. import select
03. import time
04. 
05. class MiniChat:
06.     def __init__(self, HOST : str = 'localhost', PORT : int = 27015) -> None:
07.         self.server = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
08.         self.server.setblocking(0)
09.         self.server.bind((HOST, PORT))
10.         self.server.listen()
11.         self.CONN_LIST = [self.server]
12.         print(f"Waiting connections in {PORT}...")
13. 
14.     def BroadCastData(self, conn, info) -> None:
15.         for sock in self.CONN_LIST:
16.             if sock != self.server and sock != conn:
17.                 sock.send((info + "\n\r").encode())
18. 
19.     def ClientDisconnect(self, conn) -> None:
20.         msg = "Client " + str(conn.getpeername()) + " is disconnected..."
21.         self.BroadCastData(conn, msg)
22.         print(msg)
23.         conn.close()
24.         self.CONN_LIST.remove(conn)
25. 
26.     def Shutdown(self) -> None:
27.         while self.CONN_LIST:
28.             for conn in self.CONN_LIST:
29.                 self.CONN_LIST.remove(conn)
30. 
31.     def LoopMessage(self) -> None:
32.         while self.CONN_LIST:
33.             read, write, err = select.select(self.CONN_LIST, [], [], 1)
34.             for sock in read:
35.                 if sock is self.server:
36.                     conn, addr = sock.accept()
37.                     conn.setblocking(0)
38.                     self.CONN_LIST.append(conn)
39.                     conn.send("Wellcome to Server Chat in Python.\n\r".encode())
40.                     self.BroadCastData(conn, "[%s:%s] entered room." % addr)
41.                     print("Client [%s:%s] connecting..." % addr)
42.                 else:
43.                     try:
44.                         data = sock.recv(512).decode().rstrip(None)
45.                         if data:
46.                             if data.lower() == '/shutdown':
47.                                 self.Shutdown()
48.                             self.BroadCastData(sock, str(sock.getpeername()) + f":{data}")
49.                         else:
50.                             self.ClientDisconnect(sock)
51.                     except:
52.                         self.ClientDisconnect(sock)
53.             time.sleep(0.5)
54. 
55. if __name__ == '__main__':
56.     MyChat = MiniChat()
57.     MyChat.LoopMessage()
58.     print("Server Shutdown...")

Código em Python

Se você tiver alguma dúvida sobre este código, pode consultar a documentação do Python. Mas acredito que você, caro leitor, que está acompanhando as explicações, não terá de fato muita dificuldade em entender o script. Se bem que ele pode parecer estranho a primeira vista. Mas tudo isto se deve ao modo como o Python é implementado. Então consulte a documentação para entender de fato como as coisas funcionam. Mas vamos dar uma rápida passada, só para lhe ajudar um pouco.

A classe se inicia na linha cinco e termina na linha 53. Na linha 56, fazemos o uso do constructor da classe, ou seja, o procedimento __init__. Este se encontra na linha seis. Este procedimento irá de fato criar o servidor. Mas o servidor ainda não estará ouvido nenhuma conexão. No entanto, quando o __init__ retorna, teremos a chamada na linha 57. Esta irá direcionar a execução para a linha 31. Observe que neste procedimento da linha 31, o que fizemos foi remover a thread. Ou seja, agora o servidor irá fazer uso do mesmo tipo de acompanhamento que era feito no servidor em C++. Preste atenção ao fato de que ainda estamos dentro de um laço. Este na linha 32. Porém quero que você tente entender o seguinte: Este laço, tanto pode estar na classe, quanto fora dela. Que mesmo assim o servidor irá conseguir cumprir com o seu papel.

Por que isto é importante para nós? O motivo é que se você colocar o laço fora da classe, precisará ter uma forma de verificar se o servidor ainda pode estar ativo. Com o laço dentro da classe, tudo que precisamos para criar o servidor, são as linhas 56 e 57. Isto nos permite empacotar este servidor para ser importado dentro de outros scripts de Python.

Agora vem a parte interessante. Se você ignorar o fato de que temos um laço na linha 32, este mesmo servidor poderia ser empacotado e levado para dentro do Excel. Isto para que o xlwings pudesse usar ele, de uma forma um pouco diferente, do que foi visto até o momento. Porém, está implementação vista acima, foi feita desta forma propositalmente. Isto por que você facilmente irá conseguir compreender as mudanças entre cada uma das versões do servidor em Python. Assim perceberá sem muito esforço, como deixaremos o servidor em Python, não ficar competindo o uso da CPU com o Excel. Então, imaginando que você tenha de fato entendido o código, vamos mudar novamente este mesmo código visto acima. Agora ele poderá ser empacotado sem problemas. Podendo ser usado a qualquer momento e em qualquer script.

01. import socket as sock
02. import select as sel
03. 
04. class MiniChat:
05.     def __init__(self, HOST : str = 'localhost', PORT : int = 27015) -> None:
06.         self.server = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
07.         self.server.bind((HOST, PORT))
08.         self.server.listen()
09.         self.CONN_LIST = [self.server]
10.         print(f"Waiting connections in {PORT}...")
11. 
12.     def __BroadCastData(self, conn, info) -> None:
13.         for sock in self.CONN_LIST:
14.             if sock != self.server and sock != conn:
15.                 sock.send((info + "\n\r").encode())
16. 
17.     def __ClientDisconnect(self, conn) -> None:
18.         msg = "Client " + str(conn.getpeername()) + " is disconnected..."
19.         self.BroadCastData(conn, msg)
20.         print(msg)
21.         conn.close()
22.         self.CONN_LIST.remove(conn)
23. 
24.     def ExistConnection(self) -> bool:
25.         if self.CONN_LIST:
26.             return True
27.         return False
28.     
29.     def CheckMessage(self, sleep) -> str:
30.         read, write, err = sel.select(self.CONN_LIST, [], [], sleep)
31.         for sock in read:
32.             if sock is self.server:
33.                 conn, addr = sock.accept()
34.                 self.CONN_LIST.append(conn)
35.                 conn.send("Wellcome to Server Chat in Python.\n\r".encode())
36.                 self.__BroadCastData(conn, "[%s:%s] entered room." % addr)
37.                 print("Client [%s:%s] connecting..." % addr)
38.             else:
39.                 try:
40.                     data = sock.recv(512).decode().rstrip(None)
41.                     if data:
42.                         if data.lower() == '/shutdown':
43.                             self.CONN_LIST.clear()
44.                         self.__BroadCastData(sock, str(sock.getpeername()) + f":{data}")
45.                         return str(sock.getpeername()) + f"> {data}"
46.                     else:
47.                         self.__ClientDisconnect(sock)
48.                 except:
49.                     self.__ClientDisconnect(sock)
50.         return ""
51. 
52. if __name__ == '__main__':
53.     MyChat = MiniChat()
54.     while MyChat.ExistConnection(0.2):
55.         info = MyChat.CheckMessage()
56.         if info:
57.             print(f"{info}")
58.     print("Server Shutdown...")

Código em Python

Muito bem. Aqui neste script visto logo acima, fiz algumas melhorias no código, a fim de que ele pudesse fazer o que falei acima. Ou seja, que ele possa ser empacotado e usado sem problemas em outras aplicações. Mas vamos entender o que está se passando aqui agora. Pois este código já não ficará de fato preso aguardando algum evento ocorrer na rede.

Em primeiro lugar, os procedimentos BroadCastData e ClientDisconnect, agora são privativos da classe. Ou seja, eles neste código, não poderão ser acessados, diretamente, fora do corpo da classe. Se bem que existe meios de acessar tais procedimentos, que são privativos. Mas não irei dizer como fazer, para que você não fique tentado a fazer isto em outros momentos. Quero que você, tenha a visão de que é possível colocar coisas privativas no Python. E não tente acessar procedimentos ou funções que são tidas como privativas. Se você tentar fazer isto, o Python irá lhe informar, esta tentativa como sendo um erro. Novamente, existe uma forma de acessar procedimentos privativos dentro do Python. Fica ao seu inteiro trabalho procurar saber como fazer isto. Mas sinceramente desaconselho o uso de tal prática. Já que isto quebra uma das premissas do encapsulamento de código. Se o procedimento ou função, são tidos como privativos. Eles tem o seu motivo de ter sido declarado assim.

Bem com isto, temos apenas dois procedimentos, que são realmente públicos. O ExistConnection e CheckMessage. O ExistConnection irá nos devolver um valor booleano. Como você pode ver na linha 24. Este valor retornado, tem como finalidade servir para que fora do corpo da classe, tenhamos como verificar se o servidor ainda está ou não ativo. Ou melhor dizendo, se temos ou não alguma conexão sendo observada.

Já o CheckMessage, que se encontra na linha 29, irá retornar a mensagem postada por algum cliente. Mas não somente isto. Note que na linha 43, encerraremos a pedido de algum cliente o servidor. Isto parece meio que dramático. Já que um cliente qualquer, não poderia, a princípio fazer este tipo de coisa. Mas já que este código é voltado apenas para fins didáticos. Não vejo problema em fazer isto desta forma.

De qualquer maneira, na linha 45, retornaremos o que foi digitado por algum cliente. Veja que usamos tanto o nome do cliente, quanto o que foi digitado como sendo o retorno da função. Isto é importante para fins de análise e experimentação.

Mas o que realmente nos interessa. E agora vem a parte interessante neste código. É o conteúdo entre as linhas 53 e 58. Observe que o laço que existia dentro da classe, no código anterior, foi removido e jogado para este ponto. Para ser mais exato, ele agora está na linha 54. Já na linha 55 capturamos o que algum cliente postou no soquete. E se o valor for de alguma forma relevante. Ou seja, contiver algum dado. Imprimiremos, o dado informado, no terminal que o Python estiver usando. Isto na linha 57. Então tudo que qualquer cliente postar, será visto por todos os demais. Além é claro de ser possível de acompanhar diretamente no terminal onde o servidor estiver rodando.

Agora um detalhe que você pode estar curioso. Mas este servidor ficará quanto tempo, em modo de espera. Isto quando nada estiver acontecendo no soquete que ele estiver observando? Esta pergunta é bastante válida. Lembra que no código anterior, falei que a função select, ficaria um segundo esperando. Isto por que este foi o tempo definido lá.

Pois bem, aqui ele ficará menos tempo. E como o interesse de fato é não precisar mexer no código da classe. Iremos passar o valor diretamente para dentro da classe. Isto é feito na linha 54. Observe que ali estamos definindo um valor inferior a um. No caso o valor definido é de zero ponto dois. Este é o tempo em segundos que ele ficará em espera, ou seja, menos de um segundo de fato. Antes da função select abortar e prosseguir em frente na execução do código.

Dependendo do trabalho a ser feito com as informações postadas no soquete, e o tempo de processamento. Podemos reduzir ainda mais este tempo. Lembrando que, diferente do modelo em que usávamos thread. Aqui o servidor somente observará o que cada uma das conexões estará fazendo. No caso do modelo que usávamos thread, cada conexão era independente. Isto torna o processamento deste modelo daqui, um pouco mais lento do que quando se usa thread para ler e escrever no soquete.

Agora preste bastante atenção aos seguintes detalhes. Este servidor, que pode ser visto no código acima. Funciona perfeitamente quando a ideia e colocar ele, sendo executando em um prompt. Mesmo com a limitação de que se houver muitos clientes, ele irá ler conexão por conexão de forma sequencial.

Porém, para que consigamos transferir informações entre o Excel e o MetaTrader 5. E isto de forma bidirecional. Este script em Python ainda não está nada adequado. Apesar de já se encontrar em um nível bem mais avançado. Tudo e absolutamente tudo que foi visto até o presente momento, funciona. Mas não de forma a promover que o script em Python trabalhe sem ficar competindo com o Excel a fim de usar a CPU.

Mesmo este último script, não consegue de fato nos permitir isto. Apesar de ele já estará nos dando uma boa ideia de como fazer as coisas. Se você não está conseguindo ver isto. Não se preocupe. De fato ver isto, não é algo assim tão simples. É preciso um pouco de experiência em programação paralela.

Você deve ter notado que em quase todos estes últimos scripts. Eu não tenho usado o xlwings. Por que? Por um acaso ele não se mostrou adequado para fazer isto? Na verdade, e lá no fundo. Qualquer coisa que você venha a utilizar, sem que seja o básico do Python, não trará uma boa experiência no uso do Excel junto com o Python. E para que você não me entenda mal. Ou que tais palavras venham a ser mal interpretadas. Deixe-me tentar esclarecer o que acabei de dizer.

Quando você desenvolve algo, seja no xlwings, ou qualquer outro pacote que nos permita ler e escrever diretamente no Excel. Você na verdade deve notar que todos os programas, funções ou procedimentos. Executam e logo finalizam a sua tarefa. Eles não ficam ali, dentro de um loop. E por mais que você tente fazer as coisas de uma forma diferente. Tais soluções jamais alcançaram o que realmente precisamos fazer. Novamente, o problema todo é o fato de que o script de um servidor, deve sempre se manter em um loop fechado. Seja aguardando ou não algo ser colocado no soquete.

O fato é que o script executará em um loop. No caso de um cliente, a coisa pode ser feita de uma maneira um pouco diferente. Você pode simplesmente, se conectar ao servidor, e no momento que isto ocorrer. O servidor irá postar a informação que deverá ser observada pelo cliente. Uma vez que o cliente receba tal informação. Ele, cliente, pode fechar a conexão. Finalizando assim o script e disponibilizando os dados para o Excel, a fim de que o Excel os trate de forma adequada. Este tratamento, pode ser feito tanto pelo Python quanto pelo VBA.

Na verdade esta questão pouco importa. Mas o que quero chamar a sua atenção, meu caro leitor. É para o fato de que se no lugar de um servidor, fosse usado um cliente. O código seria completamente diferente. Isto por que poderíamos usar algum temporizador, feito no VBA a fim de executar o cliente feito em Python. E não teríamos o problema de que o script em Python, ficaria competindo pela CPU com o Excel. Este é o fato, pelo qual digo que usar os pacotes que muitos usam normalmente. Não será o suficiente. Precisamos de uma outra solução.


Considerações finais

Neste artigo mostrei como você pode criar um servidor em Python que não fique bloqueado, enquanto espera algo acontecer no soquete. Porém, nenhum destes códigos que foram apresentados. Realmente conseguem cumprir o nosso objetivo. E como programador, você deve sair da zona de conforto. Programação é isto: Procurar uma solução para um problema. Diferente do que muitos imaginam. Que é o estilo: Copiar e colar. Programação é algo mais artístico. Algo que realmente nos exige ir além desbravando e procurando uma solução para os problemas.

Pois bem, como falei no final do tópico anterior. Usar um cliente feito em Python, junto com o Excel. Garante que o Excel não ficará estranho, a ponto de ficar impossível fazer algo nele. Porém quero, e vou mostrar como fazer um servidor em Python ser executado no Excel. Isto de forma que o Excel possa ser usado normalmente. Muitos poderiam dizer: Mas por que manter o Excel aberto? Faça os cálculos, atualize tudo, seja via Pandas, xlwings ou qualquer outro pacote que permite manipular arquivos no Excel e estará tudo certo.

Bem, esta seria a solução mais simples. Porém quero mostrar que dá para realmente fazer a coisa como imaginado. Para que você possa entender esta motivação. Pense um pouco, em um usuário médio. Por mais que você construa uma interface agradável e simples usando o Python, de modo que o usuário não precisaria do Excel para analisar os dados. Muitos destes usuários iriam de fato torcer o nariz para a sua solução. Eles iriam atrás e com razão de um programador que conseguisse dar conta do problema.

Isto para que este mesmo usuário, pudesse usar o Excel a fim de controlar algo que seria feito no MetaTrader 5. E você como programador, e querendo ser um profissional, deve entender que se a solução não existe ainda, você deverá criar a mesma. Então o tal de copiar e colar não será o suficiente.

No próximo artigo mostrarei como isto será feito. Então estude bem todo este material, pois você precisará dele para entender o que será feito no próximo artigo.

ArquivoDescrição
Experts\Expert Advisor.mq5
Demonstra a interação entre o Chart Trade e o Expert Advisor (É necessário o Mouse Study para interação)
Indicators\Chart Trade.mq5Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação)
Indicators\Market Replay.mq5Cria os controles para interação com o serviço de replay/simulador  (É necessário o Mouse Study para interação)
Indicators\Mouse Study.mq5Permite interação entre os controles gráficos e o usuário (Necessário tanto para operar o replay simulador, quanto no mercado real)
Services\Market Replay.mq5Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema)
Code VS C++\Servidor.cppCria e mantém um soquete servidor criado em C++ (Versão Mini Chat)
Code in Python\Server.pyCria e mantém um soquete em python para comunicação entre o MetaTrader 5 e o Excel
Scripts\CheckSocket.mq5Permite efetuar um teste de conexão com um soquete externo
Indicators\Mini Chat.mq5Permite implementar um mini chat via indicador (Necessário uso de um servidor para funcionar)
Experts\Mini Chat.mq5Permite implementar um mini chat via Expert Advisor (Necessário uso de um servidor para funcionar)
Arquivos anexados |
Anexo.zip (560.03 KB)
Redes neurais em trading: Segmentação de dados com base em expressões de referência Redes neurais em trading: Segmentação de dados com base em expressões de referência
Ao analisarmos a situação de mercado, a dividimos em segmentos individuais, identificando as principais tendências. No entanto, os métodos tradicionais de análise geralmente se concentram em um único aspecto, limitando a percepção. Neste artigo, apresentaremos um método que permite destacar vários objetos, oferecendo uma compreensão mais completa e em camadas da situação.
Do básico ao intermediário: Indicador (III) Do básico ao intermediário: Indicador (III)
Neste artigo iremos ver como declarar diversos indicadores de plotagem, como DRAW_COLOR_LINE e DRAW_FILLING. Além é claro, aprender como plotar indicadores múltiplos de uma forma muito simples, prática e pouco trabalhosa. Agora que realmente pode mudar a sua forma de enxergar o MetaTrader 5 e o mercado em geral.
Algoritmo de Irrigação Artificial — Artificial Showering Algorithm (ASHA) Algoritmo de Irrigação Artificial — Artificial Showering Algorithm (ASHA)
Este artigo apresenta o Algoritmo de Irrigação Artificial (ASHA), um novo método metaheurístico desenvolvido para resolver problemas gerais de otimização. Baseado na simulação dos processos de fluxo e acúmulo de água, este algoritmo constrói o conceito de um campo ideal, no qual cada unidade de recurso (água) é convocada para buscar a solução ótima. Descubra como o ASHA adapta os princípios de fluxo e acúmulo para distribuir recursos de forma eficiente em um espaço de busca e conheça sua implementação e os resultados dos testes.
Algoritmo do Campo Elétrico Artificial — Artificial Electric Field Algorithm (AEFA) Algoritmo do Campo Elétrico Artificial — Artificial Electric Field Algorithm (AEFA)
Este artigo apresenta o Algoritmo do Campo Elétrico Artificial (AEFA), inspirado na lei de Coulomb da força eletrostática. Por meio de partículas carregadas e suas interações, o algoritmo simula fenômenos elétricos para resolver tarefas complexas de otimização. O AEFA demonstra propriedades únicas em relação a outros algoritmos baseados em leis da natureza.