Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 17): Zugang zu Daten im Internet (III)

3 August 2022, 13:33
Daniel Jose
0
88

Einführung

Im vorherigen Artikel Entwicklung eines Expert Advisors von Grund auf (Teil 16): Zugriff auf Daten im Internet (II), haben wir über die Probleme und Folgen der Datenerfassung im Internet gesprochen. Wir haben auch überlegt, wie man sie in einem Expert Advisor einsetzen kann, und drei mögliche Lösungen mit ihren jeweiligen Vor- und Nachteilen diskutiert.

Bei der ersten Lösung, die eine direkte Datenerfassung über den Expert Advisor vorsah, haben wir ein mögliches Problem im Zusammenhang mit der langsamen Serverantwort berücksichtigt. Wir haben auch erwähnt, welche Folgen dies für ein Handelssystem haben kann.

Bei der zweiten Lösung haben wir einen Kanal auf der Grundlage des Client-Server-Modells implementiert, bei dem der EA als Client, ein Skript als Server und ein Objekt als Kanal fungiert. Dieses Modell funktioniert gut, bis zu dem Punkt, an dem Sie sich entscheiden, den Zeitrahmen zu ändern, wo es unbequem wird. Trotzdem ist es das beste vorgestellte System, denn die Verwendung des Client-Server-Modells gewährleistet, dass der EA nicht auf einen entfernten Server wartet, sondern einfach die im Objekt enthaltenen Daten liest, unabhängig davon, woher diese Informationen stammen.

In der dritten und letzten Lösung haben wir das Client-Server-System durch den Einsatz eines Dienstes verbessert. So begannen wir mit einer MetaTrader 5 Plattform Ressource, die ziemlich wenig untersucht ist: die globalen Variablen des Terminals. Mit dieser Lösung wurde das Problem der Zeitrahmenänderung behoben, das der größte Nachteil des Modells war, das Skripte verwendet. Wir haben jedoch ein neues Problem: Das System der globalen Variablen des Terminals erlaubt nur die Verwendung des Typs double. Viele wissen nicht, wie sie dies vermeiden können, und leiten daher verschiedene Informationen, wie z. B. einen Text, über den vom MetaTrader 5 bereitgestellten Kanal weiter.

In diesem Artikel wird erläutert, wie diese Einschränkung umgangen werden kann. Erwarten Sie aber keine Wunder, denn es wird viel Arbeit erfordern, damit das System so funktioniert, wie Sie es sich wünschen.

Dieses Mal werden wir ein alternatives System entwickeln.


1. Planung

Wie wir wissen, können wir im Kanalsystem des MetaTrader 5 nur Variablen vom Typ Double verwenden. Dieser Typ besteht aus 8 Bytes. Man könnte meinen, dass dies keine sehr nützlichen Informationen sind. Aber lassen Sie uns den nächsten Moment abwarten:

Computersysteme arbeiten mit Bytes, obwohl viele Menschen dieses Konzept vergessen haben. Es ist sehr wichtig, dieses System zu verstehen. Jedes Byte besteht aus 8 Bits. 1 Bit ist die kleinstmögliche Zahl in einem Rechensystem. Der kleinste und einfachste Typ in der Sprache ist der boolesche Typ, der aus einem einzigen Bit besteht. Dies ist die einfachste der Grundlagen.

Jede Information, egal wie komplex sie sein mag, ist also in 1 Byte enthalten. Auch hier gilt: Egal wie kompliziert eine Information ist, sie wird immer in einem Byte enthalten sein, das aus 8 Bits besteht. Wenn wir 2 Bytes verbinden, erhalten wir den ersten zusammengesetzten Datensatz im System. Dieser erste Datensatz wird als WORD bezeichnet, der zweite Datensatz als DWORD, der aus 2 WORD besteht, und der dritte Datensatz als QWORD, der aus 2 DWORD besteht. Dies ist die Nomenklatur, die in der Assemblersprache, der Muttersprache aller modernen Sprachen, verwendet wird, sodass die meisten Systeme die gleichen Typen verwenden. Der einzige Unterschied besteht darin, wie diese Typen benannt werden.

Ich hoffe, Sie konnten der Argumentation bis zu diesem Punkt folgen. Zur Erleichterung derjenigen, die noch ganz am Anfang stehen, sehen Sie sich die folgenden Zahlen an:

          

                         

Die obigen Abbildungen zeigen die wichtigsten derzeit verfügbaren Typen, die von 1 bis 64 Bit reichen. Vielleicht denken Sie: "Wozu brauche ich diese Erklärung?". Es ist wichtig, diese Informationen zu kennen, um zu verstehen, was wir in diesem Artikel tun werden, da wir diese Typen manipulieren werden, um Informationen mit verschiedenen internen Eigenschaften übergeben zu können.

Jeder dieser Typen kann je nach verwendeter Sprache unterschiedliche Namen erhalten, im Fall von MQL5 sind sie in der folgenden Tabelle aufgeführt:

Name Anzahl der Bytes Name basierend auf der Assemblersprache (Bilder oben) 
bool   Verwendet nur 1 Bit; ein Byte kann 8 boolsche Werte haben.  Verwendet nur 1 Bit; ein Byte kann 8 boolsche Werte haben.
char 1
 Byte
kurz 2  Word
int  4  DWord
long 8  QWord

Diese Tabelle deckt vorzeichenbehaftete Ganzzahlen ab, für weitere Details in MQL5 siehe Integer-Typen, andere Namen sind dort definiert. Real-Typen haben einige Ähnlichkeiten mit Integer-Typen, haben aber ihre eigene interne Formatierung und Gestaltung. Ein Beispiel für die Formatierung und Modellierung ist auf „Double-precision floating-point format“ zu sehen, aber im Grunde entspricht es der nachstehenden Tabelle:

Name Anzahl der Bytes Name basierend auf der Assemblersprache (Bilder oben) 
Float 4  DWord
Double 8  QWord

Interessant ist, dass sowohl das Gleitkomma- als auch das Ganzzahlmodell dieselbe Datenbank verwenden, allerdings mit unterschiedlichen Längen. Jetzt sind wir an dem Punkt angelangt, der uns wirklich interessiert. Wenn Sie die Logik verstehen, können Sie schließlich zu folgender Schlussfolgerung kommen, die in der folgenden Abbildung zu sehen ist:

QWORD besteht aus 8 Bytes, und somit erlaubt "double" die Eingabe von 8 Informationsbytes. Sie können zum Beispiel 8 druckbare Zeichen in eine globale Terminalvariable eingeben und erhalten das Ergebnis der Verbindung zwischen dem Dienst und dem EA, wie unten dargestellt.

Die Daten sind in Ordnung, ich denke, die Idee selbst ist verständlich. Wenn die Nachricht mehr als 8 druckbare Zeichen hat, muss sie in mehrere Teile zerlegt werden. Wenn die Informationen jedoch sehr schnell, d. h. in einem Zyklus, übermittelt werden sollen, müssen Sie so viele globale Terminalvariablen verwenden, wie für die Übertragung der Nachrichten in einem Zyklus erforderlich sind. Dann müssen sie zusammengeklebt werden, um die ursprüngliche Botschaft wiederherzustellen. Wenn sie jedoch in Paketen geliefert werden kann, müssen wir ein Formular für den Server erstellen, damit der Dienst weiß, dass der Client, in diesem Fall der EA, die Nachricht lesen und auf den nächsten Block warten wird.

Für diese Art von Problem gibt es mehrere Lösungen. Wenn Sie diese Lösungen verstehen oder implementieren wollen, müssen Sie nicht alles von Grund auf neu erstellen - Sie können die gleiche Modellierung wie bei Netzwerkkommunikationsprotokollen wie https://de.wikipedia.org/wiki/InternetprotokollfamilietitleTCP/IP oder UDP verwenden und die Idee mit Hilfe von globalen Terminalvariablen an das Informationsübertragungssystem anpassen. Wenn Sie erst einmal verstanden haben, wie die Protokolle funktionieren, ist diese Aufgabe nicht mehr kompliziert und wird zu einer Frage des Geschicks und der Kenntnis der verwendeten Sprache. Dies ist ein sehr umfangreiches Thema, das für jede Art von Situation und Problem eine eigene Untersuchung verdient.


2. Umsetzung

Nun, da wir die Idee, die wir verwenden werden, verstanden haben, können wir eine erste Implementierung vornehmen, um zu testen, wie das System Informationen zwischen dem Dienst und dem EA übertragen wird. Es werden jedoch nur druckbare Zeichen übergeben.

2.1. Grundmodell

Wir werden ein System aus dem vorherigen Artikel verwenden und die Dateien ändern, beginnend mit der Header-Datei. Der neue Inhalt ist im nachstehenden Code vollständig wiedergegeben:

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

Diese Kopfzeilen sind einfach. Sie enthalten eine Deklaration des globalen Terminalvariablen. Sie hat auch eine neue Struktur, eine union. Die union unterscheidet sich von Structur struct dadurch, dass eine Struktur eine Kombination von Daten ohne Verschachtelung ist, während die Vereinigung sie immer dann verwendet, wenn sich kleinere Daten innerhalb der größeren befinden. Im vorherigen Fall haben wir einen Double-Wert als Basis, der 8 Bytes enthält. Aber achten Sie darauf, dass ich ein System verwende, um die Länge sizeof zu erfassen. Wenn wir also in Zukunft ein größeren double-Wert haben, was unwahrscheinlich ist, wird sich dieser Code automatisch daran anpassen.

Daraus ergibt sich das Folgende:

Beachten Sie, dass dies dem obigen Bild ähnelt, aber das ist das, was eine union tut.

Der nächste zu ändernde Code ist der EA, der dem Client entspricht. Der vollständige Code ist unten zu sehen:

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

Beachten Sie, dass wir hier die Funktion CharArrayToString verwenden, um ein uchar-Array in eine Zeichenkette zu konvertieren. Achten Sie jedoch darauf, dass wir immer noch einen double-Wert erhalten, da dies der einzige Wert ist, der von einer globalen Variablen des Terminals erhalten werden kann. Im Gegensatz dazu folgt die Zeichenkette string C/C++, und wir können daher nicht irgendein Zeichen verwenden, sondern nur ein eigenes erstellen. Aber das ist eine andere Geschichte. An dieser Stelle wird nicht näher darauf eingegangen, wie dies zu bewerkstelligen ist: Sie können die Datenkompression zur Modellierung verwenden, um die 8-Byte-Grenze zu überschreiten.

Wir brauchen aber noch ein Programm, das als Server fungiert. In unserem Fall ist der Server ein Dienst. Nachfolgend finden Sie den Code zum Testen des Systems:

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

Dies ist etwas Einfaches, aber äußerst effektiv und funktionell.

Wenn wir das Programm auf der Plattform starten, erhalten wir folgendes Ergebnis


Es mag albern und sinnlos erscheinen, aber mit ein wenig Kreativität kann jemand dieses System nützlich genug machen und es Dinge tun lassen, die sich andere nicht einmal vorstellen können.

Um dies zu demonstrieren, ändern wir das System und zeigen eine sehr einfache Sache, nur um Neugier und Interesse zu wecken. Überlegen Sie, welche sehr exotischen Funktionen sich für ein solches Kommunikationssystem finden lassen.


2.2. Hinweise austauschen

Der Austausch von Hinweisen ist der Austausch von Informationen zwischen dem Client und dem Server, bei dem der Server weiß, welche Informationen der Client erhalten möchte, sodass der Server mit der Produktion oder der Suche nach diesen Informationen beginnen kann.

Das Konzept ist recht einfach zu verstehen. Aber die Implementierung kann eine ziemliche Herausforderung sein, besonders wenn es um die Datenmodellierung geht, bei der wir nur 8 Bytes zur Verfügung haben, während der Kanal für die Datenübertragung verwendet wird.

2.2.1. Test der Client-Server-Kommunikation

Werfen Sie einen Blick auf den Code des Dienstes, der im Folgenden vollständig dargestellt ist:

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

Achten Sie auf einen besonders interessanten Teil im neuen Code des Dienstes (der als Server fungiert). Jetzt haben wir drei Variablen anstelle von einer. Sie arbeiten daran, einen Kanal zu schaffen, der groß genug ist, um die Kommunikation zwischen dem Client (in diesem Fall ein EA) und dem Server (unser Dienst) zu ermöglichen. Bitte beachten Sie die folgenden Hinweise.

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

Dies sind die vom Kunden veröffentlichten Daten. Beachten Sie, dass wir 2 Variablen verwenden, um 3 verschiedene Informationen zu übergeben. Aber wie ist das möglich? Um dies zu verstehen, müssen wir uns den Code der Kopfzeilen ansehen, der im Folgenden vollständig dargestellt ist.

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

Man könnte meinen, dass jede Variable innerhalb dieser union von den anderen isoliert ist. Ich empfehle Ihnen, sich den Anfang dieses Artikels anzusehen, denn obwohl wir Variablen mit unterschiedlichen Namen haben, werden sie hier als eine Variable behandelt, die 8 Bytes breit ist. Um dies zu verdeutlichen, sehen Sie sich das folgende Bild an, das genau wiedergibt, was passiert:

Dieses Schema zeigt, was sich im uDataServer befindet.

Wenn es Ihnen zu kompliziert erscheint, sollten Sie versuchen, mit unions zu experimentieren, um zu verstehen, wie sie tatsächlich funktionieren, da sie beim Programmieren sehr nützlich sind.

Aber kommen wir zurück zum System. Als Nächstes müssen Sie den Code für den Client - den EA - erstellen. Der vollständige Text ist unten zu sehen.

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

Beachten Sie, dass wir in diesem EA Informationen senden und empfangen, d. h. wir können steuern, wie der Dienst funktionieren soll. In einer Variablen übergeben wir eine kleine Zeichenkette, die angibt, wonach der Dienst suchen soll, und in der anderen übergeben wir 2 Adresspunkte.

Als Antwort gibt der Dienst Informationen zurück. Aber um diesen ersten Punkt zu verstehen, sehen Sie sich das Ergebnis im folgenden Video an:



3.1.2.2 - Erstellung einer praktischen Version

Jetzt, wo wir gesehen haben, wie das System funktioniert, können wir etwas wirklich Funktionelles machen. Dieses Mal werden wir Informationen vom Webserver sammeln. Dies erfordert eine Reihe von Änderungen, die ein perfektes Verständnis des Geschehens gewährleisten. Manchmal denken wir, dass wir aktualisierte Daten erhalten, obwohl wir in Wirklichkeit Datenmüll zur Analyse verwenden. Sie müssen in der Programmierphase sehr vorsichtig sein, um sich nicht diesem Risiko auszusetzen. Sie können so viele Tests wie möglich hinzufügen und versuchen, das System dazu zu bringen, jede merkwürdige Aktivität zu melden, die es während der Ausführung entdeckt.

Vergessen Sie nicht: Informationen helfen Ihnen nur dann, wenn Sie ihnen vertrauen.

Zunächst bearbeiten wir die Header-Datei so, dass sie wie folgt aussieht:

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

Die hervorgehobenen Teile stellen die Codes dar, die wir verwenden werden, um eine seltsame Aktivität zu melden. Sie sollten maximal 8 Zeichen verwenden, aber Sie müssen auch eine Sequenz erstellen, bei der es unwahrscheinlich ist, dass sie vom Markt erstellt wird, was nicht einfach zu bewerkstelligen ist. Selbst wenn alles in Ordnung zu sein scheint, besteht immer die Gefahr, dass der Markt einen Wert erzeugt, der der Sequenz entspricht, die Sie als Server-Fehlermeldung verwenden. Sie können zu diesem Zweck auch eine globale Variable des Terminals verwenden, wodurch sich die Zahl der möglichen Kombinationen erhöht und Sie somit mehr Dinge erstellen können. Aber ich wollte so wenige globale Terminalvariablen wie möglich verwenden. In einem realen Fall würde ich jedoch darüber nachdenken und möglicherweise eine Variable nur zur Anzeige und Meldung von Fehlern oder abnormalen Aktivitäten verwenden.

Der nächste Teil ist der vollständige Code des 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);
        }
};
//+------------------------------------------------------------------+

Die hervorgehobenen Zeilen sind sehr wichtig und sollten gut durchdacht sein, da wir wirklich wissen wollen, was vor sich geht. Wie Sie sehen, können wir dem Nutzer etwas mehr Details mitteilen, als die in der Header-Datei erstellten Sequenzen bieten, sodass es einfacher wird, die Lösung zu programmieren und zu pflegen. Der Rest des Codes hat sich nicht wesentlich geändert. Sehen Sie sich den unten stehenden Servicecode an.

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

Die hervorgehobene Zeile ist ebenfalls wichtig - der Dienst wird darauf hinweisen, dass er nicht mehr läuft.

Wenn Sie dieses System ausführen, erhalten Sie das folgende Ergebnis:


Schlussfolgerung

Ich hoffe, dass ich die Idee, die mit dem Recherchieren, Suchen und Verwenden von Webdaten auf der MetaTrader 5 Plattform verbunden ist, erklärt habe. Ich verstehe, dass dies anfangs vielleicht nicht ganz klar ist, vor allem für diejenigen, die keine sehr umfangreichen Kenntnisse im Programmieren haben, aber mit der Zeit, durch Disziplin und Lernen, werden Sie das meiste von diesem Material beherrschen. Hier habe ich versucht, zumindest ein wenig von dem weiterzugeben, was ich weiß.

Übersetzt aus dem Portugiesischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/pt/articles/10447

Beigefügte Dateien |
Servi0o_-_EA.zip (10.71 KB)
Datenwissenschaft und maschinelles Lernen (Teil 06): Gradientenverfahren Datenwissenschaft und maschinelles Lernen (Teil 06): Gradientenverfahren
Der Gradientenverfahren spielt eine wichtige Rolle beim Training neuronaler Netze und vieler Algorithmen des maschinellen Lernens. Es handelt sich um einen schnellen und intelligenten Algorithmus, der trotz seiner beeindruckenden Arbeit von vielen Datenwissenschaftlern immer noch missverstanden wird - sehen wir uns an, worum es geht.
Automatisierter Grid-Handel mit Limit-Orders an der Moskauer Börse (MOEX) Automatisierter Grid-Handel mit Limit-Orders an der Moskauer Börse (MOEX)
Der Artikel befasst sich mit der Entwicklung eines MQL5 Expert Advisor (EA) für MetaTrader 5, der auf MOEX arbeiten soll. Der EA soll eine Grid-Strategie beim Handel auf MOEX mit dem MetaTrader 5 Terminal verfolgen. Der EA schließt Positionen durch Stop-Loss und Take-Profit und entfernt schwebende Aufträge im Falle bestimmter Marktbedingungen.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 18): Neues Auftragssystems (I) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 18): Neues Auftragssystems (I)
Dies ist der erste Teil des neuen Auftragssystems. Seit wir begonnen haben, diesen EA in unseren Artikeln zu dokumentieren, hat er verschiedene Änderungen und Verbesserungen erfahren, wobei das gleiche Modell des Auftragssystems auf dem Chart beibehalten wurde.
Das Preisbewegungsmodell und seine wichtigsten Bestimmungen (Teil 1): Die einfachste Modellversion und ihre Anwendungen Das Preisbewegungsmodell und seine wichtigsten Bestimmungen (Teil 1): Die einfachste Modellversion und ihre Anwendungen
Der Artikel liefert die Grundlagen für eine mathematisch rigorose Theorie der Preisbewegungen und des Funktionierens des Marktes. Bis heute gibt es keine mathematisch strenge Theorie der Preisbewegung. Stattdessen haben wir es mit erfahrungsbasierten Annahmen zu tun, die besagen, dass sich der Preis nach einem bestimmten Muster in eine bestimmte Richtung bewegt. Natürlich wurden diese Annahmen weder durch Statistiken noch durch die Theorie gestützt.