![Desenvolvendo um EA de negociação do zero (Parte 16): Acessando dados na WEB (II)](https://c.mql5.com/2/49/Developing_a_trading_Expert_Advisor_from_scratch_007_600x314.jpg)
Desenvolvendo um EA de negociação do zero (Parte 16): Acessando dados na WEB (II)
1.0 - Introdução
No artigo anterior, Desenvolvendo um EA de negociação do zero (Parte 15): Acessando dados na WEB (I) eu introduzi toda a lógica e ideias por traz dos métodos de utilizar a plataforma MetaTrader 5 para acessar dados de mercado presentes na WEB, em sites especializados em nos dar informações de mercado.
Lá mostrei os detalhes de como acessar o site, como conseguir encontrar e adquirir as informações de tais sites para serem utilizados na plataforma, mas a coisa toda não fica somente naquilo, já que apenas capturar os dados não faz muito sentido, a parte mais interessante de fato é saber como levar estes dados para dentro da plataforma e poder usá-los em um EA, mas o caminho para fazer isto não é tão obvio, ou melhor dizendo, tão simples a ponto de você conseguir fazer, sem de fato conhecer e entender todos os recursos que estão presentes no MetaTrader 5.
2.0 - Planejamento e Implementação
A questão é se você não leu e não entendeu o artigo anterior, peço por favor que leia o mesmo e tente entender todos os conceitos presentes lá, pois aqui a coisa será para gente grande, vamos explorar uma serie enorme de coisas passando por problemas e finalmente chegando a uma solução que é ao mesmo tempo bela para não dizer brilhante, já que vamos utilizar o MetaTrader 5 de uma forma muito pouco explorada, digo isto por ter tido dificuldades em achar referencias do uso de alguns recursos presentes na plataforma, mas aqui vou tentar explicar como usar um destes recursos.
Então se preparem, e mãos a obra.
2.1 - Acessando os dados da WEB via EA
Esta é a parte mais interessante que se pode fazer dentro deste sistema, apesar de ser algo simples de ser feito, é de longe a coisa mais perigosa se for mal planejada, perigosa por conta que pode deixar o EA travado esperando o retorno de alguma resposta do servidor, mesmo que seja apenas por alguns instantes.
A lógica aqui é apresentada na imagem abaixo:
veja que o EA se comunica diretamente com o servidor WEB que contém as informações que desejamos capturar, um exemplo de código que executa exatamente assim, é visto logo abaixo, o código esta na integra.
#property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ int OnInit() { EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { Print(GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D)); } //+------------------------------------------------------------------+ string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit) { string headers, szInfo = ""; char post[], charResultPage[]; int counter; if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1) return "Bad"; for (int c0 = 0, c1 = StringLen(szTest); c0 < c1; c0++) if (szTest[c0] != charResultPage[iTest + c0]) return "Failed"; for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error"; for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++); for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]); return szInfo; } //+------------------------------------------------------------------+
Se você reparar, irá ver que o código é exatamente o último código que foi criado no artigo anterior, só que aquele código agora faz parte do EA, e foi adaptado para isto, ou seja se a coisa já funcionava lá, então irá funcionar aqui também. Bem, a diferença é que aqui no EA, eu gerei uma condição, esta condição implica que o código será executado a cada segundo, ou seja o EA irá fazer uma requisição ao servidor WEB que estamos indicando, a cada segundo, e irá aguardar a resposta para somente depois apresentar o dados capturados e voltar a executar outras funções internas, e o ciclo irá se repetir por todo o tempo de vida do EA, o resultado da execução pode ser visto logo abaixo.
Bem, mas apesar de fazer isto desta forma, novamente não aconselho tão prática, por conta do risco do EA ficar travado esperando a resposta do servidor, mesmo que seja por alguns instantes, isto pode comprometer em muito o sistema de negociação da plataforma e do próprio EA, mas se você de fato esta interessado apenas em estudar o método, este sistema já será suficiente para que você aprenda bastante.
Mas se você tiver um servidor local que irá fazer o roteamento entre as informações da WEB e a plataforma, pode ser que este método já será o suficiente, já que caso o sistema faça a requisição o máximo que irá acontecer, é do servidor local ainda não contar com nenhuma informação e responder rapidamente, isto irá lhe poupar dos próximos passos que teremos que passar.
Mas agora vamos ver uma outra forma pelo menos um pouco mais segura de executar este tipo de tarefa, já que vamos utilizar o próprio sistema de thread do MetaTrader 5 para conseguir ter pelo menos um mínimo de segurança e evitar que o EA fique a sujeito as condições do servidor web remoto, podendo travar por alguns instantes esperando o servidor remoto responder a sua requisição, mas ao mesmo tempo vamos dar condições do EA saber o que se passa, conseguindo coletar informações da web.
2.2 - Criando um canal de comunicação
Uma maneira mais adequada e ao mesmo tempo simples de se conseguir fazer a captura de dados na WEB e utilizar tais dados em um EA, e por meio de um canal, porém apesar de funcionar, ela em alguns casos não é a mais adequada, pois existem limitações no uso de tais canais, mas pelo menos o EA irá conseguir ter acesso as informações coletadas na WEB sem o risco de ficar esperando o servidor remoto responder.
Vejam que acima eu falei que a maneira mais simples de resolver o problema seria fazer o roteamento das informações, ou seja você iria criar um servidor local, que iria baixar os dados pra você e disponibilizar estes dados para a plataforma MetaTrader 5, mas isto exige um grau de conhecimento e um certo poder de processamento, além de complicar de forma inútil a grande maioria dos casos, mas podemos utilizar os recursos fornecidos pelo MetaTrader 5 para criar um canal muito similar, porém muito mais simples do que fazer o roteamento via servidor local.
A figura abaixo mostra como iremos promover o tal canal.
Ele é criado pelo uso de um objeto, notem que o EA irá pesquisar dentro do objeto, a informação que o script postou lá, para entender como de fato isto funciona precisaremos de 3 códigos, e eles podem ser vistos abaixo, na integra, um será o EA, outro será um cabeçario, que conterá o objeto, e por ultimo um script.
#property copyright "Daniel Jose" #property description "Testing Inner Channel" #property version "1.00" //+------------------------------------------------------------------+ #include <Inner Channel.mqh> //+------------------------------------------------------------------+ int OnInit() { EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { Print(GetInfoInnerChannel()); } //+------------------------------------------------------------------+
O próximo código é o cabeçario a ser usado. vejam que o objeto é declarado aqui de forma a ser compartilhado entre o EA e o script.
//+------------------------------------------------------------------+ //| Canal Intra Process.mqh | //| Daniel Jose | //| | //+------------------------------------------------------------------+ #property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define def_NameObjectChannel "Inner Channel Info WEB" //+------------------------------------------------------------------+ void CreateInnerChannel(void) { long id; ObjectCreate(id = ChartID(), def_NameObjectChannel, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(id, def_NameObjectChannel, OBJPROP_COLOR, clrNONE); } //+------------------------------------------------------------------+ void RemoveInnerChannel(void) { ObjectDelete(ChartID(), def_NameObjectChannel); } //+------------------------------------------------------------------+ inline void SetInfoInnerChannel(string szArg) { ObjectSetString(ChartID(), def_NameObjectChannel, OBJPROP_TEXT, szArg); } //+------------------------------------------------------------------+ inline string GetInfoInnerChannel(void) { return ObjectGetString(ChartID(), def_NameObjectChannel, OBJPROP_TEXT); } //+------------------------------------------------------------------+
e finalmente temos o script a ser usado, este irá substituir a criação de um servidor local, na verdade ele é que faz o trabalho do servidor.
#property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ #include <Inner Channel.mqh> //+------------------------------------------------------------------+ void OnStart() { CreateInnerChannel(); while (!IsStopped()) { SetInfoInnerChannel(GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D)); Sleep(200); } RemoveInnerChannel(); } //+------------------------------------------------------------------+ string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit) { string headers, szInfo = ""; char post[], charResultPage[]; int counter; if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1) return "Bad"; for (int c0 = 0, c1 = StringLen(szTest); (!_StopFlag) && (c0 < c1); c0++) if (szTest[c0] != charResultPage[iTest + c0]) return "Failed"; for (int c0 = 0, c1 = StringLen(szFind); (!_StopFlag) && (c0 < c1); c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error"; for (counter = 0; (!_StopFlag) && (charResultPage[counter + iInfo] == 0x20); counter++); for (;(!_StopFlag) && (charResultPage[counter + iInfo] != cLimit); counter++) szInfo += CharToString(charResultPage[counter + iInfo]); return (_StopFlag ? "" : szInfo); } //+------------------------------------------------------------------+
vejam o seguinte: existe um objeto que é visto pelo EA, e criado pelo script, o fato de fazer assim é por que o EA e o script podem conviver juntos no mesmo gráfico, então este objeto será nosso canal de comunicação entre o EA e o Script. O EA será o cliente e o script será o servidor, já o objeto é o canal de comunicação entre eles. Desta forma o script irá ficar capturando os valores no servidor remoto da WEB e irá colocar o valor encontrado no objeto. O EA irá de tempos em tempos ver qual o valor que esta no objeto, caso o objeto exista, já que se o script não estiver em execução o objeto não deverá estar disponível, mas os momentos em que o EA olha o valor no objeto não irá atrapalhar em nada a execução dele, caso o script trave por conta de estar esperando a resposta do servidor remoto, para o EA isto é irrisório, ele irá continuar o seu trabalho independente do que o script estiver fazendo.
Mas apesar desta solução ser bonita ela não é perfeita, existe um problema nesta solução, e o problema é o script.
Para entender veja o video abaixo, mas prestem atenção a cada um dos detalhes no video.
Tudo funciona as mil maravilhas, e é algo a se esperar, já que este tipo de solução é muito usada nos meios da programação quando estamos desenvolvendo programas do tipo cliente servidor, onde não queremos que um trave o outro, ou seja usamos um canal, seja ele qual for para fazer a comunicação entre os processos, muitas das vezes quando se esta em um mesmo ambiente, este canal será criado com o uso da memória, cria-se uma região isolada, porém compartilhada e visível tanto para o cliente quanto para o servidor. O servidor coloca os dados ali e o cliente quando desejar vai na mesma região e pega os dados disponíveis, ou seja um independe do outro, mas ambos estarão relacionados.
A ideia aqui é utilizar este mesmo principio, só que no entanto a forma como o script funciona, nos gera um problema, quando mudamos o timeframe, o script é encerrado, mesmo que utilizemos um loop infinito ele é encerrado pelo MetaTrader 5, bem quando isto acontece teríamos que reinicializar o script colocando ele novamente no gráfico, mas se a troca de timeframe for constante, isto se tornará um problema, além de ser um tormento ficar toda hora recolocando o script no gráfico.
Isto fora o fato de que você pode acabar se esquecendo de olhar se o script está ou não no gráfico, e assim acabará por utilizar informações erradas, já que da forma como o EA esta codificado não temos nenhuma segurança de que seremos avisados caso o script não esteja no gráfico, isto poderia ser resolvido melhorando o código do EA de forma a testar se o script esta ou não no gráfico, esta tarefa não é difícil de ser implementada, um simples teste de qual foi o momento da ultima postagem do script no objeto já resolveria o problema.
Mas temos uma forma de criar uma solução bastante mais adequada, pelo menos em alguns casos, para dizer a verdade ela é uma solução quase que perfeita, e usaremos este mesmo conceito apresentado acima, só que no lugar do script iremos usar um serviço.
2.3 - Criando um serviço
Esta é uma solução extrema, mas como o script tem o problema de ser encerrado a cada mudança de timeframe, temos que utilizar um outro método, só que ao resolver um problema iremos acabar tendo outro, mas é bom que você saiba quais são os recursos possíveis e como utilizar cada um, mas o principal é conhecer as limitações que cada uma das soluções apresenta, e assim tentar achar um caminho do meio, que permita resolver o problema da melhor forma possível.
Programação é isto dai, tentar resolver um problema se gerar um novo.
A intenção aqui é criar algo parecido com a figura abaixo:
Apesar de parecer simples, os recursos aqui envolvidos são muito pouco explorados de maneira geral, então vou tentar esmiuçar os detalhes envolvidos de forma a ajudar quem estiver procurando saber mais sobre como trabalhar com estes recursos.
2.3.1 Acessando as variáveis globais
Esta parte é tão pouco explorada, que no início até cogitei criar uma dll apenas para dar suporte a este recurso, mas pesquisando a documentação do MQL5 encontrei uma referência a este recurso. Bem, a questão é que precisamos de ter acesso ou criar um ponto em comum entre o serviço e o EA, no caso quando usamos um script, este ponto era o objeto, mas ao usar um serviço não conseguimos fazer a mesma coisa, a solução seria usar uma variável externa, mas ao tentar fazer isto o funcionamento não era o esperado, para mais detalhes veja variáveis externas, lá é explicado como proceder.
Assim a ideia quase morreu, e voltei a pensar no uso de uma dll, mas queria explorar o MetaTrader 5 e o MQL5, então ao olhar o terminal encontrei algo, que pode ser visto na imagem abaixo:
Isto é que eu estava procurando, aqui adicionei uma variável para poder verificar como a coisa poderia ser configurada, e podemos usar apenas valores double, você pode pensar que isto é um problema, apesar de sim em vários casos por nos limitar, será mais que o suficiente quando desejamos transmitir mensagens curtas, que é o nosso caso, você deve pensar no tipo double como sendo na verdade uma string curta de 8 caracteres, então dá para passar valores ou mensagens curtas entre os programas.
Bem a primeira parte do problema esta resolvida, o MetaTrader 5 nos fornece meios de fazer um canal sem necessidade de uma dll apenas para isto, mas agora temos um outro problema: Como acessar estas variáveis pelo programa ?!?! É possível criar elas dentro do programa, seja o EA, script, indicador ou serviço ?!?! Ou somos forçados a usar apenas as declaradas no terminal ?!?!
Estas questões são muito importantes para que possamos de fato usar o recurso, caso não seja possível fazer o uso via programas, teríamos que ir pelo caminho das dlls, mas sim é possível, fazer isto via programas, para mais detalhes veja o seguinte tópico: Variáveis globais do terminal.
2.3.2 Usando uma variável de terminal para troca de informações
Bem, agora que temos as bases, precisamos criar algo bem simples para poder testar e entender como o processo de uso das variáveis de terminal irá funcionar na prática.
Para isto foi criado os seguintes códigos, todos estão na integra logo abaixo. O Primeiro é o arquivo de cabeçario:
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define def_GlobalNameChannel "InnerChannel" //+------------------------------------------------------------------+
Aqui nos simplesmente definimos um nome para a variável global de terminal comum entre os dois processos que executarão no terminal gráfico.
O próximo código é mostrado abaixo, que é o serviço a ser executado.
#property service #property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ #include <Inner Channel.mqh> //+------------------------------------------------------------------+ void OnStart() { double count = 0; while (!IsStopped()) { if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel); GlobalVariableSet(def_GlobalNameChannel, count); count += 1.0; Sleep(1000); } } //+------------------------------------------------------------------+
O funcionamento dele é bem simples, ele irá testar se a variável já se encontra definida, isto é feito pela função GlobalVariableCheck, caso a variável ainda não exista, ela será criada de forma temporária pela função GlobalVariableTemp para depois receber um valor pela função GlobalVariableSet, ou seja estamos testando, criando e gravando informações, o serviço está funcionando como um servidor, assim como era feito pelo script, só que ainda não estamos acessando um site na web, vamos com calma, temos que entender como o sistema funciona.
O próximo passo, é criar o programa cliente, que no caso será o EA, ele pode ser visto abaixo:
#property copyright "Daniel Jose" #property description "Testing internal channel\nvia terminal global variable" #property version "1.03" //+------------------------------------------------------------------+ #include <Inner Channel.mqh> //+------------------------------------------------------------------+ int OnInit() { EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { double value; if (GlobalVariableCheck(def_GlobalNameChannel)) { GlobalVariableGet(def_GlobalNameChannel, value); Print(value); } } //+------------------------------------------------------------------+
Aqui também o código é bem simples, a cada segundo aproximadamente, o EA irá verificar se a variável existe, caso ela exista, ele irá ler o valor usando GlobalVariableGet e logo em seguida imprimir este valor no terminal.
Bem, talvez muitos não sabem como fazer, então vamos com calma, primeiro vamos colocar o serviço para executar, isto é feito da seguinte forma:
Mas pode acontecer outro tipo de cenário também, onde o serviço estava parado e você o faz voltar a funcionar, neste caso você irá proceder da seguinte forma:
Depois de fazer isto verificamos as variáveis de terminal e temos o seguinte resultado:
Note que o sistema aparentemente esta funcionando, mas temos agora que colocar o EA no gráfico e ter a leitura dos valores e assim confirmar a comunicação pelo canal. Então depois de colocar o EA no gráfico obtemos o seguinte resultado:
E ai está, o sistema funciona conforme desejamos, talvez você não esteja percebendo, mas estamos tendo o seguinte modelo que pode ser visto na imagem abaixo, que é um típico formato cliente servidor, e é justamente o que queremos fazer e estamos procurando uma forma de implementar, já que temos vantagens que mencionei antes sobre este modelo.
Agora tudo que precisamos fazer é adicionar o sistema de leitura e captura de valores da WEB no serviço, e assim teremos o modelo final para testes, esta parte é bem simples, pois o que de fato iremos fazer é pegar o código que estamos usando deste o inicio a adicioná-lo no serviço. Então para o nosso teste será preciso apenas modificar o arquivo do servidor, de forma a ler o valor no site da WEB e postar este valor de forma que o cliente irá ler o mesmo, assim o novo código do serviço é visto logo abaixo, na integra.
#property service #property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ #include <Inner Channel.mqh> //+------------------------------------------------------------------+ void OnStart() { string szRet; while (!IsStopped()) { if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel); szRet = GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D); GlobalVariableSet(def_GlobalNameChannel, StringToDouble(szRet)); Sleep(1000); } } //+------------------------------------------------------------------+ string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit) { string headers, szInfo = ""; char post[], charResultPage[]; int counter; if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1) return "Bad"; for (int c0 = 0, c1 = StringLen(szTest); c0 < c1; c0++) if (szTest[c0] != charResultPage[iTest + c0]) return "Failed"; for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error"; for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++); for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]); return szInfo; } //+------------------------------------------------------------------+
Agora temos um sistema funcionando conforme mostrado na figura abaixo:
ou seja, ele já esta práticamente completo, e quando executamos o mesmo obtemos os seguintes resultados, mas agora a troca de tempo gráfico não irá mais ser problema.
Conclusão
Aqui exploramos alguns recursos pouco explorados ainda na plataforma MetaTrader 5, um deles é os canais de comunicação, mas ainda não estamos tirando todo o proveito que este recurso realmente pode nos dar, podemos ir além do que foi mostrado aqui, e isto será visto no próximo artigo ... mas de uma forma ou de outra, tudo que foi visto até aqui nesta serie, mostra o quando podemos fazer dentro da plataforma MetaTrader 5, tudo que precisamos e escolher o caminho e trilhar o mesmo até obter o resultado procurado, mas temos que ter conhecimento sobre as limitações, vantagens e riscos envolvidos em cada um dos possíveis caminhos.
![Gráficos na biblioteca DoEasy (Parte 95): Controles de objetos gráficos compostos](https://c.mql5.com/2/44/MQL5-avatar-doeasy-library3-2__7.png)
![Desenvolvendo um EA de negociação do zero( Parte 15): Acessando dados na WEB (I)](https://c.mql5.com/2/46/development__6.png)
![Desenvolvendo um EA de negociação do zero (Parte 17): Acessando dados na WEB (III)](https://c.mql5.com/2/47/development.png)
![Desenvolvendo um EA de negociação do zero (Parte 14): Volume at Price (II)](https://c.mql5.com/2/46/development__5.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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
Muito bom o artigo.
Abre possibilidades para troca de informações em mercados que não é totalmente coberto pelo MT5, como as cryptos.
Ou mesmo dados de indicadores financeiros, que pode influenciar na tomada das decisões nas estratégias.
Muito bom o artigo.
Abre possibilidades para troca de informações em mercados que não é totalmente coberto pelo MT5, como as cryptos.
Ou mesmo dados de indicadores financeiros, que pode influenciar na tomada das decisões nas estratégias.
E não é somente isto ... no próximo artigo que sairá semana que vem a coisa toma muito mais corpo .... 😁👍