English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
preview
Desenvolvendo um EA de negociação do zero (Parte 17): Acessando dados na WEB (III)

Desenvolvendo um EA de negociação do zero (Parte 17): Acessando dados na WEB (III)

MetaTrader 5Exemplos | 2 junho 2022, 15:59
677 0
Daniel Jose
Daniel Jose

1.0 - Introdução

No artigo anterior, Desenvolvendo um EA de negociação do zero ( Parte 16 ) - Acessando dados na WEB ( II ), eu expliquei os problemas e implicações envolvidas na captura de informações na WEB para serem usada em um EA, dei 3 soluções possíveis para conseguir executar a tarefa, cada uma tem seus prós e contras.

Na primeira solução que era buscar as informações diretamente via EA, falei do problema que pode ocorrer caso o servidor remoto demore a responder, e as implicações que isto pode trazer para o sistema de negociação.

Na segunda solução implementamos um canal, segundo um modelo cliente servidor, onde o cliente era o EA, o servidor era um script e o canal de comunicação era um objeto, este modelo é muito bom até o momento em que você precisar mudar o tempo gráfico, dai ele começa a ficar um tanto quanto inconveniente, mas tirando este fato, é o melhor sistema apresentado, já que o fato de usamos um modelo cliente servidor garante que o EA não irá ficar esperando o servidor remoto responder, ele simplesmente vai e ler a informação que esta no objeto, não importando de onde a informação tenha vindo.

Na terceira e última solução, melhoramos o sistema cliente servidor, usando um serviço para isto, e com isto passamos a usar um recurso pouco explorado da plataforma MetaTrader 5, que são as variáveis globais de terminal, nesta solução corrigimos o problema de mudança no tempo gráfico que é o maior inconveniente do modelo que usa um script, mas passamos a ter um novo problema, o sistema de variáveis globais de terminal, permitem usar apenas o tipo double, e muitos não sabem como fazer para contornar isto e assim passa informações diferente, como por exemplo um trecho de texto via canal fornecido pelo MetaTrader 5.

Este artigo irá tratar justamente disto, como fazer para contornar esta limitação, mas não esperem milagres, para fazer a coisa funcionar como você possivelmente irá necessitar será preciso muito trabalho duro.

Então vamos por as mãos na massa, ou melhor começar a codificar um sistema alternativo.


2.0 - Planejamento

Para começar, sabemos que podemos usar somente variáveis do tipo double no sistema de canal fornecido pelo MetaTrader 5, e que este tipo é composto de 8 bytes. Mas ai você pensa: E dai ?!?! No que isto irá me ajudar ?!?! Calma, vamos entender, uma coisa:

Sistema computacionais trabalham com bytes, mesmo que muitos tenham esquecido deste conceito, ele é importante e necessário, e cada byte é composto de 8 bits. E 1 bit é a menor quantidade possível em um sistema computacional, e no caso o menor e mais simples tipo presente em uma linguagem é o tipo boolean, que é composto justamente disto 1 único bit. Isto é o mais básico do básico.

Bem, qualquer informação, não importa o qual complicada ela seja, irá estar contida dentro de 1 byte. Novamente não importa o quanto a informação seja complicada, ela sempre irá estar dentro de 1 byte, que são compostos de 8 bits. Quando unimos 2 bytes teremos o primeiro conjunto composto em um sistema, este primeiro conjunto é conhecido como WORD, o segundo como DWORD que seria 2 WORD, e o terceiro seria QWORD que seria 2 DWORD, isto é uma nomenclatura usada em assembly que é a linguagem mãe de todas a linguagens modernas, então grande parte dos sistema utilizam os mesmos tipos, mas tem um detalhe, o nome que estes tipos irão receber.

Espero que estejam conseguindo acompanhar o raciocínio até aqui, mas para facilitar as coisas para quem esta começando, veja as imagens abaixo:

          

                         

Nestas figuras acima temos os principais tipos disponíveis até o momento, e elas cobrem deste 1 bit até 64 bits. Mas vocês podem pensar: Por que você esta explicando isto ?!?! O detalhe é que é importante saber isto, para conseguir entender o que vamos fazer até o fim do artigo, já que iremos manipular estes tipos de forma a poder transferir informações com diferentes propriedades internas.

Bom continuando, cada um destes tipos podem receber nomes distintos dependendo da linguagem que se esta usando, no caso do MQL5 eles seguem a tabela abaixo:

Nome Quantidade de bytes Nome baseado no Assembly ( figuras acima ) 
bool   Apenas 1 bit é usado, pode haver 8 bools em um byte  Apenas 1 bit é usado, podendo haver 8 bools em um byte
char 1
 Byte
short 2  Word
int  4  DWord
long 8  QWord

esta tabela cobre os valores inteiros com sinais, para mais detalhes dentro do MQL5 veja tipos inteiros, lá são definidos outros nomes além destes. Já os tipos reais tem uma certa similaridade com com os tipos inteiros, mas tem seu próprio modo de formatação e modelagem interna, um exemplo de formatação e modelagem pode ser visto em double precision floating point format, mas basicamente seguirá a tabela a seguir:

Nome Quantidade de bytes Nome baseado no Assembly ( figura acima ) 
Float 4  DWord
Double 8  QWord

Notem uma coisa interessante aqui, tanto os modelos de ponto flutuante, quanto os modelos inteiros utilizam a mesma base de dados, mas com comprimentos diferentes, então estamos chegamos ao ponto de real interesse. Se você entendeu a lógica da coisa vai acabar chegando a seguinte conclusão que pode ser vista na imagem logo abaixo:

Ou seja, uma QWORD representam 8 bytes, então um valor double nos permite colocar 8 bytes de informação, e se você estiver usando a tabela ASCII para transferir informações, poderá colocar 8 caracteres imprimíveis em uma variável global de terminal, e conseguirá obter o seguinte resultado da comunicação entre um serviço e o EA, que pode ser visto logo abaixo.

é verdade que os dados estão em ordem, mas acredito que deu para entender a ideia em si. O grande detalhe é que se a mensagem for maior que 8 caracteres imprimíveis, ela terá que ser fragmentada em mais partes, se a entrega tiver que ser feita rapidamente, ou seja em 1 ciclo, você terá que usar tantas variáveis globais de terminal quantas forem necessárias para transferir a mensagem em um ciclo, e depois unir elas de forma adequada a ponto de reconstruir a mensagem original, mas se ela poder ser entregue em pacotes, você terá que criar uma forma do servidor, no caso o serviço saber que o cliente, que no caso o EA, leu a mensagem postada e que irá esperar o próximo bloco.

Este tipo de problema contém soluções diversas, mas quem desejar entender, ou implementar tais soluções, não precisa criar a coisa do zero, você pode usar a mesma modelagem que é feita em protocolos de comunicação de rede, como por exemplo o TCP/IP ou o UDP, e adaptar a ideia ao sistema de transferência de informações usando variáveis globais de terminal, uma vez entendido como os protocolos funcionam, esta tarefa deixa de ser complicada e passa a ser uma questão de habilidade e conhecimento da própria linguagem que você estará usando. Este é um assunto bastante amplo e que merece um estudo dedicado para cada tipo de situação e problema a ser solucionado.


3.0 - Implementação

Bem, agora que vocês entenderam a ideia que vamos usar, podemos fazer uma implementação inicial, para verificar como o sistema irá transferir informações entre o serviço e o EA, mas vamos transferir caracteres imprimíveis apenas.

3.0.1 Modelo base

No sistema do artigo anterior, vamos modificar os arquivo, começando com o arquivo de cabeçario, agora ele terá o seguinte conteúdo, e este esta na integra no código abaixo:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalNameChannel   "InnerChannel"
//+------------------------------------------------------------------+
union uDataServer
{
        double  value;
        char    Info[sizeof(double)];
};
//+------------------------------------------------------------------+

Este cabeçario é básico, nele temos a declaração da variável global de terminal, conforme foi visto anteriormente, e temos uma nova estrutura, uma union. Uma união é diferente de uma struct, a estrutura é uma combinação dos dados sem que eles se intercalem, já a união ocorre a intercalação, onde um dados menor irá ficar dentro de um maior. No caso acima teremos como base o valor double, e dentro deste valor 8 valores bytes, mas reparem que usei um sistema de captura de comprimento de tipo sizeof, desta forma caso futuramente venhamos a ter um tipo double maior, o que é pouco provável, este código automaticamente irá se adaptar a isto.

Então no final teremos a seguinte figura:

vejam que se parece com a que foi vista pouco acima, mas é isto que a união faz.

O próximo código a ser modificado é o EA, que corresponde o cliente, o código pode ser visto na integra logo a seguir:

#property copyright "Daniel Jose"
#property description "Testing internal channel\nvia terminal global variable"
#property version "1.04"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
int OnInit()
{
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        uDataServer loc;
        
        if (GlobalVariableCheck(def_GlobalNameChannel))
        {
                GlobalVariableGet(def_GlobalNameChannel, loc.value);
                Print(CharArrayToString(loc.Info, 0, sizeof(uDataServer)));
        }
}
//+------------------------------------------------------------------+

Notem que aqui usamos a função CharArrayToString para converter a cadeia de caracteres em uma string, mas observem que recebemos um valor double, que é o único possível de ser recebido de uma variável global de terminal, mas uma string na linguagem MQL5 segue os mesmos princípios de uma em C/C++, por este motivo não podemos usar qualquer carácter, salvo o fato de criarmos o nosso próprio sistema, mas isto é uma outra história, não entrarei em detalhes aqui sobre como fazer isto, já que você pode estar usando uma modelagem onde os dados estarão compactados, para assim ultrapassar o limite de 8 bytes.

Mas ainda falta um programa que é o servidor, no caso nosso servidor é um serviço e o código para testar o sistema irá ficar conforme o mostrado abaixo:

//+------------------------------------------------------------------+
#property service
#property copyright "Daniel Jose"
#property version   "1.03"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        uDataServer loc;
        char car = 33;
        
        while (!IsStopped())
        {
                if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel);
                for (char c0 = 0; c0 < sizeof(uDataServer); c0++)
                {
                        loc.Info[c0] = car;
                        car = (car >= 127 ? 33 : car + 1);
                }
                GlobalVariableSet(def_GlobalNameChannel, loc.value);
                Sleep(1000);
        }
}
//+------------------------------------------------------------------+

Vejam quem é algo bem simples, mas no entanto extremamente eficaz e funcional.

Quando rodamos isto na plataforma iremos obter exatamente o seguinte resultado


Talvez isto possa lhe parecer bobagem, e algo sem sentido, mas alguém com um pouco de criatividade pode fazer este sistema ser bastante útil para fazer algumas coisas não imaginadas por outros.

Para demonstrar irei modificar o sistema e mostrar uma coisa bem simples, só para aguçar a curiosidade de vocês e quem sabe fazer com que alguém imagine uma funcionalidade bem exótica para este sistema de comunicação.


3.0.2 - Trocando figurinhas

Esta troca de figurinhas é uma forma que eu tenho de me referir a uma troca de informações entre o cliente e o servidor de forma que o servidor irá saber qual o tipo de informação que o cliente estará desejando, assim o servidor pode começar a produzir, ou buscar estas informações.

O conceito é bem simples de ser entendido, mas implementar isto pode ser uma tarefa bastante desafiadora, ainda mais quando estamos lidando com uma modelagem de dados onde temos disponíveis apenas 8 bytes, e devemos passar toda e qualquer informação dentro deste canal.

3.0.2.1 - Testando a comunicação entre cliente e servidor

Vamos fazer o seguinte, observem o código do serviço que pode ser visto na integra logo abaixo:

#property service
#property copyright "Daniel Jose"
#property version   "1.03"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        uDataServer loc, loc1, loc2;
        char car = 33;
        
        while (!IsStopped())
        {
                if (!GlobalVariableCheck(def_GlobalValueInChannel))
                {
                        GlobalVariableTemp(def_GlobalValueInChannel);
                        GlobalVariableTemp(def_GlobalMaskInfo);
                        GlobalVariableTemp(def_GlobalPositionInfos);
                }
                for (char c0 = 0; c0 < sizeof(uDataServer); c0++)
                {
                        loc.Info[c0] = car;
                        car = (car >= 127 ? 33 : car + 1);
                }
                GlobalVariableSet(def_GlobalValueInChannel, loc.value);
                GlobalVariableGet(def_GlobalMaskInfo, loc1.value);
                GlobalVariableGet(def_GlobalPositionInfos, loc2.value);
                Print(CharArrayToString(loc1.Info, 0, sizeof(uDataServer)), "   ",loc2.Position[0], "    ", loc2.Position[1]);
                Sleep(1000);
        }
}
//+------------------------------------------------------------------+

Notem algumas coisas, interessantes neste novo código de serviço, ou servidor. Agora não estaremos lidando com 1 variável, mas com 3 variáveis, e elas funcionam de forma a criar um canal suficientemente grande para que o cliente, no caso o EA, possa conversar com o servidor, que no caso é o serviço, mas observem a seguinte linha:

Print(CharArrayToString(loc1.Info, 0, sizeof(uDataServer)), "   ",loc2.Position[0], "    ", loc2.Position[1]);

Aqui temos os dados que foram postados pelo cliente, notem que estamos usando 2 variáveis para passar 3 informações diferentes, mas como isto é possível ?!?! Para entender é preciso ver o código do cabeçario, que é mostrado logo abaixo na integra.

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalValueInChannel        "Inner Channel"
#define def_GlobalMaskInfo                      "Mask Info"
#define def_GlobalPositionInfos         "Positions Infos"
//+------------------------------------------------------------------+
union uDataServer
{
        double  value;
        uint    Position[2];
        char    Info[sizeof(double)];
};
//+------------------------------------------------------------------+

Bem você que esta iniciando pode pensar que cada variável dentro desta união esta isolada uma da outra, se você esta pensando isto, aconselho você olhar o inicio destes artigo, pois apesar de termos variáveis com nomes distintos, aqui elas são tratadas como sendo uma única variável, e esta terá 8 bytes de largura. Mas para ficar mais claro veja a imagem abaixo ela representa exatamente o que esta acontecendo:

  Este é esquema que esta dentro da união uDataServer.

Se o esquema acima lhe parece complicado, é bom você experimentar usar uniões durante um tempo para entender como de fato elas funcionam, pois elas tem uma utilidade muito grande no campo da programação.

Mas voltemos ao sistema, a próxima coisa a ser feita é criar o código do cliente, ou melhor dizendo do EA, e isto pode ser visto baixo na integra.

#property copyright "Daniel Jose"
#property description "Testing internal channel\nvia terminal global variable"
#property version "1.04"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
enum eWhat {DOW_JONES, SP500};
input eWhat     user01 = DOW_JONES;     //Buscar
//+------------------------------------------------------------------+
int OnInit()
{
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        uDataServer loc;
        
        SetFind();
        if (GlobalVariableCheck(def_GlobalValueInChannel))
        {
                GlobalVariableGet(def_GlobalMaskInfo, loc.value);
                Print(CharArrayToString(loc.Info, 0, sizeof(uDataServer)), "  ", GlobalVariableGet(def_GlobalValueInChannel));
        }
}
//+------------------------------------------------------------------+
inline void SetFind(void)
{
        static int b = -1;
        uDataServer loc1, loc2;
        
        if ((GlobalVariableCheck(def_GlobalValueInChannel)) && (b != user01))
        {
                b = user01;
                switch (user01)
                {
                        case DOW_JONES  :
                                StringToCharArray("INDU:IND", loc1.Info, 0, sizeof(uDataServer));
                                loc2.Position[0] = 172783;
                                loc2.Position[1] = 173474;
                                break;
                        case SP500              :
                                StringToCharArray("SPX:IND", loc1.Info, 0, sizeof(uDataServer));
                                loc2.Position[0] = 175484;
                                loc2.Position[1] = 176156;
                                break;
                }
                GlobalVariableSet(def_GlobalMaskInfo, loc1.value);
                GlobalVariableSet(def_GlobalPositionInfos, loc2.value);
        }
};
//+------------------------------------------------------------------+

Observem que estamos passando e recebendo informações neste EA, ou seja podemos controlar como o serviço deverá funcionar, em um variável passamos uma pequena string que irá dizer o que o serviço deverá procurar, e em uma outra variável passamos 2 pontos de endereçamento,

como resposta o serviço nos devolverá uma informação. mas para entender este primeiro momento veja o resultado que esta no video abaixo:




3.0.2.2 - Criando uma versão prática

Bom agora que já vimos como o sistema funciona, podemos fazer algo verdadeiramente funcional, e desta vez vamos recolher informações do servidor da WEB, mas para isto temos que fazer uma serie de modificações, para garantir um perfeito entendimento do que esta acontecendo, pois não queremos imaginar que estamos recebendo dados atualizados, quando na verdade estamos usando lixo em uma análise, é preciso tomar muito cuidado durante a fase de programação para que você não corra este risco, então adicione a maior quantidade possível de testes e procure fazer o sistema informar qualquer atividade estranha que ele possa detectar durante o seu funcionamento.

Lembre-se: A informação só lhe será útil se você confiar nela.

Bom a primeira coisa a fazer é modificar o arquivo de cabeçario, então ele agora estará assim:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalValueInChannel        "Inner Channel"
#define def_GlobalMaskInfo              "Mask Info"
#define def_GlobalPositionInfos         "Positions Infos"
//+------------------------------------------------------------------+
#define def_MSG_FailedConnection        "BAD"
#define def_MSG_FailedReturn            "FAILED"
#define def_MSG_FailedMask              "ERROR"
#define def_MSG_FinishServer            "FINISH"
//+------------------------------------------------------------------+
union uDataServer
{
        double  value;
        uint            Position[2];
        char            Info[sizeof(double)];
};
//+------------------------------------------------------------------+

Os pontos em destaque são códigos que iremos usar para informar alguma atividade estranha, você deve usar no máximo 8 caracteres, mas também tem que criar uma sequencia que dificilmente será criada pelo mercado, ou seja a coisa não é nada fácil de ser implementada, mesmo que pareça estar tudo bem, sempre existe o risco do mercado gerar um valor que irá coincidir com a sequência que você estará usando como mensagens de falhas no servidor, apesar de tudo, você pode também usar uma variável global de terminal somente para este proposito, isto irá aumentar muito o número de combinações possíveis e com isto você poderá passar uma quantidade bem maior de coisas. Mas eu queria usar o mínimo possível de variáveis globais de terminal, mas em um caso real eu sinceramente irá pensar neste tipo de coisa, e possivelmente iria usar uma variável apenas para indicação e comunicação de erros ou atividades anormais.

Bem o próximo código é o EA, ele pode ser visto abaixo na integra.

#property copyright "Daniel Jose"
#property description "Testing internal channel\nvia terminal global variable"
#property version "1.04"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
enum eWhat {DOW_JONES, SP500};
input eWhat     user01 = DOW_JONES;             //Buscar
//+------------------------------------------------------------------+
int OnInit()
{
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        ClientServer();
}
//+------------------------------------------------------------------+
inline void ClientServer(void)
{
        uDataServer loc1, loc2;
        string          sz0;
        
        SetFind();
        if (GlobalVariableCheck(def_GlobalValueInChannel))
        {
                GlobalVariableGet(def_GlobalMaskInfo, loc1.value);
                loc2.value = GlobalVariableGet(def_GlobalValueInChannel);
                sz0 = CharArrayToString(loc2.Info, 0, sizeof(uDataServer));
                if (sz0 == def_MSG_FailedConnection) Print("Failed in connection."); else
                if (sz0 == def_MSG_FailedReturn) Print("Error in Server Web."); else
                if (sz0 == def_MSG_FailedMask) Print("Bad Mask or position."); else
                if (sz0 == def_MSG_FinishServer) Print("Service Stop."); else
                Print(CharArrayToString(loc1.Info, 0, sizeof(uDataServer)), "  ", loc2.value);
        }
}
//+------------------------------------------------------------------+
inline void SetFind(void)
{
        static int b = -1;
        uDataServer loc1, loc2;
        
        if ((GlobalVariableCheck(def_GlobalValueInChannel)) && (b != user01))
        {
                b = user01;
                switch (user01)
                {
                        case DOW_JONES  :
                                StringToCharArray("INDU:IND", loc1.Info, 0, sizeof(uDataServer));
                                loc2.Position[0] = 172783;
                                loc2.Position[1] = 173474;
                                break;
                        case SP500              :
                                StringToCharArray("SPX:IND", loc1.Info, 0, sizeof(uDataServer));
                                loc2.Position[0] = 175487;
                                loc2.Position[1] = 176159;
                                break;
                }
                GlobalVariableSet(def_GlobalMaskInfo, loc1.value);
                GlobalVariableSet(def_GlobalPositionInfos, loc2.value);
        }
};
//+------------------------------------------------------------------+

As linhas em destaque são muito importantes, e devem ser bem pensadas, pois queremos de fato saber o que esta acontecendo, vejam que podemos informar ao usuário algo bem mais detalhado que aquelas sequencias criadas no arquivo de cabeçario, então faça a coisa de forma a ser fácil de você programar e manter. O restante do código praticamente não foi modificado, mas agora vamos ver o código do serviço.

#property service
#property copyright "Daniel Jose"
#property version   "1.03"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        uDataServer loc1, loc2;
        
        while (!IsStopped())
        {
                if (!GlobalVariableCheck(def_GlobalValueInChannel))
                {
                        GlobalVariableTemp(def_GlobalValueInChannel);
                        GlobalVariableTemp(def_GlobalMaskInfo);
                        GlobalVariableTemp(def_GlobalPositionInfos);
                }
                GlobalVariableGet(def_GlobalMaskInfo, loc1.value);
                GlobalVariableGet(def_GlobalPositionInfos, loc2.value);
                if (!_StopFlag)
                {
                        GlobalVariableSet(def_GlobalValueInChannel, GetDataURL(
                                                                                "https://tradingeconomics.com/stocks",
                                                                                100,
                                                                                "<!doctype html>",
                                                                                2,
                                                                                CharArrayToString(loc1.Info, 0, sizeof(uDataServer)),
                                                                                loc2.Position[0],
                                                                                loc2.Position[1],
                                                                                0x0D
                                                                               )
                                        );
                        Sleep(1000);
                }
        }
        GlobalVariableSet(def_GlobalValueInChannel, Codification(def_MSG_FinishServer));
}
//+------------------------------------------------------------------+
double 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 Codification(def_MSG_FailedConnection);
        for (int c0 = 0, c1 = StringLen(szTest); (c0 < c1) && (!_StopFlag); c0++) if (szTest[c0] != charResultPage[iTest + c0]) return Codification(def_MSG_FailedReturn);
        for (int c0 = 0, c1 = StringLen(szFind); (c0 < c1) && (!_StopFlag); c0++) if (szFind[c0] != charResultPage[iPos + c0]) return Codification(def_MSG_FailedMask);
        if (_StopFlag) return Codification(def_MSG_FinishServer);
        for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++);
        for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]);
        
        return StringToDouble(szInfo);
}
//+------------------------------------------------------------------+
inline double Codification(const string arg)
{
        uDataServer loc;
        StringToCharArray(arg, loc.Info, 0, sizeof(uDataServer));
        
        return loc.value;
}
//+------------------------------------------------------------------+

A linha em destaque é importante para garantir que o serviço irá informar que não esta mais executando.

Bem quando rodamos este sistema, teremos o seguinte resultado:



Conclusão

E com isto acredito ter passado para você a ideia por traz da pesquisa, busca e utilização de dados da WEB na plataforma MetaTrader 5, sei que no começo é algo que assusta quem esta começando na arte da programação, mas com o tempo, disciplina e estudo, você acabará dominando grande parte deste material, aqui tentei passar pelo menos um pouco do que sei, acredito que tenha conseguido fazer isto.

Arquivos anexados |
Servi0o_-_EA.zip (10.71 KB)
Como desenvolver sistemas baseados em médias móveis Como desenvolver sistemas baseados em médias móveis
Existem muitas maneiras diferentes de filtrar os sinais gerados por qualquer estratégia. Provavelmente, a mais simples delas consiste no uso de uma média móvel. Vamos falar sobre isso neste artigo.
Gráficos na biblioteca DoEasy (Parte 95): Controles de objetos gráficos compostos Gráficos na biblioteca DoEasy (Parte 95): Controles de objetos gráficos compostos
Neste artigo, consideraremos ferramentas para gerenciar objetos gráficos compostos, nomeadamente controles de um objeto gráfico padrão estendido. Hoje vamos nos desviar um pouco do tópico anterior, que era mover um objeto gráfico composto. Em vez disso, vamos fazer um manipulador de eventos de alteração de gráfico que tem algum objeto gráfico composto, e vamos lidar com os objetos de controle do objeto gráfico composto.
Gráficos na biblioteca DoEasy (Parte 96): Trabalhando com eventos do rato/gráfico em objetos-formas Gráficos na biblioteca DoEasy (Parte 96): Trabalhando com eventos do rato/gráfico em objetos-formas
Neste artigo desenvolveremos recursos para manusear os eventos do mouse em objetos-formas e adicionaremos novas propriedades de rastreamento ao objeto símbolo. Além disso, hoje modificaremos a classe do objeto símbolo, porque desde que foi escrito, os símbolos do gráfico têm novas propriedades que é desejável levar em conta e acompanhar a nível de suas mudanças.
Desenvolvendo um EA de negociação do zero (Parte 16): Acessando dados na WEB (II) Desenvolvendo um EA de negociação do zero (Parte 16): Acessando dados na WEB (II)
Como levar os dados da WEB para dentro de um EA . 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.