English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Développer un Expert Advisor de trading à partir de zéro (Partie 17) : Accès aux données sur le web (3)

Développer un Expert Advisor de trading à partir de zéro (Partie 17) : Accès aux données sur le web (3)

MetaTrader 5Exemples | 13 avril 2023, 09:31
1 086 0
Daniel Jose
Daniel Jose

Introduction

Dans l'article précédent Développer un Expert Advisor de trading à partir de zéro (Partie 16) : Accès aux données sur le web (2), nous avons parlé des problèmes et des conséquences de la capture de données sur le web. Nous avons également étudié la manière de l'utiliser dans un Expert Advisor et discuté de 3 solutions possibles, chacune ayant ses avantages et ses inconvénients.

Dans la première solution, qui impliquait la capture de données directement via l'Expert Advisor, nous avons envisagé une solution possible, liée à la lenteur de la réponse du serveur. Nous avons également mentionné les conséquences que cela peut avoir sur un système de trading.

Dans la deuxième solution, nous avons mis en place un canal basé sur le modèle client-serveur, dans lequel l'EA fait office de client, un script sert de serveur et un objet sert de canal. Ce modèle fonctionne bien jusqu'à ce que vous décidiez de modifier la période, ce qui devient alors peu pratique. Malgré cela, il s'agit du meilleur système présenté, car l'utilisation du modèle client-serveur garantit que l'EA ne n'attendra pas la réponse d’un serveur distant : il lira simplement les données contenues dans l'objet, quelle que soit l'origine de ces informations.

Dans la troisième et dernière solution, nous avons amélioré le système client-serveur en utilisant un service. Nous avons ainsi commencé à utiliser une ressource de la plateforme MetaTrader 5, qui est assez peu étudiée : les variables globales du terminal. Cette solution a permis de résoudre le problème du changement de période, qui constituait le principal inconvénient du modèle utilisant des scripts. Mais nous rencontrons un nouveau problème : le système des variables globales du terminal ne permet d'utiliser que des variables de type double. Nombreux sont ceux qui ne savent pas comment éviter cela et qui transmettent donc diverses informations (comme par exemple un texte) par le canal fourni par MetaTrader 5.

Dans cet article, nous allons voir comment contourner cette limitation. Mais ne vous attendez pas à des miracles. Il faudra beaucoup de travail pour que le système fonctionne comme vous le souhaitez.

Cette fois-ci, nous allons élaborer un système alternatif.


1. Plan

Comme nous le savons, nous ne pouvons utiliser que des variables de type ’double’ dans le système de canaux fourni par MetaTrader 5. Ce type d’objet est composé de 8 octets. Peut-être que vous pensez que ces informations ne sont pas très utiles. Mais voyons ce qu'il en est réellement au moment suivant :

Les systèmes informatiques fonctionnent avec des octets (même si de nombreuses personnes aient oublié ce concept). C’est un système qu’il est très important de comprendre. Chaque octet est composé de 8 bits. Un bit est le plus petit nombre possible dans un système informatique. Le plus petit type et le plus simple présent dans le langage est le type booléen, qui ne consiste qu’en un seul bit. C'est l’élément le plus simple des bases.

Donc toute information, quelle que soit sa complexité, sera contenue dans 1 octet. Encore une fois, quelle que soit la complexité de l'information, elle sera toujours contenue dans un octet, composé lui-même de 8 bits. Lorsque nous assemblons 2 octets, nous obtenons le premier ensemble composé du système. Ce premier ensemble est connu sous le nom de WORD, le second sous le nom de DWORD (qui sera 2x WORD) et le troisième sous le nom de QWORD (qui sera 2x DWORD). Il s'agit de la nomenclature utilisée en Assembleur, qui est la langue mère de tous les langages modernes, de sorte que la plupart des systèmes utilisent les mêmes types. La seule différence réside dans la manière dont ces types sont nommés.

J'espère que vous suivez le raisonnement jusqu'ici. Pour faciliter la tâche de ceux qui débutent, voici quelques illustrations :

          

                         

Les images ci-dessus montrent les principaux types actuellement disponibles (ils couvrent de 1 à 64 bits). Vous vous demandez peut être pourquoi cette explication est nécessaire ? Il est important de connaître ces informations pour comprendre ce que nous allons faire tout au long de cet article. Nous allons en effet manipuler ces types afin de pouvoir transmettre des informations avec différentes propriétés internes.

Chacun d’eux peut avoir des noms différents en fonction du langage utilisé. Dans le cas de MQL5, ils sont présentés dans le tableau suivant :

Nom Nombre d'octets Nom basé sur le langage Assemblage (images ci-dessus) 
bool   N'utilise qu'1 bit ; un octet peut contenir 8 valeurs de type ’bool’.  N'utilise qu'1 bit ; un octet peut contenir 8 valeurs de type ’bool’.
char 1
 Byte
short 2  Word
int  4  DWord
long 8  QWord

Ce tableau couvre les entiers signés. Pour plus de détails en MQL5, voir les types entiers, d'autres noms y sont définis. Les types réels présentent certaines similitudes avec les types entiers, mais ont leur propre formatage interne et leur propre style. Un exemple de formatage et de modélisation peut être visible sur un nombre avec une précision de type ’double’, mais il correspondra essentiellement au tableau ci-dessous :

Nom Nombre d'octets Nom basé sur le langage Assemblage (images ci-dessus) 
Float 4  DWord
Double 8  QWord

Il est intéressant de noter que les modèles entier et à virgule flottante utilisent la même base de données, mais avec des tailles différentes. Nous en arrivons maintenant au point qui nous intéresse vraiment. Si vous comprenez la logique, vous pouvez éventuellement arriver à la conclusion suivante :

QWORD est composé de 8 octets. Donc le type "double" permet de mettre 8 octets d'information. Par exemple, vous pouvez passer 8 caractères imprimables dans une variable globale du terminal, et vous obtiendrez le résultat de la connexion entre le service et l'EA, comme ci-dessous :

Les données sont correctes. Je pense que l'idée elle-même est compréhensible. Le détail important est que, si le message comporte plus de 8 caractères imprimables, il devra être découpé en plusieurs parties. Mais si l'information doit être délivrée très rapidement, c'est-à-dire en un cycle, vous devrez utiliser autant de variables globales du terminal que nécessaire pour transmettre les messages. Il faut ensuite les concaténer pour rétablir le message d'origine. Mais s'il peut être transmis par paquets, nous devrons créer une forme pour le serveur afin que le service sache que le client, qui est dans ce cas l'EA, lira le message et attendra le bloc suivant.

Ce type de problème a plusieurs solutions. Si vous souhaitez comprendre ou mettre en œuvre ces solutions, vous n'aurez pas besoin de tout créer à partir de zéro. Vous pouvez utiliser la même modélisation que dans les protocoles de communication réseau tels que TCP/IP ou UDP et adapter l'idée au système de transfert d'informations à l'aide de variables globales du terminal. Une fois que vous avez compris le fonctionnement des protocoles, cette tâche n'est pas compliquée et cela devient une question de compétence et de connaissance du langage que vous utilisez. Il s'agit d'un sujet très vaste qui mérite une étude distincte pour chaque type de situation et pour chaque problème.


2. Implémentation

Maintenant que nous comprenons l'idée que nous allons utiliser, nous pouvons procéder à une première mise en œuvre pour tester la manière dont le système transférera les informations entre le service et l'EA. Nous ne transmettrons que des caractères imprimables.

2.1. Modèle de base

Nous utiliserons le système de l'article précédent et nous modifierons les fichiers, en commençant par le fichier d'en-tête. Son nouveau contenu est présenté dans son intégralité ci-dessous :

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

Cet en-tête est basique. Il contient la déclaration de la valeur globale du terminal. Il dispose également d'une nouvelle structure, une union. L'union diffère de la structure car la structure est une combinaison de données sans entrelacement, alors que l'union l'utilise toujours, lorsque des données plus petites se trouvent à l'intérieur de données plus grandes. Dans le cas précédent, nous avons comme base une valeur de type double, qui contient 8 octets. Attention, j'utilise ici sizeof pour obtenir la longueur du type. Donc si nous avons un double plus grand dans le futur, ce qui est peu probable, ce code s'y adaptera automatiquement.

Nous obtenons ainsi ce qui suit :

Notez que cela ressemble à l'image précédente, et c'est ce que fait l’union.

Il nous faut maintenant modifier le code de l’EA correspondant au client. Le code complet est visible ci-dessous :

#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)));
        }
}
//+------------------------------------------------------------------+

Notez que nous utilisons ici la fonction CharArrayToString pour convertir un tableau uchar en une chaîne de caractères. Notez cependant que nous obtenons toujours une valeur de type double car c'est le seul type qui peut être reçu d'une variable globale du terminal. Contrairement à cela, le type string en MQL5 suit le principe de C/C++ : nous ne pouvons donc pas utiliser n'importe quel caractère, nous pouvons seulement créer le nôtre. Mais ceci est une autre histoire. Nous n'entrerons pas dans les détails de la procédure à suivre ici : vous pouvez utiliser la compression des données de modélisation pour dépasser la limite de 8 octets.

Nous avons encore besoin d'un programme qui joue le rôle de serveur. Dans notre cas, le serveur sera un service. Vous trouverez ci-dessous le code permettant de tester notre système :

//+------------------------------------------------------------------+
#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);
        }
}
//+------------------------------------------------------------------+

C'est quelque chose de simple mais aussi d'extrêmement efficace et fonctionnel.

En lançant le programme dans la plateforme, nous obtenons le résultat suivant :


Cela peut sembler stupide et inutile, mais avec un peu de créativité, quelqu'un peut rendre ce système suffisamment utile et lui faire faire des choses que nous n’avons même pas imaginé.

Pour le démontrer, modifions le système et montrons quelque chose de simple, juste pour éveiller la curiosité et l'intérêt. Vous pouvez réfléchir aux fonctionnalités très exotiques qui peuvent être trouvées pour un tel système de communication.


2.2. Échange d'autocollants

L'échange d'autocollants est l'échange d'informations entre le client et le serveur au cours duquel le serveur sait quelles informations le client souhaite recevoir et peut donc commencer à produire ou à rechercher ces informations.

Le concept est assez simple à comprendre. Mais sa mise en œuvre peut constituer un véritable défi, en particulier lorsqu'il s'agit de modéliser des données et que nous ne disposons que de 8 octets dans le canal utilisé pour transférer des données.

2.2.1. Test de communication client-serveur

Le code du service est présenté ci-dessous dans son intégralité :

#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);
        }
}
//+------------------------------------------------------------------+

Prêtez attention à une partie particulièrement intéressante du nouveau code du service (qui agit comme un serveur) : nous avons maintenant 3 variables au lieu d'une seule. Elles permettent de créer un canal suffisamment large pour permettre la communication entre le client (qui est un EA dans notre cas) et le serveur (notre service). Prêtez bien attention à la ligne suivante :

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

Ce sont les données publiées par le client. Notez que nous utilisons donc 2 variables pour transmettre 3 informations différentes. Mais comment cela est-il possible ? Pour le comprendre, il faut voir le code de l'en-tête, présenté dans son intégralité ci-dessous :

#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)];
};
//+------------------------------------------------------------------+

Vous pouvez penser que chaque variable de cette union est isolée des autres. Je vous propose de revenir au début de cet article, car bien que les variables aient des noms différents, elles sont ici traitées comme une seule variable, d'une largeur de 8 octets. Pour mieux comprendre, regardez l'image ci-dessous, qui reflète bien ce qui se passe :

Ce schéma montre comment est composé uDataServer.

Si cela vous semble trop compliqué, je vous recommande de tester les unions pour comprendre comment elles fonctionnent réellement, car elles sont très utiles en programmation.

Mais revenons à notre système. L'étape suivante consiste à créer le code pour le client (i.e. l'EA). Le code intégral est donné ci-dessous :

#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;     //Search
//+------------------------------------------------------------------+
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);
        }
};
//+------------------------------------------------------------------+

Notez que dans cet EA, nous envoyons et nous recevons des informations, c'est-à-dire que nous pouvons contrôler la manière dont le service doit fonctionner. Dans une des variables, nous passons une petite chaîne de caractères qui indiquera ce que le service doit rechercher, et dans l'autre, nous passons 2 points d'adresse.

En réponse, le service renvoie des informations. Pour comprendre ce premier point, regardez le résultat dans la vidéo ci-dessous :



3.1.2.2 - Création d'une version en pratique

Maintenant que nous avons vu comment cela fonctionne, nous pouvons créer un système vraiment fonctionnel. Nous allons cette fois-ci collecter des informations sur le serveur web. Cela nécessite un certain nombre de modifications qui garantissent une parfaite compréhension de ce qui se passe. Nous pouvons parfois penser que nous recevons des données actualisées alors qu'en fait, nous utilisons des données anciennes dans notre analyse. Il faut être très prudent pendant la programmation pour ne pas s'exposer à ce risque. Vous pouvez ajouter autant de tests que possible et essayer de faire en sorte que le système signale toute activité bizarre qui pourrait être détectée en cours d'exécution.

Souvenez-vous : Les informations ne vous seront utiles que si vous leur faites confiance.

Modifions tout d’abord le fichier d'en-tête pour qu'il ressemble à ceci :

#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)];
};
//+------------------------------------------------------------------+

Les parties surlignées représentent le code que nous utiliserons pour signaler une activité étrange. Vous devez utiliser un maximum de 8 caractères. Mais vous devez également créer une séquence qui n'est pas susceptible d'être créée par le marché, ce qui n'est pas facile à faire. Même si tout semble bien se comporter, il y a toujours un risque que le marché génère une valeur correspondant à la séquence que vous utiliserez comme message d'erreur du serveur. Quoi qu'il en soit, vous pouvez également utiliser pour cela une variable globale du terminal, ce qui augmentera le nombre de combinaisons possibles, vous permettant ainsi de créer plus de choses. Mais je voulais utiliser le moins de variables globales du terminal possible. Dans un cas réel, j'avoue que j’y réfléchirais et j'utiliserais peut être une variable uniquement pour indiquer et signaler les erreurs ou les activités anormales.

La partie suivante est le code complet de l'EA :

#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;             //Search
//+------------------------------------------------------------------+
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);
        }
};
//+------------------------------------------------------------------+

Les lignes mises en évidence sont très importantes et doivent être bien pensées, car nous voulons vraiment avoir le détail de ce qu’il se passe. Comme vous pouvez le constater, nous pouvons donner à l'utilisateur plus de détails que ceux offerts par les séquences créées dans le fichier d'en-tête. Cela facilite la programmation et la maintenance de la solution. Le reste du code n'a pas changé énormément. Regardez le code du service ci-dessous :

#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;
}
//+------------------------------------------------------------------+

La ligne surlignée est également importante : ici le service signale qu'il n'est plus en cours d'exécution.

Lorsque vous exécutez ce système, vous obtiendrez donc le résultat suivant :


Conclusion

J'espère avoir expliqué l'idée liée à la recherche et l'utilisation de données web sur la plateforme MetaTrader 5. Je comprends que cela puisse ne pas être très clair au début, surtout pour ceux qui n'ont pas de connaissances approfondies en programmation. Mais avec le temps, avec de la discipline et de l'apprentissage, vous finirez par maîtriser la plupart de ces concepts. J'ai essayé ici de partager au moins un peu de mes connaissances.

Traduit du portugais par MetaQuotes Ltd.
Article original : https://www.mql5.com/pt/articles/10447

Fichiers joints |
Servi0o_-_EA.zip (10.71 KB)
Apprenez à concevoir un système de trading basé sur l'Ecart-Type (Standard Deviation) Apprenez à concevoir un système de trading basé sur l'Ecart-Type (Standard Deviation)
Voici un nouvel article de notre série sur la conception d’un système de trading à l'aide des indicateurs techniques les plus populaires sur la plateforme de trading MetaTrader 5. Dans ce nouvel article, nous allons apprendre à concevoir un système de trading basé sur l'indicateur Standard Deviation (écart-type).
Développer un Expert Advisor de trading à partir de zéro (Partie 16) : Accès aux données sur le web (2) Développer un Expert Advisor de trading à partir de zéro (Partie 16) : Accès aux données sur le web (2)
Il n'est pas évident de savoir comment introduire des données provenant du Web dans un Expert Advisor. Ce n'est pas si facile à faire sans connaître toutes les possibilités offertes par MetaTrader 5.
Apprenez à concevoir un système de trading basé sur l'Oscillateur de Chaikin Apprenez à concevoir un système de trading basé sur l'Oscillateur de Chaikin
Bienvenue dans ce nouvel article de notre série consacrée à l'apprentissage de la conception d'un système de trading à l'aide d’un indicateur technique parmi les plus populaires. Dans ce nouvel article, nous allons apprendre à concevoir un système de trading basé sur l'indicateur Chaikin Oscillator (Oscillateur de Chaikin).
Data Science et Apprentissage Automatique (partie 6) : Descente de Gradient Data Science et Apprentissage Automatique (partie 6) : Descente de Gradient
La Descente de Gradient joue un rôle important dans la formation des réseaux neuronaux et de nombreux algorithmes d'apprentissage automatique. C'est un algorithme rapide et intelligent. Mais malgré son travail impressionnant, il est encore mal compris par beaucoup de data scientists. Voyons de quoi il s'agit.